Jakarta EE中如何注入接口全部实现类?解决空指针异常问题
Jakarta EE 实现工厂模式并解决空指针问题
问题根源
Jakarta EE 里远程EJB(标注@Remote)的注入逻辑和Spring存在差异:
- 远程EJB的实例是容器生成的代理对象,在
@PostConstruct阶段可能还未完成初始化,直接用Instance<First>遍历无法获取有效实例 - 远程Bean的发现机制和本地Bean不同,
Instance可能无法扫描到所有实现类 - 调用
FirstType.HELLO时返回空指针,大概率是缺少对应枚举值的First实现类,或者该类未被容器正确扫描
解决办法
1. 改用本地接口(最省心方案)
如果不需要跨服务器远程调用,把@Remote换成@Local,本地EJB的注入和实例发现逻辑更稳定,和Spring行为更接近:
@Local public interface First { String getSomething(); FirstType getType(); } @Local public interface TestWSFactory { First getFirst(FirstType type); }
修改后原有的Instance<First>遍历逻辑即可正常工作,容器会自动扫描所有First的本地实现类。
2. 必须保留远程接口?用BeanManager手动查找
如果一定要维持远程调用,放弃Instance注入,改用BeanManager手动查找所有实现First远程接口的Bean:
@Stateless public class TestWSFactoryImpl implements TestWSFactory { @Inject BeanManager beanManager; private Map<FirstType, First> map; @PostConstruct void setUp() { map = new HashMap<>(); // 查找所有标注@Remote的First实现类 Set<Bean<?>> beans = beanManager.getBeans(First.class, new AnnotationLiteral<Remote>() {}); for (Bean<?> bean : beans) { CreationalContext<?> ctx = beanManager.createCreationalContext(bean); First firstInstance = (First) beanManager.getReference(bean, First.class, ctx); map.put(firstInstance.getType(), firstInstance); } } @Override public First getFirst(FirstType type) { return Optional.ofNullable(map.get(type)).orElseThrow(() -> new IllegalArgumentException("找不到对应类型的实现:" + type)); } }
注意所有First的实现类都要标注@Stateless,且确保容器能扫描到这些类。
3. 补全缺失的实现类
你提到有两个First实现类,但只提供了对应FirstType.ACTION的ActionFirstImpl,调用FirstType.HELLO返回空指针,说明缺少对应枚举值的实现类。补写示例如下:
@Stateless public class HelloFirstImpl implements First { @Override public String getSomething() { return "Hello from HELLO"; } @Override public FirstType getType() { return FirstType.HELLO; } }
同时要确保该类处于容器的扫描路径中(比如放在WEB-INF/classes对应的包下)。
4. 延迟Map初始化到第一次调用
如果不想修改接口或使用BeanManager,可将Map的初始化从@PostConstruct移到第一次调用getFirst时,避开远程代理未初始化的时机:
@Stateless public class TestWSFactoryImpl implements TestWSFactory { @Inject Instance<First> firstList; private volatile Map<FirstType, First> map; @Override public First getFirst(FirstType type) { // 双重检查锁实现延迟初始化 if (map == null) { synchronized (this) { if (map == null) { map = new HashMap<>(); firstList.forEach(t -> map.put(t.getType(), t)); } } } return Optional.ofNullable(map.get(type)).orElseThrow(() -> new IllegalArgumentException("找不到对应类型的实现:" + type)); } }
这样能确保调用时Instance<First>已被容器完全初始化,可获取所有有效实例。
验证注意点
- 使用WildFly、Payara等支持Jakarta EE 8+的容器,避免API兼容问题
- 确保所有EJB类都在容器的扫描范围内
- 调用
TestWSFactory必须通过容器注入,不能手动new,否则依赖注入不会生效
内容的提问来源于stack exchange,提问作者Spam Garbage
相关产品推荐
相关产品推荐

