Dependency injection 使用自定义注释和CDI注入的消息路由/处理

Dependency injection 使用自定义注释和CDI注入的消息路由/处理,dependency-injection,annotations,java-ee-6,cdi,Dependency Injection,Annotations,Java Ee 6,Cdi,我有一个JavaEE6Web应用程序,并使用WebSocket协议与浏览器通信。浏览器可以发送各种类型的消息,在ServersOnMessage方法中,我希望根据消息类型将消息路由(或分派)到特定的消息处理程序类。我想通过注释配置或注册这些消息处理程序,类似于servlet(@WebServlet(“/there”))的机制。和servlet一样,我希望能够在消息处理程序中使用CDI注入 目前,我有一个MessageType注释、一个MessageHandler接口和3个实现 @Document

我有一个JavaEE6Web应用程序,并使用WebSocket协议与浏览器通信。浏览器可以发送各种类型的消息,在ServersOnMessage方法中,我希望根据消息类型将消息路由(或分派)到特定的消息处理程序类。我想通过注释配置或注册这些消息处理程序,类似于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;
    }
}
我的3条传入示例消息是:

"first:Message to handle with FirstMessageHandler"
"second:Message to handle with SecondMessageHandler"
"third:Message to handle with DefaultMessageHandler"
这很好,第一条和第二条消息分别由FirstMessageHandler和SecondMessageHandler处理。第三条消息由默认消息处理程序处理,因为没有为处理密钥“third”注册的其他处理程序

我的问题是:我不能在消息处理程序中使用注入,因为它们是使用Java反射创建的。有人知道如何让注释处理和CDI注入“结合”起来吗?或者有人认为这种方法是胡说八道,有其他解决办法吗

致以最诚挚的问候

Sebastian

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


如果你需要的话,我可以解释更多。

这是我最后的方法:

我将PostConstruct方法用于MessageDispatcher,在那里查找所有消息处理程序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)
    {
...
公共类MessageDispatcher
{
私有映射messageHandlerMap=newhashmap();
@注入
DefaultMessageHandler DefaultMessageHandler;
@注入
BeanManager BeanManager;
@施工后
公共无效登记管理器()
{
Set creationalContext=beanManager.createCreationalContext(bean);
MessageHandler MessageHandler=(MessageHandler)beanManager.getReference(bean,MessageHandler.class,CreationContext);
messageHandlerMap.put(键,messageHandler);
}
}
}
public MessageHandler getMessageHandler(字符串键)
{
MessageHandler=(MessageHandler)messageHandlerMap.get(key);
return messageHandler!=null?messageHandler:defaultMessageHandler;
}
}
@记录
@限定词
@保留(运行时)
@目标({类型、方法、字段、参数})
public@interface MessageType
{
@无约束力
字符串值();
}
@抑制警告(“全部”)
公共类MessageTypeLiteral扩展AnnotationLiteral实现MessageType
{
私有静态最终长serialVersionUID=1L;
@凌驾
公共字符串值()
{
返回“”;
}
}
公共类DefaultMessageHandler实现MessageHandler
{
@注入
ResourceBundleProvider ResourceBundleProvider;
@凌驾
public void processMessage(字符串inputmessage)
{
...
@消息类型(“第一个”)
公共类FirstMessageHandler实现MessageHandler
{
@注入
ResourceBundleProvider ResourceBundleProvider;
@凌驾
public void processMessage(字符串inputmessage)
{
...
@MessageType
注释中的
@NonBinding
注释对于发现所有用
@MessageType(“xxx”)
注释的bean独立于实际注释值(此处:xxx)似乎很重要

我希望这解释了重要的事情。更多细节请问我

塞巴斯蒂安参见并调整
这是一种CDI方法,用于动态运行时根据运行时决策选择服务。TypeEnum也可以是字符串。

我想知道事件实际上是如何触发的。在internet上的各种CDI事件示例中,事件以某种方式被注入。对我来说,这种方法似乎有点过分,而且非常静态,因为我必须定义限定符f或者每一种新类型的消息。是的,事件被注入。您只需要创建一个限定符(您已经有了注释,所以已经完成了一半)和一个文本。文本将需要创建一个具有正确值的注释实例,您将在触发事件时使用该实例。如果您能给出一个小示例,那将非常好。特别是如何在传入websocket消息时触发事件。我最初的方法的魅力在于,我可以从每个消息中使用密钥ge从已注册的处理程序映射中选择合适的消息处理程序,而不需要一个大的静态if-else构造。我希望以某种方式保留这个概念,并另外让消息处理程序由CDI容器管理,以便将其他依赖项注入其中。我在vim中编写了这篇文章,尚未编译它,但这篇文章我会给你一个主意,我试过这样做,但最后我不知道如何在中心位置注册处理程序或至少注册消息键(我的messageHandlerMap).但是我学到了很多关于CDI事件和限定符或注释的知识。但是关于AnnotationLiteral,我想知道为什么编译器会抱怨实现MessageType接口的MessageTypeLiteral类。警告是:“注释类型MessageType不应该用作MessageTypeLiteral的超级接口”对于
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)
    {
...