Skip to content

使用泛型作为自动装配限定符 (Using Generics as Autowiring Qualifiers)

除了 @Qualifier 注解之外,你还可以使用 Java 泛型类型作为一种隐式的限定方式。例如,假设你有以下配置:

java
@Configuration
public class MyConfiguration {

	@Bean
	public StringStore stringStore() {
		return new StringStore();
	}

	@Bean
	public IntegerStore integerStore() {
		return new IntegerStore();
	}
}
kotlin
@Configuration
class MyConfiguration {

	@Bean
	fun stringStore() = StringStore()

	@Bean
	fun integerStore() = IntegerStore()
}

假设上述 Bean 实现了泛型接口(即 Store<String>Store<Integer>),你可以对 Store 接口进行 @Autowired 注入,此时泛型将作为限定符使用,如下例所示:

java
@Autowired
private Store<String> s1; // 使用 <String> 限定,注入 stringStore bean

@Autowired
private Store<Integer> s2; // 使用 <Integer> 限定,注入 integerStore bean
kotlin
@Autowired
private lateinit var s1: Store<String> // 使用 <String> 限定,注入 stringStore bean

@Autowired
private lateinit var s2: Store<Integer> // 使用 <Integer> 限定,注入 integerStore bean

泛型限定符也适用于自动装配列表(List)、Map 实例和数组。以下示例自动装配了一个泛型 List

java
// 注入所有具有 <Integer> 泛型的 Store Bean
// Store<String> Bean 不会出现在此列表中
@Autowired
private List<Store<Integer>> s;
kotlin
// 注入所有具有 <Integer> 泛型的 Store Bean
// Store<String> Bean 不会出现在此列表中
@Autowired
private lateinit var s: List<Store<Integer>>

补充教学 —— 泛型注入的“降维打击”

在 Spring 的早期版本中,泛型在注入时会被擦除(Type Erasure),导致如果你有多个不同泛型的同名类,Spring 会因为类型冲突而报错。但从 Spring 4.0 开始,Spring 增强了对泛型的感知能力。

1. 为什么它是“隐式限定符”? 在没有泛型支持前,如果你有两个 Store 实例,你必须配合 @Qualifier("stringStore") 来区分。 现在,你只需要写成 Store<String>,Spring 内部会提取出泛型参数,将其作为一个过滤条件。这比写字符串限定符更加类型安全,且支持 IDE 的重构和代码提示。

2. 典型的应用场景:通用 CRUD 模式 这是泛型注入最强大的地方。你可以定义一个通用的 BaseService<T>

java
public class BaseService<T> {
    @Autowired
    protected BaseMapper<T> mapper; // 自动根据子类的泛型注入对应的 Mapper
}

@Service
public class UserService extends BaseService<User> {
    // 这里的 mapper 会自动被注入为 BaseMapper<User>
}

这种模式可以极大地减少重复的注入代码。

3. 它是如何做到的? Spring 并不是真的绕过了 Java 的类型擦除,而是通过反射读取了类定义中的“签名”(Signature)元数据。这就是为什么它要求 Bean 的返回类型必须明确声明泛型信息。

  • 注意:如果你在 @Bean 方法里返回的是原始类型(Raw Type),泛型注入就会失效。

4. 集合注入的精准过滤 文档中提到的 List<Store<Integer>> 非常有用。设想你有一个插件系统,每个插件处理不同类型的数据:

java
@Autowired
private List<Processor<Image>> imageProcessors; // 只拿处理图片的插件

Spring 会过滤掉 Processor<Video> 等其他不相关的插件,让你的业务代码非常干净。

Based on Spring Framework.