我有一个Java EE 6 Web应用程序,并使用WebSocket协议与浏览器进行通信。浏览器可以发送各种类型的消息,并且在服务器 onMessage 方法中,我希望根据消息类型将消息路由(或调度)到特定的消息处理程序类。我想通过注释配置或注册这些消息处理程序,类似于servlet的机制(@WebServlet(“/ there”))。就像在servlet中一样,我希望能够在消息处理程序中使用CDI注入。
现在我有一个 MessageType 注释,一个 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处理。第三条消息由缺省消息处理程序处理,因为没有注册其他处理程序来处理密钥“third”。
我的问题:我不能在消息处理程序中使用注入,因为它们是使用Java反射创建的。有谁知道如何获得注释处理和CDI注射“结婚”?或者有人认为这种方法是胡说八道,还有另一种解决办法吗?
最诚挚的问候 塞巴斯蒂安
答案 0 :(得分:1)
这是我最后的方法:
我将PostConstruct方法用于MessageDispachter,在那里我查找所有的消息处理程序bean。对于每个这些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)。
我希望这能解释重要的事情。有关详细信息,请与我联系
塞巴斯蒂安
答案 1 :(得分:0)
我认为你最简单的解决办法就是保留你拥有的东西,删除扫描因为你不需要它,把你的注释改成限定符并用限定符激活一个CDI事件(你需要为三个不同的限定符中的每一个创建一个AnnotationLite,因为值是绑定的)并且消息作为有效负载。
如果你需要,我可以解释更多。
答案 2 :(得分:0)
查看并调整Dynamically fire CDI event with qualifier with members 它是运行时决策的动态运行时选择服务的CDI方式。 TypeEnum也可以是String。