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