You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 18:55:26