在 EJB 调用中返回 CDI bean 引用
Returning CDI bean reference in EJB call
Java EE 中是否允许以下内容?
我有一个 @Singleton
会话 bean,用作注册表并自动发现在整个应用程序中使用的某些策略,如下所示:
public interface Strategy {
Class<?> supportedType();
}
@Singleton
public class StrategyRegistry {
@Inject
private Instance<Strategy> strategies;
private Map<Class<?>, Strategy> registry = new HashMap<>();
@PostConstruct
public void startup() {
Iterator<Strategy> it = strategies;
while (it.hasNext()) {
Strategy s = it.next();
registry.put(s.supportedType(), s);
}
}
// return provided strategy to caller
public Strategy strategyFor(Class<?> clz) {
return registry.get(clz);
}
}
现在我的问题是是否允许将 @Inject
ed CDI bean 共享给其他客户端(通过 #strategyFor(...)
),或者这是否会导致意外行为或副作用?
这些策略是无状态和线程安全的,因此通常应该可以同时使用它们。
请注意,上面 CDI bean 的所有者是 @Singleton
并且 CDI bean 当前是 @Dependent
,因此如果我正确阅读规范,它们应该在整个生命周期内可用应用程序。
最初我们使用 EJB 无状态 bean,strategyFor(...)
仅将代理返回给实际的 bean,我认为这相当安全。这在具有最新 WELD 版本的 Wildfly 8 上不再可能,因此我们切换到 CDI bean。然而,对于 CDI bean,返回的是实际的 bean,而不是代理。
如果不允许这样做,以下是安全的替代方案吗?
@Inject private BeanManager beanManager;
public Strategy strategyFor(Class<?> clz) {
Strategy s = registry.get(clz);
if (s == null) return null;
return beanManager.getReference(s.getClass());
}
见下文
@Qualifier
@Retention(RUNTIME)
@Target({FIELD,METHOD,TYPE,PARAMETER})
public interface StrategyContext {
//Nonbinding and default so that you can have one producer method for all strategies
@NonBinding
Class<?> supportedType() default Object.class;
}
@Singleton
public class StrategyRegistry {
private Map<Class<?>, Strategy> registry = new HashMap<>();
@Inject
void startup(@Any final Instance<Strategy> strategies) {
for(Strategy strategy:strategies)
registry.put(Strategy.supportedType(), Strategy);
}
}
@Produces
@StrategyContext
@ApplicationScoped
public Strategy strategyFor(final InjectionPoint ip) {
final StrategyContext sc = ip.getAnnotated().getAnnotation(StrategyContext.class);
final Class<?> supportedType = sc.supportedType();
return registry.get(supportedType);
}
}
然后随处使用。
@Stateless
public class MyService {
@Inject
@StrategyContext(supportedType=MySupportedType.class)
private Strategy strategy;
}
关于生产者和注入点:
NOTE
If at runtime, the strategy may not exist and hence the method may return null, then annotate the producer with @Dependent, not @ApplicationScope and hence at the injection point, don't inject the raw strategy, but:
@Inject
@StrategyContext(supportedtype=MySupportedType.class)
private Instance<Strategy> strategy
...
if(!strategy.isUnsatisfied()) { strategy.get().doSomething();}
我不明白为什么不这样做,因为 @Depend(默认范围)是一个伪范围,因此 bean 管理器不再对该 bean 感兴趣。它从那里开始的生命周期完全取决于外部 bean 或其他直接引用它的 类 - 它是一个普通的旧 POJO
Java EE 中是否允许以下内容?
我有一个 @Singleton
会话 bean,用作注册表并自动发现在整个应用程序中使用的某些策略,如下所示:
public interface Strategy {
Class<?> supportedType();
}
@Singleton
public class StrategyRegistry {
@Inject
private Instance<Strategy> strategies;
private Map<Class<?>, Strategy> registry = new HashMap<>();
@PostConstruct
public void startup() {
Iterator<Strategy> it = strategies;
while (it.hasNext()) {
Strategy s = it.next();
registry.put(s.supportedType(), s);
}
}
// return provided strategy to caller
public Strategy strategyFor(Class<?> clz) {
return registry.get(clz);
}
}
现在我的问题是是否允许将 @Inject
ed CDI bean 共享给其他客户端(通过 #strategyFor(...)
),或者这是否会导致意外行为或副作用?
这些策略是无状态和线程安全的,因此通常应该可以同时使用它们。
请注意,上面 CDI bean 的所有者是 @Singleton
并且 CDI bean 当前是 @Dependent
,因此如果我正确阅读规范,它们应该在整个生命周期内可用应用程序。
最初我们使用 EJB 无状态 bean,strategyFor(...)
仅将代理返回给实际的 bean,我认为这相当安全。这在具有最新 WELD 版本的 Wildfly 8 上不再可能,因此我们切换到 CDI bean。然而,对于 CDI bean,返回的是实际的 bean,而不是代理。
如果不允许这样做,以下是安全的替代方案吗?
@Inject private BeanManager beanManager;
public Strategy strategyFor(Class<?> clz) {
Strategy s = registry.get(clz);
if (s == null) return null;
return beanManager.getReference(s.getClass());
}
见下文
@Qualifier
@Retention(RUNTIME)
@Target({FIELD,METHOD,TYPE,PARAMETER})
public interface StrategyContext {
//Nonbinding and default so that you can have one producer method for all strategies
@NonBinding
Class<?> supportedType() default Object.class;
}
@Singleton
public class StrategyRegistry {
private Map<Class<?>, Strategy> registry = new HashMap<>();
@Inject
void startup(@Any final Instance<Strategy> strategies) {
for(Strategy strategy:strategies)
registry.put(Strategy.supportedType(), Strategy);
}
}
@Produces
@StrategyContext
@ApplicationScoped
public Strategy strategyFor(final InjectionPoint ip) {
final StrategyContext sc = ip.getAnnotated().getAnnotation(StrategyContext.class);
final Class<?> supportedType = sc.supportedType();
return registry.get(supportedType);
}
}
然后随处使用。
@Stateless
public class MyService {
@Inject
@StrategyContext(supportedType=MySupportedType.class)
private Strategy strategy;
}
关于生产者和注入点:
NOTE If at runtime, the strategy may not exist and hence the method may return null, then annotate the producer with @Dependent, not @ApplicationScope and hence at the injection point, don't inject the raw strategy, but:
@Inject
@StrategyContext(supportedtype=MySupportedType.class)
private Instance<Strategy> strategy
...
if(!strategy.isUnsatisfied()) { strategy.get().doSomething();}
我不明白为什么不这样做,因为 @Depend(默认范围)是一个伪范围,因此 bean 管理器不再对该 bean 感兴趣。它从那里开始的生命周期完全取决于外部 bean 或其他直接引用它的 类 - 它是一个普通的旧 POJO