Class Autowiring <T> for any T
So, I have some common components that use reflection to initialize themselves, and so they need Class<T> objects during initialization. These components use annotations to generate useful metadata and / or transform the object into another representation that is more suitable for the task.
I reduced my problem to this sample:
@Component public class Instantiator<T> { final Class<T> klass; @Autowired public Instantiator(Class<T> klass) { this.klass = klass; } public T instantiate() { try { return klass.newInstance(); } catch (InstantiationException|IllegalAccessException e) { throw new RuntimeException(e); } } } Spring doesn't know how to automatically inject instances of Class<T> , so I tried to write the following boilerplate code for every T for which I want Class<T> be available.
@Bean Class<Instantiatee> instantiateeClass() { return Instanciatee.class; } This does not work.
Spring, since version 4 supports Autowiring generic types, but in my case it should determine to which T Class<T> assigned. Since Spring creates singleton beans by default and therefore cannot output the corresponding T , I tried adding @Scope("prototype") , but I ended up with ClassCastException , since the container does not know how to draw T output anyway.
So, I removed the @Component annotation from Instantiator and set this workaround for every T I have:
@Bean Instantiator<Instantiatee> instantiator() { return new Instantiator<>(Instantiatee.class); } Do you know a workaround to do this work so that T output every time I want Instantiator or another common component in it?
FYI, we are using Spring 4.1.4 with download.
I posted a more complete sample: https://gist.github.com/anonymous/79e1a7ebe7c25c00a6c2 .
By defining beans with @Bean , you specify the bean name in the default method name - in your case getInstanciateeClass . Also, when auto-paused, the default bean is considered the parameter name, in your case klass . Because of this, Spring cannot match beans because they have different names and most likely there is more than one Class instance in the ApplicationContext . It doesnβt matter if one is Class<Foo> and the other is Class<Bar> , Spring sees them as Class , so it cannot autoconnect by type.
You can fix this by using the same default name, both when defining a bean or when it is auto-updated.
@Autowired public Instanciator(Class<T> klass) { this.klass = klass; } @Bean Class<Instanciatee> klass() { return Instanciatee.class; } You can also specify the bean name in the @Bean annotation:
@Autowired public Instanciator(Class<T> klass) { this.klass = klass; } @Bean(name = "klass") Class<Instanciatee> getInstanciateeClass() { return Instanciatee.class; } Or you can also name the bean when auto-provisioning:
@Autowired @Qualifier("getInstanciateeClass") public Instanciator(Class<T> klass) { this.klass = klass; } @Bean Class<Instanciatee> getInstanciateeClass() { return Instanciatee.class; }