0

我有一个 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类,它使用反射扫描类路径以查找带注释的消息处理程序,实例化它们并将它们放入映射中:

@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 注入“结婚”吗?或者是否有人认为这种方法是胡说八道并且有另一种解决方案?

最好的问候
塞巴斯蒂安

4

3 回答 3

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")独立于实际注释值(这里:xxx)注释的bean。

我希望这能解释重要的事情。更多详情请咨询我

塞巴斯蒂安

于 2012-10-30T18:49:55.670 回答
0

我认为您最简单的解决方案是保留您所拥有的,去掉扫描,因为您不需要它,将您的注释更改为限定符并使用限定符触发 CDI 事件(您需要创建一个AnnotationLiteral 用于三个不同的限定符中的每一个,因为该值是绑定的)和消息作为有效负载。

如果您需要,我可以解释更多。

于 2012-10-26T04:30:14.700 回答
0

查看和调整Dynamically fire CDI event with qualifier with members 这是一种通过运行时决策动态选择服务的 CDI 方式。TypeEnum 也可以是字符串。

于 2017-09-07T16:49:24.553 回答