2012-10-24 82 views
0

我有一個Java EE 6 Web應用程序,並使用WebSocket協議與瀏覽器進行通信。瀏覽器可以發送各種類型的消息,並在服務器 onMessage方法我想根據消息類型將消息路由(或調度)到特定的消息處理程序類。我想通過註釋來配置或註冊這些消息處理程序,類似於servlet的機制(@WebServlet(「/ there」))。就像在servlet中一樣,我希望能夠在消息處理程序中使用CDI注入。自定義註釋和CDI注入消息路由/處理

現在我有一個消息類型註釋,一個的MessageHandler接口和3層的實施方式。

@Documented 
@Target(ElementType.TYPE) 
@Retention(RetentionPolicy.RUNTIME) 
public @interface MessageType 
{ 
    String value(); 
} 


public interface MessageHandler 
{ 
    public void processMessage(String inputMesssage); 
} 


@MessageType("first") 
public class FirstMessageHandler implements MessageHandler 
{ 
    @Inject 
    ResourceBundleProvider resourceBundleProvider; 

    @Override 
    public void processMessage(String inputMesssage) 
    { 
     System.out.println("FirstMessageHandler#processMessage: " + inputMesssage); 
     System.out.println("InjectionTest: " + resourceBundleProvider.getValue("label.language")); 
    } 
} 


@MessageType("second") 
public class SecondMessageHandler implements MessageHandler 
{ 
    @Override 
    public void processMessage(String inputMesssage) 
    { 
     System.out.println("SecondMessageHandler#processMessage: " + inputMesssage); 
    } 
} 


public class DefaultMessageHandler implements MessageHandler 
{ 
    @Override 
    public void processMessage(String inputMesssage) 
    { 
     System.out.println("DefaultMessageHandler#processMessage: " + inputMesssage); 
    } 
} 

我也有一類 MessageDispatcher它使用reflections掃描類路徑註釋消息處理程序,實例化它們,並將它們放入一個地圖:

@ApplicationScoped 
public class MessageDispatcher 
{ 
    private Map<String, MessageHandler> messageHandlerMap = new HashMap<String, MessageHandler>(); 

    @Inject 
    DefaultMessageHandler defaultMessageHandler; 

    public MessageDispatcher() 
    { 
     registerAnnotatedHandlers(); 
    } 

    private void registerAnnotatedHandlers() 
    { 
     Reflections reflections = new Reflections("namespace"); 

     try 
     { 
      for (Class<?> annotatedClass : reflections.getTypesAnnotatedWith(MessageType.class)) 
      { 
       String annotationValue = annotatedClass.getAnnotation(MessageType.class).value(); 

       for (Class<?> interfaceClass : annotatedClass.getInterfaces()) 
        if (!annotationValue.isEmpty() && interfaceClass.equals(MessageHandler.class)) 
         messageHandlerMap.put(annotationValue, (MessageHandler) annotatedClass.newInstance()); 
      } 
     } 
     catch (Exception e) 
     { 
      e.printStackTrace(); 
     }  
    } 

    public MessageHandler getMessageHandler(String key) 
    { 
     MessageHandler messageHandler = messageHandlerMap.get(key); 

     return messageHandler != null ? messageHandler : defaultMessageHandler; 
    } 
} 

最後,在我的WebSocket servlet的 onMessage方法我從入站消息中提取密鑰並將其用於消息路由:

public synchronized void onMessage(String data) 
{ 
    String[] message = data.split(":"); 

    // Choose the message handler from the message 
    MessageHandler messageHandler = messageDispatcher.getMessageHandler(message[0]); 

    // Process the message by the message handler 
    messageHandler.processMessage(message[1]); 
} 

我3個傳入樣本信息是:

"first:Message to handle with FirstMessageHandler" 
"second:Message to handle with SecondMessageHandler" 
"third:Message to handle with DefaultMessageHandler" 

這工作正常,第一和第二消息由FirstMessageHandler和SecondMessageHandler分別處理。第三條消息由缺省消息處理程序處理,因爲沒有其他處理程序註冊用於處理「第三個」關鍵字。

我的問題:我不能在消息處理程序中使用注入,因爲它們是使用Java反射創建的。有人知道如何獲得註釋處理和CDI注入「已婚」嗎?還是有人認爲這種方法是廢話,並有另一個解決方案呢?

問候
塞巴斯蒂安

回答

1

這是我最後的辦法:

我花了PostConstruct方法,我MessageDispachter,我看所有的消息處理豆。對於這些bean中的每一個,我都可以獲得它們的註釋值和對bean的引用(其中還包括創建bean)。然後我將這兩個註釋值和bean引用存儲到我的messageHandlerMap中。有很多CDI委託和攔截參與的,但它的工作原理:

public class MessageDispatcher 
{ 
    private Map<String, MessageHandler> messageHandlerMap = new HashMap<String, MessageHandler>(); 

    @Inject 
    DefaultMessageHandler defaultMessageHandler; 

    @Inject 
    BeanManager beanManager; 

    @PostConstruct 
    public void registerHandlers() 
    { 
     Set<Bean<?>> messageHandlerBeans = beanManager.getBeans(MessageHandler.class, new MessageTypeLiteral()); 
     for (Bean<?> bean : messageHandlerBeans) 
     { 
      String key = bean.getBeanClass().getAnnotation(MessageType.class).value(); 

      if (!key.isEmpty()) 
      { 
       CreationalContext<?> creationalContext = beanManager.createCreationalContext(bean); 
       MessageHandler messageHandler = (MessageHandler) beanManager.getReference(bean, MessageHandler.class, creationalContext); 
       messageHandlerMap.put(key, messageHandler); 
      } 
     } 
    } 

    public MessageHandler getMessageHandler(String key) 
    { 
     MessageHandler messageHandler = (MessageHandler) messageHandlerMap.get(key); 
     return messageHandler != null ? messageHandler : defaultMessageHandler; 
    } 
} 


@Documented 
@Qualifier 
@Retention(RUNTIME) 
@Target({TYPE, METHOD, FIELD, PARAMETER}) 
public @interface MessageType 
{ 
    @Nonbinding 
    String value(); 
} 


@SuppressWarnings("all") 
public class MessageTypeLiteral extends AnnotationLiteral<MessageType> implements MessageType 
{ 
    private static final long serialVersionUID = 1L; 

    @Override 
    public String value() 
    { 
     return ""; 
    } 
} 


public class DefaultMessageHandler implements MessageHandler 
{ 
    @Inject 
    ResourceBundleProvider resourceBundleProvider; 

    @Override 
    public void processMessage(String inputMesssage) 
    { 
... 


@MessageType("first") 
public class FirstMessageHandler implements MessageHandler 
{ 
    @Inject 
    ResourceBundleProvider resourceBundleProvider; 

    @Override 
    public void processMessage(String inputMesssage) 
    { 
... 

@NonBinding標註在@MessageType註釋似乎是重要的是找到與@MessageType("xxx")獨立於實際的註釋值(這裏的註釋:所有的bean:XXX )。

我希望這解釋了重要的事情。欲瞭解更多詳情請諮詢我

塞巴斯蒂安

0

我覺得你的最簡單的這種解決方案將讓你擁有什麼,剝離出的掃描,因爲你不需要它,改變你的註釋是一個預選賽和火帶有限定符的CDI事件(您需要爲三個不同的限定符中的每一個創建一個AnnotationLiteral,因爲該值是綁定的),並將消息作爲有效負載。

我可以解釋更多,如果你需要它。

+0

我想知道事件是如何被實際解僱的。在互聯網上發現的各種CDI事件例子中,事件被注入了某種程度。對我來說,這種方法似乎有點矯枉過正,並且非常靜態,因爲我必須爲每種新類型的消息定義限定符。 –

+0

是的,事件被注入。你只需要創建一個限定符(你已經有了註釋,所以你只有一半)和一個文字。文字將需要用正確的值創建註釋的實例,並且在您啓動事件時使用該實例。 – LightGuard

+0

如果你能舉一個小例子,那將會很棒。特別是如何在傳入websocket消息時觸發事件。 我最初的方法的魅力在於,我可以使用每個消息中的密鑰從註冊處理程序的映射中選擇適當的消息處理程序,而不必使用大的靜態if-else結構。我想以某種方式保持這個概念,並且還有消息處理程序由CDI容器管理,以便將其他依賴注入到它們中。 –