ChatGPT解决这个技术问题 Extra ChatGPT

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

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

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


S
Shishir Kumar

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

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

Spring Framework 4.0 和 Java 泛型

泛型类型的弹簧和自动装配

希望这对您有所帮助。


这真太了不起了。我认为类型擦除使这种事情变得不可能。
如何从 beanfactory 通过 resolvabletype 获取 bean?
@JohnStricler 我也认为这不能安全地完成。我还没有检查 ResolvableType 的实现,但这是否意味着我们现在可以安全地获得泛型类型?
实际上,在某些情况下,您可以在 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
这适用于包扫描 bean 自动配置吗?我不这么认为。
S
Shishir Kumar

如果您不想升级到 Spring 4,则必须按名称自动装配,如下所示:

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

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

您的意思是使用具体的 StringItem & IntegerItem 直接上课?
a
applecrusher

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


        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+
z
zg_spring

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

如果不定义,默认是按类型,spring注入使用JDK反射机制。

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

和上面的人的反应一样,你应该点@Qualifier来告诉spring应该注入哪个bean。

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

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


s
shikjohari

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


R
Ramkumar S

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