在EJB调用中返回CDI bean引用

时间:2015-03-04 10:33:07

标签: java-ee cdi

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 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());
}

2 个答案:

答案 0 :(得分:1)

请参阅以下

@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;

}

关于生产者和注射点:

  

请注意   如果在运行时,策略可能不存在,因此该方法可能返回null,然后使用@Dependent注释生产者,而不是@ApplicationScope,因此在注入点,不要注入原始策略,但是:

  @Inject
    @StrategyContext(supportedtype=MySupportedType.class)
    private Instance<Strategy> strategy

    ...
    if(!strategy.isUnsatisfied()) { strategy.get().doSomething();}

答案 1 :(得分:1)

我不明白为什么不这样做,因为@Depend(默认范围)是伪范围,因此bean管理器不再对bean感兴趣。它的生命周期完全取决于外部bean或其他直接引用它的类 - 它是一个普通的旧POJO