JAX-RS集成测试本地运行正常 Jenkins下Undertow部署空指针异常
问题原因及排查方案
仅Jenkins环境复现的可能原因
该空指针异常根因为CDI.current().getBeanManager()调用返回空,仅在Jenkins环境复现的常见原因如下:
- 类路径缺失:Jenkins构建环境的测试运行时类路径缺少Weld SE核心依赖,CDI Provider未被正确注册,导致
CDI.current()无法获取到实例 - 测试资源缺失:
src/test/resources下的META-INF/beans.xml未被正确复制到Jenkins的构建输出目录,Weld无法识别CDI归档,没有完成BeanManager初始化 - 类加载器冲突:Jenkins节点复用Gradle Daemon、或者多个构建任务并行执行时,不同构建的Weld实例被同一个类加载器加载,导致全局CDI实例被覆盖或找不到
- 执行时序问题:Jenkins节点性能低于本地开发机,Weld容器初始化未完成时就触发了Undertow的Servlet上下文监听器,
CDI.current()还未持有可用实例 - 依赖版本冲突:Jenkins拉取的依赖树存在Weld相关包版本不一致,比如
weld-web和weld-core版本不匹配,导致BeanManager初始化失败
排查及解决步骤
- 验证类路径一致性:分别在本地和Jenkins环境执行
gradle dependencies --configuration testRuntimeClasspath,对比weld-core、weld-se-core、weld-web等依赖的版本,确保没有冲突或缺失 - 检查测试资源加载:在Jenkins构建脚本中新增步骤,打印测试构建输出目录下的META-INF目录内容,确认
beans.xml存在且未被过滤 - 禁用Gradle守护进程验证:在Jenkins执行测试的命令后添加
--no-daemon参数,排除Daemon残留上下文的影响 - 调整容器初始化逻辑:在启动Undertow前手动初始化Weld容器,并将上下文ID传入部署配置,避免走
CDI.current()的fallback逻辑,代码修改参考:
public static void startContainer() throws ServletException { // 提前初始化Weld容器 Weld weld = new Weld(); WeldContainer weldContainer = weld.initialize(); System.setProperty("org.jboss.resteasy.port", "8085"); server = new UndertowJaxrsServer().start(); ResteasyDeployment deployment = new ResteasyDeployment(); deployment.setInjectorFactoryClass("org.jboss.resteasy.cdi.CdiInjectorFactory"); deployment.setApplicationClass(MyApp.class.getName()); DeploymentInfo di = server.undertowDeployment(deployment); // 传入Weld上下文ID,让监听器直接通过ID获取BeanManager di.addInitParameter(Container.CONTEXT_ID_KEY, weldContainer.getId()); di.setClassLoader(Server.class.getClassLoader()); di.setResourceManager(new ClassPathResourceManager(Server.class.getClassLoader())); di.setContextPath("/vincent-service"); di.setDeploymentName("DI"); di.addListeners(Servlets.listener(org.jboss.weld.environment.servlet.Listener.class)); server.deploy(di); }
- 排查并行执行影响:如果Jenkins配置了测试并行执行,临时关闭并行执行验证问题是否消失,若消失则为每个测试任务配置唯一的Weld上下文ID即可
内容的提问来源于stack exchange,提问作者Vincent F
相关产品推荐
相关产品推荐

