SpringBootTest中如何移除/覆盖@Component类的@RestartScope注解?
问题描述
有一个标注了@Component的RequestInterceptor类,同时被@RestartScope注解修饰。在使用@SpringBootTest执行集成测试时,配置了@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD)清理上下文,只要测试触发该拦截器中调用的bean方法,就会抛出如下异常:
Request processing failed; nested exception is org.springframework.boot.context.properties.ConfigurationPropertiesBindException: Error creating bean with name 'spring.flyway-org.springframework.boot.autoconfigure.flyway.FlywayProperties': Could not bind properties to 'FlywayProperties' : prefix=spring.flyway, ignoreInvalidFields=false, ignoreUnknownFields=true; nested exception is java.lang.IllegalStateException: org.springframework.web.context.support.GenericWebApplicationContext@55fbebba has been closed already org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.boot.context.properties.ConfigurationPropertiesBindException: Error creating bean with name 'spring.flyway-org.springframework.boot.autoconfigure.flyway.FlywayProperties': Could not bind properties to 'FlywayProperties' : prefix=spring.flyway, ignoreInvalidFields=false, ignoreUnknownFields=true; nested exception is java.lang.IllegalStateException: org.springframework.web.context.support.GenericWebApplicationContext@55fbebba has been closed already at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014) at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909)
移除@RestartScope注解后所有测试正常执行,但无法修改该类,需要找到在测试中绕过这个问题的方法。
解决方案
1. 覆盖Bean作用域(推荐)
创建测试专属配置类,重新定义RequestInterceptor的Bean,覆盖原有的@RestartScope作用域为默认的单例:
@TestConfiguration public class TestInterceptorOverrideConfig { // 按实际情况注入原Interceptor依赖的所有Bean @Bean @Primary // 确保这个Bean优先被容器选中注入 public RequestInterceptor testRequestInterceptor(DependencyBean1 dep1, DependencyBean2 dep2) { // 若原类用构造注入,直接通过构造方法传入依赖 return new RequestInterceptor(dep1, dep2); } }
在测试类上引入这个配置:
@SpringBootTest @DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD) @Import(TestInterceptorOverrideConfig.class) public class YourIntegrationTest { // 测试代码 }
容器会优先使用这个测试配置的Bean,原有的@RestartScope修饰的Bean会被忽略,避免上下文关闭后的作用域冲突。
2. 测试中禁用该拦截器
如果测试不需要这个拦截器的功能,可以通过测试配置移除它的注册:
@TestConfiguration public class DisableInterceptorConfig { @Bean public WebMvcConfigurer interceptorDisabler() { return new WebMvcConfigurer() { @Override public void addInterceptors(InterceptorRegistry registry) { // 过滤掉原RequestInterceptor实例 registry.getInterceptors().removeIf(interceptor -> interceptor instanceof RequestInterceptor); } }; } }
同样在测试类上用@Import(DisableInterceptorConfig.class)引入即可。
3. 调整上下文清理策略
如果测试之间的上下文污染可以接受,修改@DirtiesContext的classMode,比如改用AFTER_EACH_TEST_METHOD或者AFTER_CLASS:
@SpringBootTest @DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) public class YourIntegrationTest { // 测试代码 }
这个方案无需额外配置,但如果测试之间有状态依赖可能会导致测试失败,需要根据实际情况评估。
内容的提问来源于stack exchange,提问作者Felipe Wagner
相关产品推荐
相关产品推荐

