Spring Framework 4.0 和 Java 泛型

工程 | Phil Webb | 2013年12月03日 | ...

随着 Spring Framework 4.0 的最新 RC2 发布;以及 GA 将在年底前发布,这里简单介绍一下一些应该可以改善您使用 Java 泛型类型的工作的变化。

Spring 在很长一段时间内都拥有相当不错的 Java 泛型支持。例如,使用 3.2 版本,您可以轻松地使用 @Autowired 注解将特定类型的所有 bean 注入到泛型 List

@Autowired
private List<MyType> beans; 
// all beans that extends MyType will be injected

Spring 的转换服务、绑定系统和 Web MVC 框架都是“泛型感知的”,并且还有方便的 GenericCollectionTypeResolverGenericTypeResolver 实用程序类,您可以在自己的代码中使用它们。

不幸的是,早期版本的 Spring 的一个问题是您不能将泛型用作限定符的形式。例如,您可能有这样的配置

@Configuration
public class MyConfiguration {

	@Bean
	public Store<String> stringStore() {
		return new StringStore();
	}

	@Bean
	public Store<Integer> integerStore() {
		return new IntegerStore();
	}

}

如果您尝试使用 Spring 3.2 的 @Autowire Store<String>,您将收到以下异常

"未定义 [Store] 类型的符合条件的 bean:预期单个匹配 bean,但找到 2 个:stringStore, integerStore"

在这里,Spring 告诉您它找到了 Store 的两个实现,并且无法区分它们。

一种潜在的解决方法是直接使用具体的 StringStoreIntegerStore 类,而不是使用泛型 Store 接口。但是,如果您的实现类是“私有的”,或者如果它们是使用代理动态创建的(就像在 Spring Data Repository 的情况下),这可能是不可能的。

另一种选择是在 @Bean 方法和 @Autowire 字段上都使用 @Qualifier 注解,以便为 Spring 提供关于您实际想要的 Bean 的提示。

幸运的是,从 Spring Framework 4.0 开始,不再需要这种解决方法。Spring 4.0 将自动将泛型视为 @Qualifier 的一种形式。例如

@Autowired
private Store<String> s1; // Injects the stringStore bean

@Autowired
private Store<Integer> s2; // Injects the integerStore bean

您甚至可以在注入到列表时使用嵌套泛型

// Inject all Store beans as long as they have an <Integer> generic
// Store<String> beans will not appear in this list
@Autowired
private List<Store<Integer>> s;  

在幕后,新的 ResolvableType 类提供了实际使用泛型类型的逻辑。您可以使用它自己来轻松导航和解析类型信息。ResolvableType 上的大多数方法本身将返回一个 ResolvableType,例如

// Assuming 'field' refers to 's' above
ResolvableType t1 = ResolvableType.forField(field); // List<Store<Integer>> 
ResolvableType t2 = t1.getGeneric(); // Store<Integer>
ResolvableType t3 = t2.getGeneric(); // Integer
Class<?> c = t3.resolve(); // Integer.class

// or more succinctly
Class<?> c = ResolvableType.forField(field).resolveGeneric(0, 0);

这是另一个例子,展示了如果您获得超类型或实现的接口,泛型信息是如何保留的。

// Assuming 'm' refers to a method that returns MultiValueMap<Integer, String> 
ResolvableType t1 = ResolvableType.forMethodReturnType(m); // MultiValueMap<Integer, String> 
ResolvableType t2 = t1.asMap(); // Map<Integer, List<String>>
ResolvableType t3 = t2.getGeneric(1); // List<String>
ResolvableType t4 = t3.getGeneric(); // String
Class<?> c = t4.resolve(); // String.class

// or more succinctly
Class<?> c = ResolvableType.forMethodReturnType(m).asMap().resolveGeneric(1, 0);

Spring 4.0 将于 12 月 12 日发布,但如果您想立即试用泛型支持,您可以尝试最新的 4.0 发布候选版本,可在我们的 里程碑存储库 中找到。

获取 Spring 新闻通讯

与 Spring 新闻通讯保持联系

订阅

更进一步

VMware 提供培训和认证,以加速您的进步。

了解更多

获得支持

Tanzu Spring 在一个简单的订阅中提供对 OpenJDK™、Spring 和 Apache Tomcat® 的支持和二进制文件。

了解更多

即将举行的活动

查看 Spring 社区中所有即将举行的活动。

查看全部