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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 14:40:29