英文:
SpringBoot @WebMvcTest is loading non-dependent beans when @ComponentScan is used along with @SpringBootApplication
问题
我正在使用 SpringBoot 2.3.3.RELEASE,并且我有以下的Web控制器和服务。
myapp
- controllers
- ProductController
- OrderController
- services
- ProductService
- OrderService
ProductController
仅依赖于 ProductService
,而 OrderController
仅依赖于 OrderService
。
以下是我的SpringBoot主入口类:
package com.sivalabs.myapp;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
我有一个用于测试 ProductController
的 @WebMvcTest
控制器,如下所示:
@WebMvcTest(controllers = ProductController.class)
class ProductControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private ProductService productService;
//一些测试
}
在这个配置下一切都运行得非常完美。
我尝试在Spring组件中使用一些外部库,该库具有不同的包名称,因此我想要重写 @ComponentScan
,如下所示:
package com.sivalabs.myapp;
import com.somelib.BeanConfig;
@SpringBootApplication
@ComponentScan(basePackageClasses = {Application.class, BeanConfig.class})
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
当我在主入口类上包含 @ComponentScan
并运行我的基于 @WebMvcTest
的测试 ProductControllerTest
时,除了初始化 ProductService
外,SpringBoot 还试图初始化 OrderService
。理论上,ProductControllerTest
不应加载 OrderService
,因为 ProductController
不依赖于 OrderService
。这是一个错误吗?
解决方法:
- 如果我使用与
@SpringBootApplication
元注解上使用的方式相同的方式使用@ComponentScan
,并包括basePackageClasses
,它可以正常工作。
package com.sivalabs.myapp;
@SpringBootApplication
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class)
},
basePackageClasses = {Application.class, BeanConfig.class})
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
- 如果我不是在主入口类上添加
@ComponentScan
,而是添加另一个配置类并在该类上添加@ComponentScan
,那么它可以正常工作。
package com.sivalabs.myapp.config;
@Configuration
@ComponentScan(basePackageClasses = {BeanConfig.class})
public class AppConfig {
}
这是组件扫描过程中的错误吗,还是按预期工作的?
英文:
I am using SpringBoot 2.3.3.RELEASE and I have following web controllers and Services.
myapp
- controllers
- ProductController
- OrderController
- services
- ProductService
- OrderService
ProductController
only depends on ProductService
and OrderController
only depends on OrderService
.
Following is my SpringBoot main entrypoint class:
package com.sivalabs.myapp;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
I have an @WebMvcTest
controller for testing ProductController
as follows:
@WebMvcTest(controllers = ProductController.class)
class ProductControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private ProductService productService;
//some tests
}
Everything works perfectly fine with this configuration.
I am trying to use some external library with Spring components which has different package name, so I want to override @ComponentScan
as follows:
package com.sivalabs.myapp;
import com.somelib.BeanConfig;
@SpringBootApplication
@ComponentScan(basePackageClasses = {Application.class, BeanConfig.class})
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
When I include @ComponentScan
on my main entrypoint class and run my @WebMvcTest
based test ProductControllerTest
then in addition to ProductService
SpringBoot is trying to initialise OrderService
also. Ideally ProductControllerTest
should not load OrderService
as ProductController
doesn't depend on OrderService
. Is it a bug?
Workarounds:
- If I use
@ComponentScan
the way it is used on@SpringBootApplication
meta-annotation and includebasePackageClasses
it is working fine.
package com.sivalabs.myapp;
@SpringBootApplication
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) },
basePackageClasses = {Application.class, BeanConfig.class})
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
- Instead of adding
@ComponentScan
on main entrypoint class if I add another configuration class and add@ComponentScan
on that class then it's working fine.
package com.sivalabs.myapp.config;
@Configuration
@ComponentScan(basePackageClasses = {BeanConfig.class})
public class AppConfig {
}
Is it a bug in component scanning process or is it working as expected?
答案1
得分: 1
它按预期工作。
@SpringBootAplication 已经使用过滤器进行了 @ComponentScan,重新声明它,就像在解决方法#1中那样,是没有意义的,对吧?
原始代码的问题在于 @ComponentScan 会覆盖在 @SpringBootApplocation 注解内部应用的排除过滤器。所以当 @WebMvc 尝试使用过滤器请求上下文的一部分时,它会失败,并且会加载整个上下文。
只需检查在每种情况下为上下文初始化了哪些 bean。并且坚持使用解决方案2。@SpringBootTest 将扫描放置应用程序类的包,任何附加包都应该由配置来扫描。这甚至更加灵活,对吧?
英文:
It working as expected.
@SpringBootAplication already have @ComponentScan with filters, and redeclare it as in workaroud #1 doesn't make sense, right?
Problem With original code is that @ComponentScan overrides exclude filters applied inside @SpringBootApplocation annotation. And stuff is not excluded, so when @WebMvc tries to request part of context using filters it fails, and entire context is loaded.
Just check what beans are initialized for context in each scenario. And stick to solution 2. @SpringBootTest will scan packages where application class is places, and any additional packages should be scanned by configurations. That is even more flixible right?
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论