【问题标题】:Spring @Autowire many beansSpring @Autowire 许多豆子
【发布时间】:2020-03-03 15:27:28
【问题描述】:

我正在编写一个工厂类,它根据给定的参数将返回接口的各种实现:

class Factory {
 public MyType getType(String param) {
  if (param.equals("A")) {
     return new MyTypeA();
   } else if (param.equals("B")) {
     return new MyTypeB();
 } 
}

现在我想利用 Spring 的依赖注入功能,而不是 new。我想使用基于@Annotation 的自动装配。

首先想到的是在字段上使用@Autowire,如下所示:

class Factory {
@Autowired
private MyTypeA myTypeA;
@Autowired
private MyTypeB myTypeB;
         public MyType getType(String param) {
          if (param.equals("A")) {
             return myTypeA;
           } else if (param.equals("B")) {
             return myTypeB;
         } 
        }

但后来我记得不建议使用字段自动装配,因为很难对此类类进行单元测试。

接下来我可以尝试使用 setter 级别的自动装配来进行测试:

class Factory {
private MyTypeA myTypeA;
private MyTypeB myTypeB;
         public MyType getType(String param) {
          if (param.equals("A")) {
             return myTypeA;
           } else if (param.equals("B")) {
             return myTypeB;
         } 

@Autowired
public void setMyTypeA(MyTypeA a) {
 this.myTypeA = a;
}

@Autowired
public void setMyTypeB(MyTypeB b) {
 this.myTypeB = b;
}
}

好的,这看起来更好,但是等等,还有更多 - 我希望我的类是不可变的,所以我希望我的私有字段是最终的,并且只在构造函数时填充。所以接下来我可以尝试的是基于构造函数的注入:

class Factory {

private final MyTypeA myTypeA;
private final MyTypeB myTypeB;

public Factory(MyTypeA myTypeA, MyTypeB myTypeB) {
 this.myTypeA = myTypeA;
 this.myTypeB = myTypeB;
}

         public MyType getType(String param) {
          if (param.equals("A")) {
             return myTypeA;
           } else if (param.equals("B")) {
             return myTypeB;
         } 
        }

看起来不错,但问题来了 - 如果我有 20 种不同的类型,我可以根据参数值从这个工厂返回,我不能创建一个 20 参数的构造函数。

请建议,在这种情况下最好的方法是什么(尽量避免基于 xml 的自动装配)。

谢谢

【问题讨论】:

  • 这就是 @Configuration 类和条件的用途。
  • 您可以使用List<MyType> types 作为构造函数参数。然后我要做的是用Map<String, MyType> typesgetType 返回types.get(param)
  • @chrylis-onstrike- 你能举个例子吗?
  • 至于不变性,除了final,你还可以使用java.util.Collections.unmodifiableList()
  • @ChristopherSchneider 该列表会被 Spring 自动装配吗?

标签: java spring dependency-injection autowired


【解决方案1】:

假设您所有的 MyType 类已经是 Spring 托管的 bean,您可以简单地使用一个列表。

假设

@Configuration
class Config{
   @Bean
   public MyType myTypeA() {
      return new MyTypeA();
   }
   @Bean
   public MyType myTypeB() {
      return new MyTypeB();
   }
}

然后您可以将列表自动连接到Factory

@Bean
public Factory factory(List<MyType> myTypes) {
   return new Factory(myTypes);
}

此列表将同时包含myTypeAmyTypeB。从那里,我可能会在Factory 中做类似的事情:

public class Factory {

  final Map<String, MyType> myTypes;

  public Factory(List<MyType> types) {
     //build map of types with "param" as the key
  }

  public MyType getType(String param) {
     return myTypes.get(param);
  } 
}

【讨论】:

    【解决方案2】:

    您可以继续采用这种方法。

        class Factory {
    @Autowired
    private MyTypeA myTypeA;
    @Autowired
    private MyTypeB myTypeB;
             public MyType getType(String param) {
              if (param.equals("A")) {
                 return myTypeA;
               } else if (param.equals("B")) {
                 return myTypeB;
             } 
            }
    

    现在要解决在您的测试用例中注入模拟的问题,您只需使用@InjectMocks 注解即可。

    【讨论】:

    • 我想避免为新课程这样做。缺点是当 myTypeA 不是模拟对象时
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-04
    相关资源
    最近更新 更多