2016-06-12 64 views
2

我有一個自定義註釋:基於Spring中自定義註釋的方法調用?

@Target(ElementType.METHOD) 
@Retention(RetentionPolicy.RUNTIME) 
public @interface Controller { 
    EventType[] events() default EventType.MESSAGE; 
} 

而且有類方法使用它們象下面這樣:

@Controller(events = {EventType.MESSAGE, EventType.DIRECT_MESSAGE}) 
public void onMessage(Message msg) { } 

@Controller(events = {EventType.STAR_ADDED}) 
public void onStarAdded(Message msg) { } 

現在,我想要調用基於註釋events上述方法來自另一個班級的價值A。換句話說,當類A收到STAR_ADDED類型的事件時,我想調用類B中的所有方法,註釋@Controller(events = {EventType.STAR_ADDED})

我知道如何在Java中做到這一點,但春季提供任何API來做到這一點?如果是,代碼片段也會有幫助。

回答

3

解決方案1:

你也可以做這樣的事情:

enum EventType { 
    MESSAGE { 
     @Override 
     public void handleMessage(Service service, Message message) { 
      service.onMessage(message); 
     } 
    }, 
    STAR_ADDED { 
     @Override 
     public void handleMessage(Service service, Message message) { 
      service.onStarAdded(message); 
     } 

     public abstract void handleMessage(Service service, Message message); 
    } 
} 

在你的其他類,你知道什麼是 「激活」 事件:

yourEvent.handleMessage(service, message); 

解決方案2:

我不知道春天是否有任何正確的東西,否則你也可以使用反射。下面是一個使用反射的例子(我更喜歡沒有反映上述=>枚舉的解決方案):

for(Method method: Service.class.getDeclaredMethods()){ 
    Controller annotation = m.getAnnotation(Controller.class); 
    for(EventType event: annotation.events()){ 
     if(event.equals(yourActiveEventType)){ 
      method.invoke(service, message); 
     } 
     return ... 
    } 
} 

提示(而不是解決方案)3:

我真的不覺得有以下適用於你的場景,但我想我會提到它...... Spring AOP讓你在調用帶註釋的方法時觸發一些代碼(它與你的場景相反),檢查這個答案,但它可能值得閱讀你:aspectj-pointcut-for-all-methods-of-a-class-with-specific-annotation

@Around("execution(@Controller * com.exemple.YourService.*(..))") 
public Object aroundServiceMethodAdvice(final ProceedingJoinPoint pjp) 
    throws Throwable { 
    // perform actions before 

    return pjp.proceed(); 

    // perform actions after 
} 

解決方案4:(評論後加入)

使用org.reflections

<dependency> 
    <groupId>org.reflections</groupId> 
    <artifactId>reflections</artifactId> 
    <version>0.9.10</version> 
</dependency> 

例如:

Service service = ...; 
Message message = ...; 

Set<Method> methods = 
     ReflectionUtils.getMethods(Service.class, ReflectionUtils.withAnnotation(Controller.class),ReflectionUtils.withParametersAssignableTo(Message.class)); 

for(Method m: methods){ 
    Controller controller = m.getAnnotation(Controller.class); 
    for(EventType eventType: controller.value()){ 
     if(EventType.MESSAGE.equals(eventType)){ 
      m.invoke(service, message); 
     } 
    } 
} 

這是假設你已經持有的參考服務對象(其中你的方法是)。因爲你使用的是Spring,如果你的'Services'是spring管理的,你可能會從spring的上下文中得到實例,你必須親自嘗試一下,因爲這有點與你的設計綁定:

@Autowired 
private ApplicationContext appContext; 

Reflections r = new Reflections(new MethodAnnotationsScanner(), "com.your.package"); 
Set<Method> methods = r.getMethodsAnnotatedWith(Controller.class); 
for(Method m: methods){ 
    Controller controller = m.getAnnotation(Controller.class); 
    for(EventType eventType: controller.value()){ 
     if(EventType.MESSAGE.equals(eventType)){ 
      String className = m.getDeclaringClass().getSimpleName(); 
      className = className.replaceFirst(className.substring(0,1), className.substring(0,1).toLowerCase()); 
      Object service = appContext.getBean(className); 
      m.invoke(service, message); 
     } 
    } 
} 

如果您的類是彈簧託管的並且使用其默認camelcase名稱添加到上下文中,則此方法有效。

您可以簡化邏輯,但我相信主要元素在那裏。

+0

是的,你的第一個方法很好,但我沒有任何控制B類中的方法名稱的方法。你可能會認爲我想創建類似於Spring框架中的'@ Controller'註釋。所以你的第一個方法需要爲每個我不想要的事件類型分別設置方法。其次,當對兩個不同的事件採取同樣的行動時,代碼將被重複。 –

+0

我明白了,然後反射(第二個代碼示例)可能是您的首選武器! – alexbt

+0

,或者可能是AroundInvoke/aspectJ這裏有一個鏈接(用一個例子編輯我的答案):http://docs.spring.io/spring/docs/current/spring-framework-reference/html/aop.html – alexbt