Spring @Autowire many beans

ACV :

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

Christopher Schneider :

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

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=20883&siteId=1