在EJB 3.1或CDI中定义并注入映射



在使用Spring开发了几年之后,我切换到EJB,我很不高兴我没有这个用例的解决方案。假设它是由地图实现的策略模式。在春天,它可能是这样的。

<bean id="myBean" class="MyBeanImpl">
    <property name="handlers">
        <map>
            <entry key="foo" value-ref="fooHandler"/>
            <entry key="bar" value-ref="barHandler"/>
        </map>
    </property>
</bean>

在EJB/CDI中,我有这个

@Stateless
public class MyBeanImpl implements MyBean {
    private Map<String, Class<? extends Handler>> handlers = new HashMap<>();
    @PostConstruct
    public void init() {
        handlers.put("foo", FooHandlerImpl.class);
        handlers.put("bar", BarHandlerImpl.class);
    }
    //jndi lookup handlerClass.getSimpleName()
}

请注意jndi查找与实现而不是接口一起工作。没有更好的解决办法吗?不,我不想有单独的字段(foo, bar),注入它们并创建映射之后(它可以是巨大的列表和经常更改)。理想情况下,在任何配置更改的情况下,我都不会触及MyBeanImpl类。

更像CDI的方式是这样的:

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

public class HandlerLiteral extends AnnotationLiteral<Handles> implements Handles{
    private final String value;
    public HandlerLiteral(String value) {
        this.value = value;
    }
    @Override
    public String value() {
        return value;
    }
}

然后你会用@Handles("someName")注释你的每个Handler实现,例如你在这里使用的类名。这里限定符的使用更符合CDI的工作方式,我们使用内部Instance对象来解析适当的bean。然后在你的服务代码(或任何地方)你只需做:

@Inject @Any
private Instance<HandlerService> handlerInstance;
...
handlerInstance.select(new HandlerLiteral("whateverName")).get().handle(context);

如果你真的被限制使用映射,这将不适合你。但是这应该允许更动态的注册,并且基本上查看上下文中的每个处理程序。

试试这个:

@Inject @Any
private Instance<Handler> handlers;
private Map<String, Handler> handlerMap = new HashMap<>();

@PostConstruct
private void init() {
    for (Handler handler : handlers) {
        handlerMap.put(handler.getName(), handler);
    }
}

假设您的Handler接口有某种getName()方法

最新更新