如何在 Spring 中自动装配泛型类型 <T> 的 Bean?
Posted
技术标签:
【中文标题】如何在 Spring 中自动装配泛型类型 <T> 的 Bean?【英文标题】:How to Autowire Bean of generic type <T> in Spring? 【发布时间】:2022-01-24 08:02:45 【问题描述】:我有一个 bean 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>
?
【问题讨论】:
【参考方案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);
查看以下链接中的示例和教程。
Spring Framework 4.0 and Java Generics Spring and Autowiring of Generic Types希望对你有所帮助。
【讨论】:
这太棒了。我认为类型擦除使这种事情成为不可能。 如何从 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 自动配置?我不这么认为。【参考方案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 会通过解析器考虑泛型类型,这非常酷......
【讨论】:
以上是关于如何在 Spring 中自动装配泛型类型 <T> 的 Bean?的主要内容,如果未能解决你的问题,请参考以下文章
Spring框架bean的配置:基于注解的配置,Autowired 自动装配 Bean,泛型依赖注入
如何在 Spring Boot 中实现通用 JPA 存储库 - 它可以自动装配到任何实体/类类型的 Spring 服务中