使用Spring测试被EJB拦截器拦截的EJB服务时拦截器不生效如何解决
问题根因
你当前是将EJB类当成普通Spring Bean直接注册到Spring测试上下文,Spring 4.3原生不支持解析Java EE的@Interceptors、@AroundInvoke这类EJB专属注解,因此拦截器逻辑不会被触发。
方案1:基于Spring AOP适配现有拦截器(改动最小,测试速度快)
无需修改原有拦截器、服务类的业务代码,仅新增Spring侧配置即可:
- 调整测试配置类,注册拦截器并新增AOP适配逻辑:
@Configuration public static class ClassContextTest { // 注册原有EJB拦截器到Spring容器 @Bean public ValidationInterceptor validationInterceptor() { return new ValidationInterceptor(); } @Bean public FooServiceImpl produceService() { return new FooServiceImpl(); } @Bean public FooDao produceDao() { //...原有逻辑不变 } // 新增切面配置,适配EJB拦截器执行逻辑 @Bean public Advisor ejbInterceptorAdvisor(ValidationInterceptor validationInterceptor) { // 切点规则:匹配所有带@Interceptors注解的类的公共方法,也可缩小范围仅匹配FooServiceImpl AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut(); pointcut.setExpression("@within(javax.interceptor.Interceptors) && execution(public * *(..))"); // 环绕通知适配EJB的InvocationContext规范 return new DefaultPointcutAdvisor(pointcut, (MethodInterceptor) invocation -> { InvocationContext ctx = new InvocationContext() { @Override public Object getTarget() { return invocation.getThis(); } @Override public Method getMethod() { return invocation.getMethod(); } @Override public Object[] getParameters() { return invocation.getArguments(); } @Override public void setParameters(Object[] params) { invocation.setArguments(params); } @Override public Map<String, Object> getContextData() { return new HashMap<>(); } @Override public Object proceed() throws Exception { try { return invocation.proceed(); } catch (Throwable e) { throw new Exception(e); } } @Override public Constructor<?> getConstructor() { return null; } @Override public Object getTimer() { return null; } }; // 直接调用原有EJB拦截器逻辑 return validationInterceptor.intercept(ctx); }); } }
- 给测试类开启Spring AOP支持:
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = {FooServiceTest.ClassContextTest.class}) @EnableAspectJAutoProxy // 新增此注解 public class FooServiceTest { // 原有测试逻辑完全不变 }
该方案所有业务代码不需要调整,测试依然运行在纯Spring上下文,启动速度快,适配成本极低。如果有多个EJB拦截器,只需要扩展适配逻辑,读取@Interceptors注解配置的拦截器列表按顺序调用即可。
方案2:使用Arquillian嵌入式容器测试(完全对齐生产环境)
如果需要100%模拟WildFly生产环境的EJB运行规则,建议改用Arquillian集成测试框架,启动嵌入式WildFly容器部署服务,所有EJB注解都会被容器原生识别,无需做任何适配:
- 先引入Arquillian、WildFly嵌入式容器对应Java EE 7版本的依赖。
- 改造测试类为Arquillian测试:
@RunWith(Arquillian.class) public class FooServiceTest { @Deployment public static WebArchive createDeployment() { return ShrinkWrap.create(WebArchive.class) .addClasses(FooService.class, FooServiceImpl.class, FooDao.class, Foo.class, ValidationInterceptor.class) // 按需加入所有依赖的DAO、Mapper等类 .addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml"); } @EJB public FooService service; @Test public void createTest() throws Exception { Foo foo = new Foo(); // ...原有测试逻辑不变 service.create(foo); // ...原有测试逻辑不变 } }
该方案完全对齐生产运行逻辑,缺点是测试启动速度慢,依赖嵌入式容器资源。
内容的提问来源于stack exchange,提问作者kwisatz
相关产品推荐
相关产品推荐

