如何在Spring中自动装配泛型类型<T>的Bean?

106

我有一个豆子Item<T>,需要在一个@Configuration类中自动装配。

@Configuration
public class AppConfig {

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

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

}

但是当我尝试@Autowire Item<String>时,我得到以下异常。

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

我该如何在Spring中自动装配泛型类型Item<T>

6个回答

177

简单的解决方案是升级到Spring 4.0,因为它会自动将泛型视为@Qualifier的一种形式,如下所示:

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

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

实际上,当注入到列表中时,您甚至可以自动装配嵌套泛型,如下所示:

// 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;

这是如何工作的?

新的ResolvableType类提供了实际处理泛型类型的逻辑。您可以自己使用它来轻松导航和解析类型信息。大多数ResolvableType上的方法本身将返回ResolvableType,例如:

// 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);

请查看以下链接中的示例和教程。


14
太惊人了。我原以为类型擦除会让这种事情变得不可能。 - John Strickler
6
我该如何从BeanFactory中通过ResolvableType获取Bean? - ceram1
@xi.lin 我不能告诉你它是如何实现的,但是通过查看ResolvableType API(也在答案中提供了链接)和javadoc,显然可以做到(请注意getGeneric()方法)。 - drew moore
3
实际上,在Java中有些情况下可以在运行时获取泛型类型。其中一个条件是,您需要具有泛型类的子类。例如:父类 public class Parent<T> {} 子类 public class Child extends Parent<Integer> { } 现在:Child c = new Child(); System.out.println(((ParameterizedType)c.getClass().getGenericSuperclass()).getActualTypeArguments()[0]); 将打印出 class java.lang.Integer - Michał Przybylak
1
这个能和包扫描的bean自动配置一起工作吗?我觉得不行。 - Preslav Rachev
显示剩余4条评论

15
如果你不想升级到Spring 4,你必须按名称进行自动装配,就像这样:
@Autowired
@Qualifier("stringItem")
private Item<String> strItem; // Injects the stringItem bean

@Autowired
@Qualifier("integerItem")
private Item<Integer> intItem; // Injects the integerItem bean

1
你的意思是直接使用具体的 StringItemIntegerItem 类吗? - user3374518

2

以下是我为回答这个问题而制作的解决方案:

最初的回答:


        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);

BTW in Spring 5+ - howard791006

1

Spring自动装配策略是在您的配置文件(application.xml)中定义的。

如果您没有定义,默认情况下是按类型进行自动注入,Spring使用JDK反射机制进行注入。

所以List?String?和List?Item?,类型都是相同的List.class,因此Spring不知道如何进行注入。

正如上面的回答所述,您应该使用@Qualifier指定要注入哪个bean。

我喜欢使用Spring配置文件来定义bean,而不是注释。

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


-1

Spring 4.0是使用@Qualifier注解的答案。希望能有所帮助。


-1

我相信这与泛型无关... 如果您正在注入两个相同类型的不同bean,则需要提供一个限定符来帮助Spring识别它们;

...其他地方

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

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

如果您有像这样的非泛型声明,那么您需要添加限定符来帮助Spring识别它们...
@Autowired
**@Qualifier("stringItem")**
private Item item1;

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

当然,在4及以上版本中,Spring通过解析器考虑了泛型类型,这非常酷。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接