【问题标题】:How to Autowire Bean of generic type <T> in Spring?如何在 Spring 中自动装配泛型类型 <T> 的 Bean?
【发布时间】:2022-01-24 08:02:45
【问题描述】:

我有一个 bean Item&lt;T&gt; 需要在 @Configuration 类中自动装配。

@Configuration
public class AppConfig {

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

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

}

但是当我尝试@Autowire Item&lt;String&gt; 时,我得到以下异常。

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

我应该如何在 Spring 中自动装配泛型类型 Item&lt;T&gt;

【问题讨论】:

    标签: java spring generics autowired spring-annotations


    【解决方案1】:

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

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

    希望对你有所帮助。

    【讨论】:

    • 这太棒了。我认为类型擦除使这种事情成为不可能。
    • 如何从 beanfactory 通过 resolvabletype 获取 bean?
    • @JohnStricler 我也认为这不能安全地完成。我还没有检查 ResolvableType 的实现,但这是否意味着我们现在可以安全地获得泛型类型?
    • 实际上在某些情况下,您可以在 Java 的运行时获取泛型类型。一个条件,你需要有泛型类的子类。示例:父类 public class Parent&lt;T&gt; {} 子类 public class Child extends Parent&lt;Integer&gt; { } 现在:Child c = new Child(); System.out.println(((ParameterizedType)c.getClass().getGenericSuperclass()).getActualTypeArguments()[0]); 将打印 class java.lang.Integer
    • 这是否适用于包扫描 bean 自动配置?我不这么认为。
    【解决方案2】:

    如果您不想升级到 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 类吗?
    【解决方案3】:

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

    
            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+
    【解决方案4】:

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

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

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

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

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

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

    【讨论】:

      【解决方案5】:

      Spring 4.0 是使用 @Qualifier 注释的答案。希望这会有所帮助

      【讨论】:

        【解决方案6】:

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2014-05-16
          • 2015-12-03
          • 2019-07-14
          • 2016-05-26
          • 2020-01-15
          • 2012-12-30
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多