Spring Framework 4.0 和 Java泛型

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

随着最近发布的 Spring Framework 4.0 RC2 版本,以及预计在年底发布的 GA 版本,这里提供了一些更改的简要介绍,如果您使用 Java 泛型类型,这些更改应该可以改善您的工作体验。

Spring 一直以来都对 Java 泛型提供了良好的支持。例如,在 3.2 版本中,您可以轻松地将特定类型的全部 Bean 注入到泛型 List 中,只需使用 @Autowired 注解即可。

@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>,则会收到以下异常

"No qualifying bean of type [Store] is defined: expected single matching bean but found 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 社区中所有即将举行的活动。

查看全部