Jetty Embedded+Jersey+Weld整合:CDI依赖注入失败问题排查
问题:WELD无法识别@RequestScoped注解的GymDAO,导致依赖注入失败
尝试在Java SE中实现支持CDI的独立嵌入式Jetty+Jersey服务器,对外提供REST接口,但启动时抛出WELD-001408: Unsatisfied dependencies for type GymDAO with qualifiers @Default错误,系统无法识别标注了@RequestScoped的GymDAO类。
配置与代码详情
Gradle依赖配置
plugins { id 'application' id 'java' id 'eclipse' } repositories { mavenCentral() } dependencies { // Use JUnit Jupiter for testing. testImplementation 'org.junit.jupiter:junit-jupiter:5.7.2' // This dependency is used by the application. implementation 'org.slf4j:slf4j-api:2.0.4' implementation 'ch.qos.logback:logback-classic:1.4.5' implementation 'jakarta.enterprise:jakarta.enterprise.cdi-api:3.0.1' implementation 'jakarta.ws.rs:jakarta.ws.rs-api:3.0.1' implementation 'org.jboss.weld.se:weld-se-core:5.1.0.Final' implementation 'org.jboss.weld.servlet:weld-servlet-core:5.1.0.Final' implementation 'org.eclipse.jetty:jetty-server:11.0.12' implementation 'org.eclipse.jetty:jetty-servlet:11.0.12' implementation 'org.glassfish.jersey.core:jersey-server:3.1.0' implementation 'org.glassfish.jersey.containers:jersey-container-servlet-core:3.1.0' implementation 'org.glassfish.jersey.ext.cdi:jersey-cdi1x:3.1.0' implementation 'org.glassfish.jersey.ext.cdi:jersey-cdi1x-servlet:3.1.0' implementation 'org.glassfish.jersey.ext.cdi:jersey-weld2-se:3.1.0' implementation 'org.glassfish.jersey.inject:jersey-hk2:3.1.0' implementation 'org.glassfish.jersey.media:jersey-media-json-jackson:3.1.0' implementation 'com.fasterxml.jackson.core:jackson-core:2.12.7' implementation 'com.fasterxml.jackson.core:jackson-databind:2.12.7' } application { // Define the main class for the application. mainClass = 'it.gym.StartApp' } tasks.named('test') { // Use JUnit Platform for unit tests. useJUnitPlatform() }
主类代码
package it.gym; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.glassfish.jersey.servlet.ServletContainer; import org.jboss.weld.environment.se.Weld; import org.jboss.weld.environment.se.WeldContainer; import org.jboss.weld.environment.servlet.Listener; import org.jboss.weld.environment.servlet.WeldServletLifecycle; public class StartApp { public static void main(String[] args) { Weld weld = new Weld(); WeldContainer container = weld.initialize(); final Server server = new Server(9000); final ServletContextHandler context = new ServletContextHandler( ServletContextHandler.SESSIONS); context.setContextPath("/"); context.addEventListener(Listener.using(weld)); context.setAttribute( WeldServletLifecycle.BEAN_MANAGER_ATTRIBUTE_NAME, container.getBeanManager()); final ServletHolder servletHolder = new ServletHolder( ServletContainer.class); servletHolder.setInitOrder(1); servletHolder.setInitParameter( "jersey.config.server.provider.packages", "it.gym"); context.addServlet(servletHolder, "/rest/*"); server.setHandler(context); try { server.start(); server.join(); } catch (Exception e) { e.printStackTrace(); } } }
REST资源类与DAO类
package it.gym.rest; import java.util.List; import it.gym.dao.GymDAO; import jakarta.enterprise.context.RequestScoped; import jakarta.inject.Inject; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.Response.Status; @Path("/") @RequestScoped public class GymEndpoint { @Inject private GymDAO gymDAO; @GET @Path("/test") public Response test() { List<String> entity = gymDAO.getDevices(); return Response.status(Status.OK).entity(entity).build(); } }
package it.gym.dao; import java.util.ArrayList; import java.util.List; import jakarta.enterprise.context.RequestScoped; @RequestScoped public class GymDAO { public GymDAO() { } public List<String> getDevices() { return new ArrayList<>(); } }
错误日志
jakarta.servlet.ServletException: org.glassfish.jersey.servlet.ServletContainer-15eebbff==org.glassfish.jersey.servlet.ServletContainer@60d52d52{jsp=null,order=1,inst=true,async=true,src=EMBEDDED:null,STARTED} at org.eclipse.jetty.servlet.ServletHolder.initServlet(ServletHolder.java:651) ... Caused by: org.jboss.weld.exceptions.IllegalArgumentException: WELD-001408: Unsatisfied dependencies for type GymDAO with qualifiers @Default at injection point [BackedAnnotatedField] @Inject private it.gym.rest.GymEndpoint.gymDAO at it.gym.rest.GymEndpoint.gymDAO(GymEndpoint.java:0) ...
解决方案
1. 添加CDI beans.xml配置文件
CDI容器需要beans.xml来明确扫描规则,即使是CDI 3.0的隐式归档模式,在SE+Servlet混合环境下也需显式配置确保Bean被识别。
在src/main/resources/META-INF/目录下创建beans.xml:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="https://jakarta.ee/xml/ns/jakartaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/beans_3_0.xsd" version="3.0" bean-discovery-mode="all"> </beans>
bean-discovery-mode="all"会让WELD扫描所有带有CDI注解的类,确保GymDAO被识别为可注入Bean。
2. 调整Weld与Jetty的整合逻辑
当前代码同时初始化了Weld SE容器和Servlet监听器,导致容器冲突。修改主类,让Jetty的Servlet上下文托管Weld容器:
package it.gym; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.glassfish.jersey.servlet.ServletContainer; import org.jboss.weld.environment.servlet.Listener; public class StartApp { public static void main(String[] args) { final Server server = new Server(9000); final ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); context.setContextPath("/"); // 注册Weld Servlet监听器,由Servlet上下文初始化容器 context.addEventListener(new Listener()); final ServletHolder servletHolder = new ServletHolder(ServletContainer.class); servletHolder.setInitOrder(1); servletHolder.setInitParameter( "jersey.config.server.provider.packages", "it.gym"); context.addServlet(servletHolder, "/rest/*"); server.setHandler(context); try { server.start(); server.join(); } catch (Exception e) { e.printStackTrace(); } } }
3. 优化依赖配置
移除不必要的依赖,避免版本冲突与容器混淆:
- 移除
org.jboss.weld.se:weld-se-core:使用Servlet环境的Weld实现即可 - 移除
org.glassfish.jersey.ext.cdi:jersey-weld2-se:这是SE环境专属整合依赖,Servlet环境用jersey-cdi1x-servlet足够 - 移除重复的Jackson依赖:
jersey-media-json-jackson已包含Jackson核心库
修改后的依赖片段:
dependencies { testImplementation 'org.junit.jupiter:junit-jupiter:5.7.2' // Logging implementation 'org.slf4j:slf4j-api:2.0.4' implementation 'ch.qos.logback:logback-classic:1.4.5' // CDI implementation 'jakarta.enterprise:jakarta.enterprise.cdi-api:3.0.1' implementation 'org.jboss.weld.servlet:weld-servlet-core:5.1.0.Final' // Jetty implementation 'org.eclipse.jetty:jetty-server:11.0.12' implementation 'org.eclipse.jetty:jetty-servlet:11.0.12' // Jersey implementation 'jakarta.ws.rs:jakarta.ws.rs-api:3.0.1' implementation 'org.glassfish.jersey.core:jersey-server:3.1.0' implementation 'org.glassfish.jersey.containers:jersey-container-servlet-core:3.1.0' implementation 'org.glassfish.jersey.ext.cdi:jersey-cdi1x:3.1.0' implementation 'org.glassfish.jersey.ext.cdi:jersey-cdi1x-servlet:3.1.0' implementation 'org.glassfish.jersey.media:jersey-media-json-jackson:3.1.0' }
4. 验证扫描范围
确保it.gym.dao包被Weld和Jersey同时覆盖:
- beans.xml的
bean-discovery-mode="all"会扫描所有带CDI注解的类 - Jersey的
jersey.config.server.provider.packages设置为it.gym会覆盖所有子包的REST资源与相关类
内容的提问来源于stack exchange,提问作者Francesco Rosso
相关产品推荐
相关产品推荐

