104

I have a bean Item<T> which is required to be autowired in a @Configuration class.

@Configuration
public class AppConfig {

    @Bean
    public Item<String> stringItem() {
        return new StringItem();
    }

    @Bean
    public Item<Integer> integerItem() {
        return new IntegerItem();
    }

}

But when I try to @Autowire Item<String>, I get following exception.

"No qualifying bean of type [Item] is defined: expected single matching bean but found 2: stringItem, integerItem"

How should I Autowire generic type Item<T> in Spring?

Shishir Kumar
  • 7,981
  • 3
  • 29
  • 45
user3374518
  • 1,359
  • 3
  • 11
  • 11

6 Answers6

174

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.

starball
  • 20,030
  • 7
  • 43
  • 238
Shishir Kumar
  • 7,981
  • 3
  • 29
  • 45
  • 14
    This is amazing. I thought Type Erasure made this sort of thing not possible. – John Strickler Sep 26 '14 at 14:53
  • 6
    How can I get bean by resolvabletype from beanfactory? – ceram1 Oct 18 '14 at 00:43
  • @JohnStrickler I also thought this cannot be safely done. I Haven't check the implementation of ResolvableType yet, but is it means we can safely get generic type now? – xi.lin Jan 30 '15 at 04:37
  • @xi.lin I can't tell you how it's implemented, but looking at the [`ResolvableType` API](http://docs.spring.io/spring/docs/4.0.0.RC2/javadoc-api/org/springframework/core/ResolvableType.html) (also linked in answer) and javadoc, clearly it can be done (note the `getGeneric()` method) – drew moore Oct 04 '16 at 06:13
  • 3
    Actually In some cases you can get generic type in runtime in Java. One condition, you need to have subclass of generic class. Example: Parent class `public class Parent {}` subclass `public class Child extends Parent { }` and now: `Child c = new Child(); System.out.println(((ParameterizedType)c.getClass().getGenericSuperclass()).getActualTypeArguments()[0]);` will print `class java.lang.Integer` – Michał Przybylak Feb 21 '17 at 16:14
  • 1
    Will this work with package-scan bean autoconfiguration? I don't think so. – Preslav Rachev Jun 28 '18 at 08:34
  • Would this work for generic types with two or more parameterized items, e.g. `Item` for one `@Bean`, and `Item` for the second `@Bean`? – NuCradle Sep 14 '19 at 17:55
  • @JohnStrickler just in case: [this is how it is achieved](https://stackoverflow.com/questions/64873444/generic-class-type-parameter-detail-at-runtime/64934396#64934396), pretty much – Eugene Jan 02 '21 at 15:54
  • @ceram1, see: [Get spring bean via context using generic](https://stackoverflow.com/questions/30374267/). – jaco0646 Jan 02 '21 at 17:46
15

If you dont want to upgrade to Spring 4 you have to autowire by name as below :

@Autowired
@Qualifier("stringItem")
private Item<String> strItem; // Injects the stringItem bean

@Autowired
@Qualifier("integerItem")
private Item<Integer> intItem; // Injects the integerItem bean
Shishir Kumar
  • 7,981
  • 3
  • 29
  • 45
Rishav Basu
  • 391
  • 4
  • 12
2

Below is a solution I made to answer this question:


        List<String> listItem= new ArrayList<>();

        ResolvableType resolvableType = ResolvableType.forClassWithGenerics(List.class, String.class);
        RootBeanDefinition beanDefinition = new RootBeanDefinition();
        beanDefinition.setTargetType(resolvableType);
        beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
        beanDefinition.setAutowireCandidate(true);

        DefaultListableBeanFactory bf = (DefaultListableBeanFactory) configurableWebApplicationContext.getBeanFactory();

        bf.registerBeanDefinition("your bean name", beanDefinition);
        bf.registerSingleton("your bean name", listItem);
applecrusher
  • 5,508
  • 5
  • 39
  • 89
1

Spring autowired strategy is defined in your configration file(application.xml).

if you don't defined, default is by Type, spring inject use JDK reflect mechanism.

so List?String? and List?Item?, the type is same List.class, so spring confused how to inject.

and as above persons response, you should be point @Qualifier to tell spring which bean should be inject.

i like spring configration file to define bean rather then Annotation.

<bean>
 <property name="stringItem">
        <list>
              <....>
        </list>
 </property>

zg_spring
  • 349
  • 1
  • 10
-1

Spring 4.0 is the answer with the @Qualifier annotation usage. Hope this helps

shikjohari
  • 2,278
  • 11
  • 23
-1

I believe it has nothing to with generics... If you are injecting two different beans of same type then you need to provide a qualifier to help Spring identify them;

... Elsewhere

@Configuration
@Bean
public Item stringItem() {
    return new StringItem();
}

@Bean
public Item integerItem() {
    return new IntegerItem();
}

If you have a non-generic declarations like these then you need to add qualifier to help Spring identify them...

@Autowired
**@Qualifier("stringItem")**
private Item item1;

@Autowired
**@Qualifier("integerItem")**
private Item item2;

Of course, in versions 4 and above spring considers the Generic Types through the resolvers which is very cool...

Ramkumar S
  • 97
  • 5