自定义CDI Scope触发WELD-001303:无活动上下文问题求助
自定义CDI Scope报错:WELD-001303: No active contexts for scope type
问题背景
基于Jetty 11的嵌入式项目,已集成JPA、CDI、JMS和JAX-RS,原本运行正常。为添加自定义Scope,执行了以下操作:
- 定义
@MyScoped注解:
@Inherited @NormalScope @Retention(RetentionPolicy.RUNTIME) public @interface MyScoped {}
- 实现Scope上下文类
MyScopeContext:
import jakarta.enterprise.context.spi.Context; import jakarta.enterprise.context.spi.Contextual; import jakarta.enterprise.context.spi.CreationalContext; import jakarta.enterprise.inject.spi.Bean; import jakarta.enterprise.inject.spi.BeanManager; import java.lang.annotation.Annotation; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; public class MyScopeContext implements Context { private final Map<Bean<?>, Object> instances = new ConcurrentHashMap<>(); private final BeanManager beanManager; public MyScopeContext(BeanManager beanManager) { this.beanManager = beanManager; } @Override public Class<? extends Annotation> getScope() { return MyScoped.class; } @Override public <T> T get(Contextual<T> contextual, CreationalContext<T> creationalContext) { System.out.println("FLAG1"); @SuppressWarnings("unchecked") Bean<T> bean = (Bean<T>) contextual; return (T) instances.computeIfAbsent(bean, b -> beanManager.getContext(bean.getScope()).get(bean, creationalContext)); } @Override public <T> T get(Contextual<T> contextual) { System.out.println("FLAG2"); @SuppressWarnings("unchecked") Bean<T> bean = (Bean<T>) contextual; return (T) instances.get(bean); } @Override public boolean isActive() { return true; // 原代码缺失分号,已修正 } // 销毁上下文时清理实例 public void destroy() { instances.clear(); } }
- 编写扩展类
MyScopeExtension:
import jakarta.enterprise.event.Observes; import jakarta.enterprise.inject.spi.*; public class MyScopeExtension implements Extension { void afterBeanDiscovery(@Observes AfterBeanDiscovery abd, BeanManager manager) { BeanManager beanManager = CDI.current().getBeanManager(); abd.addContext(new MyScopeContext(beanManager)); } void addScope(@Observes final BeforeBeanDiscovery beforeBeanDiscovery) { beforeBeanDiscovery.addScope(MyScoped.class, true, false); } // 原代码存在重复代码块,属于语法错误 /* BeanManager beanManager = CDI.current().getBeanManager(); afterBeanDiscovery.addContext(new MyScopeContext(beanManager)); */ }
- 创建测试Bean:
@MyScoped public class TempBean { public String say() { return "Hello world"; } }
- 在
src/main/resources/META-INF/services/下创建jakarta.enterprise.inject.spi.Extension文件,内容为io.shaikezam.scope.MyScopeExtension。
报错信息
运行项目时抛出:
org.jboss.weld.contexts.ContextNotActiveException: WELD-001303: No active contexts for scope type io.shaikezam.scope.MyScoped
注意到构建后的jar中,扩展配置文件位于classes/META-INF/services/而非jar根目录的META-INF/services/,怀疑与此相关。项目pom.xml如下:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>io.shaikezam</groupId> <artifactId>jakarta-system-parent</artifactId> <version>1.0-SNAPSHOT</version> <relativePath>../pom.xml</relativePath> </parent> <artifactId>product-service</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <dependency> <groupId>org.glassfish.jersey.containers</groupId> <artifactId>jersey-container-servlet-core</artifactId> </dependency> <dependency> <groupId>org.glassfish.jersey.inject</groupId> <artifactId>jersey-cdi2-se</artifactId> </dependency> <dependency> <groupId>org.glassfish.jersey.media</groupId> <artifactId>jersey-media-json-jackson</artifactId> </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-server</artifactId> </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-servlet</artifactId> </dependency> <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> </dependency> <dependency> <groupId>org.eclipse.persistence</groupId> <artifactId>eclipselink</artifactId> </dependency> <dependency> <groupId>org.mariadb.jdbc</groupId> <artifactId>mariadb-java-client</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-nop</artifactId> </dependency> <dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct</artifactId> </dependency> <dependency> <groupId>org.apache.activemq</groupId> <artifactId>activemq-client</artifactId> </dependency> <dependency> <groupId>jakarta.jms</groupId> <artifactId>jakarta.jms-api</artifactId> </dependency> <dependency> <groupId>jakarta.enterprise</groupId> <artifactId>jakarta.enterprise.cdi-api</artifactId> </dependency> <dependency> <groupId>io.shaikezam</groupId> <artifactId>utils</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-assembly-plugin</artifactId> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> </plugin> </plugins> </build> </project>
问题分析与修复步骤
1. 修复扩展类语法错误
删除MyScopeExtension中的重复代码块,并优化BeanManager的获取方式:
import jakarta.enterprise.event.Observes; import jakarta.enterprise.inject.spi.*; public class MyScopeExtension implements Extension { void addScope(@Observes final BeforeBeanDiscovery beforeBeanDiscovery) { beforeBeanDiscovery.addScope(MyScoped.class, true, false); } void afterBeanDiscovery(@Observes AfterBeanDiscovery abd, BeanManager manager) { // 直接使用方法参数传入的BeanManager,避免容器初始化阶段调用CDI.current()的风险 abd.addContext(new MyScopeContext(manager)); } }
2. 修正Scope上下文类的循环调用问题
原get方法中调用beanManager.getContext(bean.getScope()).get()会触发无限递归,因为当前上下文就是MyScoped对应的上下文,需改为直接创建实例:
@Override public <T> T get(Contextual<T> contextual, CreationalContext<T> creationalContext) { System.out.println("FLAG1"); @SuppressWarnings("unchecked") Bean<T> bean = (Bean<T>) contextual; return instances.computeIfAbsent(bean, b -> b.create(creationalContext)); }
3. 修复Maven打包资源路径问题
确保资源文件被正确打包到jar根目录:
- 确认
src/main/resources是Maven标准资源目录,若不是,在pom.xml中添加资源配置:
<build> <resources> <resource> <directory>src/main/resources</directory> <includes> <include>**/*</include> </includes> </resource> </resources> <!-- 原有插件配置 --> </build>
- 配置
maven-assembly-plugin正确包含资源文件,添加src/main/assembly/assembly.xml:
<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.1.0 http://maven.apache.org/xsd/assembly-2.1.0.xsd"> <id>jar-with-dependencies</id> <formats> <format>jar</format> </formats> <includeBaseDirectory>false</includeBaseDirectory> <dependencySets> <dependencySet> <outputDirectory>/</outputDirectory> <useProjectArtifact>true</useProjectArtifact> <unpack>true</unpack> <scope>runtime</scope> </dependencySet> </dependencySets> <fileSets> <fileSet> <directory>${project.build.outputDirectory}</directory> <outputDirectory>/</outputDirectory> </fileSet> <fileSet> <directory>src/main/resources</directory> <outputDirectory>/</outputDirectory> </fileSet> </fileSets> </assembly>
然后在pom.xml中更新插件配置:
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-assembly-plugin</artifactId> <version>3.6.0</version> <configuration> <descriptor>src/main/assembly/assembly.xml</descriptor> <archive> <manifest> <mainClass>你的主类全限定名</mainClass> </manifest> </archive> </configuration> <executions> <execution> <id>make-assembly</id> <phase>package</phase> <goals> <goal>single</goal> </goals> </execution> </executions> </plugin>
4. 验证扩展加载
- 确认
jakarta.enterprise.inject.spi.Extension文件中的类名拼写无误,与实际类的全限定名一致。 - 编译后检查
target/classes/META-INF/services/下的文件是否存在,打包后检查jar根目录是否有META-INF/services/目录。
验证步骤
- 重新编译项目,检查资源文件路径是否正确。
- 执行打包命令,查看jar包结构是否符合预期。
- 运行项目,测试注入
TempBean是否成功,不再抛出上下文未激活异常。
内容的提问来源于stack exchange,提问作者Shay Zambrovski
相关产品推荐
相关产品推荐

