How to Autowire Bean of Generic Type <T> in Spring

How to Autowire Bean of generic type T in Spring?

Simple solution is to upgrade to Spring 4.0 as it will automatically consider generics as a form of @Qualifier, as below:

@Autowired
private Item<String> strItem; // Injects the stringItem bean

@Autowired
private Item<Integer> intItem; // Injects the integerItem bean

Infact, you can even autowire nested generics when injecting into a list, as below:

// Inject all Item beans as long as they have an <Integer> generic
// Item<String> beans will not appear in this list
@Autowired
private List<Item<Integer>> intItems;

How this Works?

The new ResolvableType class provides the logic of actually working with generic types. You can use it yourself to easily navigate and resolve type information. Most methods on ResolvableType will themselves return a ResolvableType, for example:

// Assuming 'field' refers to 'intItems' above
ResolvableType t1 = ResolvableType.forField(field); // List<Item<Integer>>
ResolvableType t2 = t1.getGeneric(); // Item<Integer>
ResolvableType t3 = t2.getGeneric(); // Integer
Class<?> c = t3.resolve(); // Integer.class

// or more succinctly
Class<?> c = ResolvableType.forField(field).resolveGeneric(0, 0);

Check out the Examples & Tutorials at below links.

  • Spring Framework 4.0 and Java Generics
  • Spring and Autowiring of Generic Types

Autowiring Beans with Generic typed parameters at runtime

Something like this, I guess:

String [] names = context.getBeanNamesForType(ResolvableType.forClassWithGenerics(Bar.class, Detail.class));
Foo<Detail> bar = context.getBean(names[0]);

Why @Autowired does not work with generic type T?

The reason for this is type erasure which happens at compile time while bean injection happens at runtime.

Since there are no bounds on it T gets erased and basically replaced by Object and Spring Data can't create repositories for Object.

See also Using generics in Spring Data JPA repositories

Spring 3.2 Autowire generic types

How about adding a constructor to the GenericService and move the autowiring to the extending class, e.g.

class GenericService<T, T_DAO extends GenericDao<T>> {
private final T_DAO tDao;

GenericService(T_DAO tDao) {
this.tDao = tDao;
}
}

@Service
FooService extends GenericService<Foo, FooDao> {

@Autowired
FooService(FooDao fooDao) {
super(fooDao);
}
}

Update:

As of Spring 4.0 RC1, it is possible to autowire based on generic type, which means that you can write a generic service like

class GenericService<T, T_DAO extends GenericDao<T>> {

@Autowired
private T_DAO tDao;
}

and create multiple different Spring beans of it like:

@Service
class FooService extends GenericService<Foo, FooDao> {
}

SpringBoot Autowiring a generic type fails because of multiple possible beans

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;


Related Topics



Leave a reply



Submit