Spring Boot循环中获取@Autowired原型Bean实例的实现方案咨询
带动态参数的原型Bean注入与单元测试解决方案
问题场景
刚接触Spring Boot,正在学习@Autowired注解、Dependency Injection(依赖注入)及单元测试,现有如下代码:
@Component class A { public void classAMethod() { for(int i=0; i<10; i++) { B b = new B(dynamicStirngValue,dynamicIntValue, i); Object xyz = b.execute(); } } } @Component @Scope("prototype") class B { public B(String str1, int int1, int int2) { // setting up the passed parameters } public Object execute() { // some code to use the class variables } }
核心需求
- 不使用
new关键字,通过依赖注入获取B的实例,方便单元测试时Mock B的execute()方法; - B的带参构造器需支持传入动态参数(循环中每次参数不同);
classAMethod运行在多线程环境,每次必须获取B的新实例以避免并发问题,且不能用synchronized。
当前困境
通过构造器Mock时需要@PrepareForTest(A.class),导致代码覆盖率为0%,需要可行的替代方案。
解决方案
方法1:用Spring原生的ObjectFactory
直接借助ObjectFactory获取原型Bean实例,它天生支持传入动态构造参数,且每次调用都会返回新的原型实例。
修改后的A类代码:
@Component class A { private final ObjectFactory<B> bFactory; // 构造注入ObjectFactory public A(ObjectFactory<B> bFactory) { this.bFactory = bFactory; } public void classAMethod() { for(int i=0; i<10; i++) { // 每次调用getObject生成新的B实例,同时传入动态参数 B b = bFactory.getObject(dynamicStirngValue, dynamicIntValue, i); Object xyz = b.execute(); } } }
方法2:直接使用BeanFactory
如果觉得ObjectFactory不够直观,也可以注入BeanFactory,它的getBean方法同样支持传入构造参数,且对原型Bean每次调用都会生成新实例:
@Component class A { private final BeanFactory beanFactory; public A(BeanFactory beanFactory) { this.beanFactory = beanFactory; } public void classAMethod() { for(int i=0; i<10; i++) { B b = beanFactory.getBean(B.class, dynamicStirngValue, dynamicIntValue, i); Object xyz = b.execute(); } } }
单元测试实现(无需@PrepareForTest)
用Mockito即可完成Mock,不会影响代码覆盖率:
@ExtendWith(MockitoExtension.class) class ATest { @Mock private BeanFactory beanFactory; // 若用ObjectFactory则Mock ObjectFactory即可 @InjectMocks private A a; @Test void testClassAMethod() { // Mock B的实例 B mockB = Mockito.mock(B.class); // 配置BeanFactory返回Mock实例 when(beanFactory.getBean(B.class, anyString(), anyInt(), anyInt())).thenReturn(mockB); // 模拟execute方法的返回值 when(mockB.execute()).thenReturn(new Object()); // 执行测试方法 a.classAMethod(); // 验证调用次数 verify(beanFactory, times(10)).getBean(B.class, anyString(), anyInt(), anyInt()); verify(mockB, times(10)).execute(); } }
方案适配性说明
- 规避直接
new:通过Spring工厂类注入获取实例,符合依赖注入规范,测试时可轻松Mock; - 动态参数支持:
getObject/getBean方法均支持传入构造参数,循环内动态传参无压力; - 多线程安全:B为原型作用域,每次获取都是全新实例,不存在线程共享状态,无需
synchronized。
内容的提问来源于stack exchange,提问作者Akshay Borse
相关产品推荐
相关产品推荐

