WildFly中CDI 4.0与Jakarta REST 3.1集成的Bean生命周期疑问
我正在研究WildFly中JAX-RS与CDI的集成,重点关注Bean的生命周期表现,但实际行为和预期不符。
代码示例
REST资源类
@Dependent @Path("/users") @Produces(MediaType.APPLICATION_JSON) public class UserResourceImpl { @PostConstruct public void init() { System.out.println("UserResourceImpl init ..."); } @PreDestroy public void destroy() { System.out.println("UserResourceImpl destroy ..."); } @GET public Response test() { return Response.ok("{ \"answer\": \"working...\"}").build(); } }
beans.xml配置
<?xml version="1.0" encoding="UTF-8"?> <beans version="4.0" 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_4_0.xsd" bean-discovery-mode="annotated"> </beans>
Bean发现模式设置为annotated,目的是精准控制Bean生命周期。
问题现象
部署到WildFly 32后,每个HTTP请求都会触发@PostConstruct方法,但请求完成后、甚至应用终止时,@PreDestroy始终没有执行。
根据RESTEasy文档,未显式定义作用域的REST根资源会绑定到@RequestScoped,我原本预期这个标注了@Dependent的Bean会表现得和@RequestScoped类似,每个请求完成时触发@PreDestroy,请问这是什么原因?是Bug吗?
问题原因分析
这不是WildFly或RESTEasy的Bug,完全是CDI @Dependent作用域的特性导致的:
- @Dependent是CDI的“依赖作用域”,它没有自己的独立上下文,Bean的生命周期完全由它的“持有者”控制。持有者创建实例后,用完如果没有显式触发销毁逻辑,CDI容器不会主动调用
@PreDestroy。 - 对于标注了@Dependent的JAX-RS根资源,RESTEasy会在每个请求到来时创建实例,但请求处理完成后,RESTEasy并不会将实例交还给CDI容器进行销毁操作,也不会主动调用
@PreDestroy——因为@Dependent Bean的销毁责任在持有者(这里就是RESTEasy的请求处理逻辑),而RESTEasy并没有实现这个销毁逻辑。
你提到的“未显式定义作用域的REST根资源绑定到Request作用域”是正确的默认行为,但那只适用于没有显式标注任何作用域的情况:此时RESTEasy会自动为根资源添加@RequestScoped,Bean属于请求上下文,请求结束时CDI容器会清理上下文,自然触发@PreDestroy。但显式标注@Dependent后,就会遵循该作用域的规则,不再触发默认的@RequestScoped绑定。
解决方案
如果想要每个请求完成时触发@PreDestroy,直接将@Dependent替换为@RequestScoped即可:
@RequestScoped @Path("/users") @Produces(MediaType.APPLICATION_JSON) public class UserResourceImpl { // 原有代码保持不变 }
你的beans.xml设置的是bean-discovery-mode="annotated",@RequestScoped属于CDI作用域注解,符合“annotated”的Bean发现规则,这个Bean会被CDI容器正确识别和管理,请求结束时@PreDestroy会正常执行。
补充说明
别混淆@Dependent和@RequestScoped的核心差异:
@RequestScoped:拥有独立的请求上下文,请求开始时创建实例,请求结束时由CDI容器统一销毁,一定会触发@PreDestroy。@Dependent:没有自己的上下文,实例的生命周期完全依附于持有者,除非持有者主动调用销毁逻辑,否则@PreDestroy不会执行。
内容的提问来源于stack exchange,提问作者office.aizaz

