Servlet是否为Java EE的CDI/托管Bean?容器中能否注入至CDI Bean及测试类?
咱们把你的问题拆成几个部分来逐个捋清楚:
1. Servlet是否属于Java EE中的CDI/托管Bean?
答案是不属于。Servlet是Java EE规范里的Web组件,由Servlet容器(比如WildFly的Web容器模块、Tomcat)负责生命周期管理——从初始化、处理请求到销毁全由它管;而CDI托管Bean是CDI容器的管理对象,遵循CDI的生命周期、依赖注入规则。两者属于Java EE中完全不同的组件模型,管理容器和生命周期逻辑都不重叠。
2. 在WildFly、JBoss等容器中,Servlet是否会被视为托管Bean?能否注入到其他CDI Bean中?
默认情况下,Servlet不会被CDI容器当作托管Bean,所以你没法直接用@Inject把Servlet注入到其他CDI Bean里。核心原因还是两者的管理边界不同:Servlet归Servlet容器管控,CDI容器不会主动接管它的生命周期。
不过有几个变通思路可以实现类似需求:
- 最推荐的做法:把Servlet里的业务逻辑抽离到独立的CDI Bean中,让Servlet去注入这个业务Bean。既符合关注点分离,也方便后续测试。
- 如果一定要在CDI Bean里获取Servlet实例,可以通过
HttpServletRequest间接获取(比如request.getServletContext().getServlet("你的Servlet名称")),但这不是CDI注入,还要注意单例Servlet和请求作用域的冲突问题。 - 非标准的极端情况:给Servlet添加CDI作用域注解(比如
@RequestScoped)并实现Serializable,部分容器可能支持这种混合模式,但兼容性差,不建议这么做。
3. 使用CdiRunner(CDI-Unit)测试时,如何注入Servlet并测试其公开方法?
CDI-Unit的核心是模拟CDI容器环境,但它不会模拟Servlet容器,所以默认没法直接把Servlet当作CDI Bean注入测试类。推荐两种测试方案:
方案一:抽离业务逻辑到CDI Bean(优先选择)
把Servlet中需要测试的公开方法的逻辑,迁移到一个独立的CDI Bean(比如加@Named或自定义限定符的Bean)里。这样你可以直接用CDI-Unit注入这个业务Bean,编写单元测试,完全不需要涉及Servlet容器的细节。
方案二:手动实例化Servlet并注入依赖
如果一定要测试Servlet本身的方法,可以这么操作:
- 在测试类中,通过CDI-Unit获取Servlet依赖的所有CDI Bean(比如用
@Inject Instance<DependencyBean> dependencyInstance)。 - 手动实例化Servlet对象(比如
MyServlet servlet = new MyServlet();)。 - 手动给Servlet注入依赖(比如调用setter方法,或者通过反射设置属性)。
- 最后调用Servlet的公开方法进行测试。
举个简单的代码示例:
@RunWith(CdiRunner.class) public class MyServletTest { @Inject private MyDependencyBean dependencyBean; // Servlet依赖的CDI Bean @Test public void testServletPublicMethod() { MyServlet servlet = new MyServlet(); // 手动注入依赖(假设Servlet有对应的setter) servlet.setDependencyBean(dependencyBean); // 调用公开方法并验证结果 String result = servlet.myPublicTestMethod(); assertEquals("预期返回值", result); } }
这种方式能测试Servlet的方法,但没法模拟Servlet容器的上下文(比如ServletContext、HttpServletRequest),如果方法依赖这些对象,还需要配合Mock框架(比如Mockito)来模拟。
内容的提问来源于stack exchange,提问作者office.aizaz

