简体   繁体   中英

Spring @Autowire many beans

I am writing a Factory class that depending on a given parameter would return various implementations of an interface:

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

Now instead of new I want to leverage Spring's dependency injection capabilities. I want to use @Annotation based autowiring.

First thing coming to mind is to use @Autowire on field like so:

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

But then I remember that field autowiring is not recommended because it is hard to unit test such classes.

Next thing I could try would be to use setter level autowiring to allow for testing:

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

OK this looks better, but wait, there is more - I want my class to be immutable so I want my private fields to be final and only get populated at constructor time. So next thing I could try would be constructor based injection:

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

Looks good, but here comes the question - if I have 20 different types I can return from this factory depending on the param value, I can't create a 20 param constructor.

Please suggest, what would be the best approach in this case (trying to avoid xml based autowiring).

Thank you

Assuming all your MyType classes are already Spring managed beans, you can simply use a list.

Assuming

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

You can then just autowire a List into Factory :

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

This list will contain both myTypeA and myTypeB . From there, what I'd likely do is something like this in 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);
  } 
}

You can continue to follow this approach.

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

Now to solve the problem of injecting mocks int your test cases, you can just use the @InjectMocks annotation to do it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM