I would like to have a singleton bean instance by generic parameter based on a single @Component
generic class.
(I am using Spring 4.)
My code :
I have an interface
like this :
public interface Mapper<I, O> {
...
}
And multiple implementation of it which are Spring @Component
s (singletons). Something like this :
@Component
public class MapperA implements Mapper<ClazzAI, ClazzAO> {
...
}
and
@Component
public class MapperB implements Mapper<ClazzBI, ClazzBO> {
...
}
where ClazzAI
, ClazzAO
, ClazzBI
and ClazzBO
are basic Java classes.
I have another Spring @Component
(singleton) which have a Mapper
class
as a generic parameter :
@Component
public class TransformerImpl<I, O, M extends Mapper<I, O>> {
/** The Mapper */
protected final M mapper;
@Inject
private TransformerImpl(final M mapper) {
this.mapper= mapper;
}
...
}
and I would like to use it like this :
@Inject
private TransformerImpl<ClazzAI, ClazzAO, MapperA> transformerA;
@Inject
private TransformerImpl<ClazzBI, ClazzBO, MapperB> transformerB;
The problem :
But Spring is not able to instantiate those 2 objects because it founds 2 implementations of Mapper
: MapperA
and MapperB
even if I specify which implementation I want as a generic parameter.
Any idea how to make it without the need of instantiate all of those beans in a @Configuration
class
?
You're asking for a singleton but requiring two injection points
@Inject
private TransformerImpl<ClazzAI, ClazzAO, MapperA> transformerA;
@Inject
private TransformerImpl<ClazzBI, ClazzBO, MapperB> transformerB;
for differently constructed objects. That doesn't make much sense.
You now realize you need two beans. If you can't (don't want to) do it in a @Configuration
class with @Bean
factory methods, you'll need to declare (and scan) two separate @Component
classes. (I made your parent constructor public here.)
@Component
class MapperATransformerImpl extends TransformerImpl<ClazzAI, ClazzAO, MapperA> {
@Inject
public MapperATransformerImpl(MapperA mapper) {
super(mapper);
}
}
@Component
class MapperBTransformerImpl extends TransformerImpl<ClazzBI, ClazzBO, MapperB> {
@Inject
public MapperBTransformerImpl(MapperB mapper) {
super(mapper);
}
}
When processing the injection target
@Inject
private TransformerImpl<ClazzAI, ClazzAO, MapperA> transformerA;
Spring will find the MapperATransformerImpl
, which is of type TransformerImpl<ClazzAI, ClazzAO, MapperA>
and inject that.
Try with Spring 4. See Using generics as autowiring qualifiers
Edit
Like @SotiriosDelimanolis explained in his answer, Spring 4 can use type parameter information as qualifiers to select which bean definition matches a particular injection point, but in the end, it will only match against bean definition with concrete type definitions. In your case, the problem is that you need a TransformerImpl bean definition for each concrete type you want to inject.
As an alternative to defining all bean definition explicitly, check my answer to Spring autowiring issues on paramaterized class
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With