在 for 循环中获取 autowired 类 bean 的不同实例

Get different instance of autowired class bean in for loop

提问人:Akshay Borse 提问时间:11/15/2023 更新时间:11/15/2023 访问量:26

问:

我是Spring Boot的新手,学习autowire注解,依赖注入和单元测试

我在Spring Boot中有一个代码,如下所示

@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
  }
}

我想用代码实现 3 件事:

  1. 与其使用我希望它使用 @autowired(依赖注入)的新关键字创建 B 的对象,这将是有帮助的,这样我就可以在单元测试时模拟 B 并模拟 execute() 方法的返回。

  2. 由于 B 有一个参数化构造函数,我希望能够通过传递参数来获取 bean,参数值将是动态的,即对于循环的每次迭代,传递的值都会有所不同。我在网上找到的示例使用属性文件中的值。

  3. classAMethod() 是在多线程环境中调用的,这就是为什么在 for 循环中我每次都创建一个类 B 的新实例,以避免并发问题,所以使用 autowire 我想获取类 B 的新实例。我不能在 B 中使用 synchronized 关键字,因为它在内部执行大量操作,因此每次创建新实例都比锁定和解锁代码块更简单,并且会导致性能问题降低,因为只有一个线程可以访问该代码块。

您能为这种情况提供一个示例吗?或者有没有其他方法。

我想这样做有两个原因:

  1. 单元测试
  2. 完全使用 Spring Boot 概念 (DI) 制作应用程序并避免使用新关键字

我可以通过构造函数模拟来进行单元测试,但这种方法的问题在于我们需要执行 @PrepareForTest(A.class),它有自己的问题,例如代码覆盖率显示为 0%。

谢谢

Java Spring spring-Boot 多线程单元 测试

评论

0赞 M. Deinum 11/15/2023
1、你不能。2. 使用该方法提供构造函数参数。为什么一开始就要使用自动连线,使用没有错。你可以把它移到一个你自动连线的工厂,这个工厂你可以在测试中模拟。applicationcontext.getBeannew

答:

1赞 Sahab 11/15/2023 #1

让我们看看我们如何解决这个问题:

  • 配置一个 bean 来供应,我们可以使用胎面安全列表使用dynamicStirngValuedynamicIntValueCopyOnWriteArrayList
@Configuration
public class ParamsKeyVal  {
    @Bean
    public List<HashMap<String, Integer>>  getParams() {
        HashMap<String, Integer> params = new HashMap<>();
        List<HashMap<String, Integer>> list = new CopyOnWriteArrayList<>();

        list.add(Map.of("key1", 10));
        list.add(Map.of("key2", 13));

        // this params can found form other source like properties file/database
        return list;
    }
}

  • 更新组件 A,使其易于测试
@Component
class A {
    private final List<HashMap<String, Integer>> params;
    private final B b;

    @Autowired
    public A(List<HashMap<String, Integer>> params, B b) {

        this.params = params;
        this.b = b;
    }

    public void classAMethod() {
        for(int i=0; i<params.size(); i++) {

            HashMap<String, Integer> entrySet = params.get(i);
            String dynamicStirngValue = null;
            Integer dynamicIntValue = null;
            for (Map.Entry<String, Integer> entry : entrySet) {

                dynamicStirngValue =  entry.getKey();
                dynamicIntValue = entry.getValue();
                break;
            }

            b.setDynamicStirngValue(dynamicStirngValue);
            b.setDynamicIntValue(dynamicIntValue);
            b.setDynamicInt(i);

            Object xyz = b.execute();
        }
    }

}

  • 更新组件 B 的无参数构造函数

@Component
@Scope("prototype")
class B {

    public B() {

    }
    public B(String str1, int int1, int int2) {
        // setting up the passed parameters
    }
    public Object execute() {
        // some code to use the class variables
    }
}