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


81

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

@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"

如何Item<T>在Spring中自动装配通用类型?

Answers:


144

一个简单的解决方案是升级到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);

在下面的链接中查看示例和教程。

希望这对您有所帮助。


10
这真太了不起了。我以为Type Erasure使得这种事情不可能实现。
约翰·斯特里克勒

5
如何从beanfactory中按可解析类型获取bean?
ceram1 2014年

@JohnStrickler我也认为这不能安全完成。我尚未检查ResolvableType的实现,但这是否意味着我们现在可以安全地获取泛型类型?
xi.lin 2015年

2
实际上,在某些情况下,您可以在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
米哈尔Przybylak

1
这可以与程序包扫描bean自动配置一起使用吗?我不这么认为。
Preslav Rachev

12

如果您不想升级到Spring 4,则必须按以下名称自动布线:

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

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

你的意思是与具体的工作StringItemIntegerItem直接类?
user3374518 2014年

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

春季5+的BTW
howard791006

1

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

如果您未定义,则默认为按类型,springject使用JDK反射机制。

所以列表?字符串?和List?Item ?,类型是相同的List.class,所以spring困惑了如何注入。

就像上述人员的回答一样,您应该指向@Qualifier告诉spring应该注入哪个bean。

我喜欢spring配置文件来定义bean而不是Annotation。

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


0

Spring 4.0是使用@Qualifier批注用法的答案。希望这可以帮助


0

我相信与泛型无关。如果要注入两个相同类型的不同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通过解析器考虑泛型,这很酷...

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.