My understanding is that from spring 4 and onward, a generic type can serve as a valid qualifier. This works just fine in the following (basic) example:
public interface TypeResolver<T>{
Class<T> type();
}
#Configuration
public TypeResolversConfig{
#Bean
public TypeResolver<Integer> integerTypeResolver(){
return () -> Integer.class
}
#Bean
public TypeResolver<String> stringTypeResolver(){
return () -> String.class
}
}
#Service
public class SomeService{
#Autowired
private TypeResolver<Integer> integerTypeResolver;
}
However if I add another bean as such:
#Service
public class SomeOtherService<T>{
#Autowired
private TypeResolver<T> tTypeResolver;
}
And then proceed to edit SomeService as follows:
#Service
public class SomeService{
#Autowired
private SomeOtherService<Integer> someOtherService;
}
I get a
Field tTypeResolver in com.example.demo.SomeOtherService required a single bean, but 2 were found
exception. Is there any way around this, or is this just an inherent limitation whereby spring can only get around type erasure to a limited extent? I am using spring boot 2.1.8.RELEASE.
Generics are not present during compile time,so Autowiring generic type is meaning less.
So you can achieve this one by like this
#Service
public class SomeService{
#Autowired
private TypeResolver<Integer> integerTypeResolver;
#Autowired
private TypeResolver<String> stringTypeResolver;
...
}
NO need of SomeOtherService.
Related
I'm trying to create a class that Autowire an object of type T.
#component
public class TaskScheduler<T extends TaskService>{
#Autowired
private T taskService;
}
the problem is that I have two components that extend TaskService.
#component
public class firstTaskService extends TaskService {
}
and
#component
public class secondTaskService extends TaskService {
}
so when this line is executed (ts is being created)
#Autowired
TaskScheduler<firstTaskService> ts;
I get this error :
Description:
Parameter 1 of constructor in TaskScheduler required a single bean, but 2 were found
the message I got suggested this :
Action: Consider marking one of the beans as #Primary, updating the consumer to accept multiple beans, or using #Qualifier to identify
the bean that should be consumed.
But from what I understood, the #Primary and #Qualifier annotations make me choose 1 of the components, which not what I want because I want to use firstTaskService and secondTaskService with that same class (TaskScheduler).
How could this be done?
Edit: Clarification: My objective is to reuse the TaskScheduler class with different classes that extend the TaskService class (not to use multiple classes that extend TaskService together in TaskScheduler).
If you want to autowire all beans that extends TaskService maybe you should change the autowired field to a List:
#Component
public class TaskScheduler<T extends TaskService>{
#Autowired
private List<T> taskService;
}
In this way Spring should put in the List all autowireable beans that extends TaskService.
EDIT: since you want to dinamically select the type of TaskService the only way I've found is the following. First, redefine your TaskScheduler:
public class TaskScheduler <T extends TaskService>{
private T taskService;
public void setTaskService(T taskService) {
this.taskService = taskService;
}
}
Your TaskService and related subclasses should remain untouched. Set up a configuration class as it follows:
#Configuration
public class TaskConf {
#Autowired
private FirstTaskService firstTaskService;
#Autowired
private SecondTaskService secondTaskService;
#Bean
public TaskScheduler<FirstTaskService> firstTaskServiceTaskScheduler(){
TaskScheduler<FirstTaskService> t = new TaskScheduler<>();
t.setTaskService(firstTaskService);
return t;
}
#Bean
public TaskScheduler<SecondTaskService> secondTaskServiceTaskScheduler(){
TaskScheduler<SecondTaskService> t = new TaskScheduler<>();
t.setTaskService(secondTaskService);
return t;
}
}
And then test your TaskScheduler in this way:
#Autowired
TaskScheduler<firstTaskService> ts;
i am really confused with spring annotations.
where to use # Autowired, where class is # Bean or # Component,
i understand we cannot use
Example example=new Example("String");
in Spring
but how alone
#Autowired
Example example;
will solve the purpose?
what about Example Constructor ,how spring will provide String value to Example Constructor?
i went through one of the article but it does not make much sense to me.
it would be great if some one can give me just brief and simple explanation.
Spring doesn't say you can't do Example example = new Example("String"); That is still perfectly legal if Example does not need to be a singleton bean. Where #Autowired and #Bean come into play is when you want to instantiate a class as a singleton. In Spring, any bean you annotate with #Service, #Component or #Repository would get automatically registered as a singleton bean as long as your component scanning is setup correctly. The option of using #Bean allows you to define these singletons without annotating the classes explicitly. Instead you would create a class, annotate it with #Configuration and within that class, define one or more #Bean definitions.
So instead of
#Component
public class MyService {
public MyService() {}
}
You could have
public class MyService {
public MyService() {}
}
#Configuration
public class Application {
#Bean
public MyService myService() {
return new MyService();
}
#Autowired
#Bean
public MyOtherService myOtherService(MyService myService) {
return new MyOtherService();
}
}
The trade-off is having your beans defined in one place vs annotating individual classes. I typically use both depending on what I need.
You will first define a bean of type example:
<beans>
<bean name="example" class="Example">
<constructor-arg value="String">
</bean>
</beans>
or in Java code as:
#Bean
public Example example() {
return new Example("String");
}
Now when you use #Autowired the spring container will inject the bean created above into the parent bean.
Default constructor + #Component - Annotation is enough to get #Autowired work:
#Component
public class Example {
public Example(){
this.str = "string";
}
}
You should never instantiate a concrete implementation via #Bean declaration. Always do something like this:
public interface MyApiInterface{
void doSomeOperation();
}
#Component
public class MyApiV1 implements MyApiInterface {
public void doSomeOperation() {...}
}
And now you can use it in your code:
#Autowired
private MyApiInterface _api; // spring will AUTOmaticaly find the implementation
I want to construct the #repository with some parameters:
#Repository
public class BasicRepository<T> {
#Autowired
MongoTemplate mongoTemplate;
private final Class typeParameterClass;
public BasicRepository(Class typeParameterClass){
this.typeParameterClass = typeParameterClass;
}
public void createCollection(T t) {
if (!mongoTemplate.collectionExists(typeParameterClass)) {
mongoTemplate.createCollection(typeParameterClass);
}
}
}
In java configuration:
#Configuration
public class SpringConfiguration {
#Bean
public BasicRepository<Topic> topicDao(){
return new BasicRepository<Topic>(Topic.class);
}
}
When I run the code ,it throws the exception about “Error creating bean with name 'basicRepository'” ,I think the annotation of "#Repository" doesnt have the constructors,I want to initialize the "typeParameterClass",Someone can explain how?
So what appears to be happening is that the component-scan is finding your #Repository class and is trying to create a bean out of it, which it can't as it will not know how to instantiate your class with the typeParameterClass parameter.
The fix could be a few of them:
Probably not a good approach may be to go ahead and remove the
#Repository annotation as you are anyway generating the bean
through #Bean annotation. The reason this is not a good option is
because #Repository annotated beans behavior is modified at runtime
. For eg, persistence exception translation to spring runtime types.
One more option could be to retain #Repository annotation but
prevent component scanning for this specific class or specific
package.
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 #Components (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
I have a question related to Spring injection.
I have a class defined with a generic type parameter. I want to know whether it is possible to inject the class object of the type T ( I mean T.class) ?
Like this:
#Component
public class MyExecutor<T> {
#Autowired
public MyExecutor(<Inject class object of T>) {
....
}
}
Thank you very much.
That concrete example would not work, but there is another way to create beans using generics and keep the type when injecting the bean in other beans.
Spring 4 has extended the support for generics in Java configuration. It is now possible to define two beans that differ only in the generic parameter used, and inject them by type in another bean, see this JIRA:
#Configuration
public class Config {
#Bean("beanA")
public MyExecutor<A> beanA() {
return new MyExecutor<A>(A.class);
}
#Bean("beanB")
public MyExecutor<B> beanB() {
return new MyExecutor<B>(B.class);
}
}
Then beanA or beanB can be injected by type:
#Component
public class OtherClass {
#Autowired
private MyExecutor<A> beanA;
}