在使用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()
方法