随着泛型用的越来越多,获取泛型实际类型信息的需求也会出现,如果用原生API,需要很多步操作才能获取到泛型,比如:
ParameterizedType parameterizedType = (ParameterizedType) ABService.class.getGenericInterfaces()[0]; Type genericType = parameterizedType.getActualTypeArguments()[1];
Spring提供的ResolvableType API,提供了更加简单易用的泛型操作支持,如:
ResolvableType resolvableType1 = ResolvableType.forClass(ABService.class); resolvableType1.as(Service.class).getGeneric(1).resolve()
对于获取更复杂的泛型操作ResolvableType更加简单。
假设我们的API是:
public interface Service<N, M> { } @org.springframework.stereotype.Service public class ABService implements Service<A, B> { } @org.springframework.stereotype.Service public class CDService implements Service<C, D> { }
① 得到类型的泛型信息
ResolvableType resolvableType1 = ResolvableType.forClass(ABService.class);
通过如上API,可以得到类型的ResolvableType,如果类型被Spring AOP进行了CGLIB代理,请使用 ClassUtils.getUserClass(ABService.class)
得到原始类型。
可以通过如下得到泛型参数的第1个位置(从0开始)的类型信息
resolvableType1.getInterfaces()[0].getGeneric(1).resolve()
因为我们泛型信息放在 Service 上,所以需要resolvableType1.getInterfaces()[0]得到。通过getGeneric(泛型参数索引)得到某个位置的泛型;resolve()把实际泛型参数解析出来。
② 得到字段级别的泛型信息
假设我们的字段如下:
@Autowired private Service<A, B> abService; @Autowired private Service<C, D> cdService; private List<List<String>> list; private Map<String, Map<String, Integer>> map; private List<String>[] array;
通过如下API可以得到字段级别的ResolvableType
ResolvableType resolvableType2 = ResolvableType.forField(ReflectionUtils.findField(GenricInjectTest.class, "cdService"));
然后通过如下API得到Service
的第0个位置上的泛型实参类型,即C
resolvableType2.getGeneric(0).resolve()
比如 List> list;
是一种嵌套的泛型用例,我们可以通过如下操作获取String类型:
ResolvableType resolvableType3 = ResolvableType.forField(ReflectionUtils.findField(GenricInjectTest.class, "list")); resolvableType3.getGeneric(0).getGeneric(0).resolve();
更简单的写法:
resolvableType3.getGeneric(0, 0).resolve(); //List<List<String>> 即String
比如Map> map;
我们想得到Integer,可以使用:
ResolvableType resolvableType4 = ResolvableType.forField(ReflectionUtils.findField(GenricInjectTest.class, "map")); resolvableType4.getGeneric(1).getGeneric(1).resolve();
更简单的写法:
resolvableType4.getGeneric(1, 1).resolve()
③ 得到方法返回值的泛型信息
假设我们的方法如下:
private HashMap<String, List<String>> method() { return null; }
得到Map中的List中的String泛型实参:
ResolvableType resolvableType5 = ResolvableType.forMethodReturnType(ReflectionUtils.findMethod(GenricInjectTest.c lass, "method")); // 直接简化写法 resolvableType5.getGeneric(1, 0).resolve();
④ 得到构造器参数的泛型信息
假设我们的构造器如下:
public Const(List<List<String>> list, Map<String, Map<String, Integer>> map) { }
我们可以通过如下方式得到第2个参数( Map>
)中的Integer:
ResolvableType resolvableType6 = ResolvableType.forConstructorParameter(ClassUtils.getConstructorIfAvailable(Cons t.class, List.class, Map.class), 1); resolvableType6.getGeneric(1, 0).resolve();
⑤ 得到数组组件类型的泛型信息
如对于private List[] array
; 可以通过如下方式获取List的泛型实参String:
ResolvableType resolvableType7 = ResolvableType.forField(ReflectionUtils.findField(GenricInjectTest.class, "array")); resolvableType7.isArray();//判断是否是数组 resolvableType7.getComponentType().getGeneric(0).resolve();
⑥ 自定义泛型类型
ResolvableType resolvableType8 = ResolvableType.forClassWithGenerics(List.class, String.class); ResolvableType resolvableType9 = ResolvableType.forArrayComponent(resolvableType8); resolvableType9.getComponentType().getGeneric(0).resolve();
ResolvableType.forClassWithGenerics(List.class, String.class)相当于创建一个List类型;
ResolvableType.forArrayComponent(resolvableType8);:相当于创建一个List[]数组;
resolvableType9.getComponentType().getGeneric(0).resolve():得到相应的泛型信息;
⑦ 泛型等价比较
如下判断两个类型是否属于同一类:
resolvableType7.isAssignableFrom(resolvableType9)
如下创建一个List[]数组,与之前的List[]数组比较,将返回false。
ResolvableType resolvableType10 = ResolvableType.forClassWithGenerics(List.class, Integer.class); ResolvableType resolvableType11= ResolvableType.forArrayComponent(resolvableType10); resolvableType11.getComponentType().getGeneric(0).resolve(); resolvableType7.isAssignableFrom(resolvableType11);
从如上操作可以看出其泛型操作功能十分完善,尤其在嵌套的泛型信息获取上相当简洁。目前整个Spring4环境都使用这个API来操作泛型信息。
如之前说的泛型注入:Spring4新特性——泛型限定式依赖注入,通过在依赖注入时使用如下类实现
GenericTypeAwareAutowireCandidateResolver QualifierAnnotationAutowireCandidateResolver ContextAnnotationAutowireCandidateResolver
还有如Spring的核心BeanWrapperImpl,以及整个Spring/SpringWevMVC的泛型操作都是替换为这个API了:GenericCollectionTypeResolver和GenericTypeResolver都直接委托给ResolvableType这个API。
⑧ 泛型限定式依赖注入
以前我们常见的repository:
public abstract class BaseRepository<M extends Serializable> { public void save(M m) { System.out.println("=====repository save:" + m); } } @Repository public class UserRepository extends BaseRepository<User> { } @Repository public class OrganizationRepository extends BaseRepository<Organization> { }
对于Repository,我们一般是这样实现的:首先写一个模板父类,把通用的crud等代码放在BaseRepository;然后子类继承后,只需要添加额外的实现。
以前我们常见的service写法
public abstract class BaseService<M extends Serializable> { private BaseRepository<M> repository; public void setRepository(BaseRepository<M> repository) { this.repository = repository; } public void save(M m) { repository.save(m); } } @Service public class UserService extends BaseService<User> { @Autowired public void setUserRepository(UserRepository userRepository) { setRepository(userRepository); } } @Service public class OrganizationService extends BaseService<Organization> { @Autowired public void setOrganizationRepository(OrganizationRepository organizationRepository) { setRepository(organizationRepository); } }
可以看到,以前必须再写一个setter方法,然后指定注入的具体类型,然后进行注入。
泛型service的写法:
public abstract class BaseService<M extends Serializable> { @Autowired protected BaseRepository<M> repository; public void save(M m) { repository.save(m); } } @Service public class UserService extends BaseService<User> { } @Service public class OrganizationService extends BaseService<Organization> { }
大家可以看到,现在的写法非常简洁。支持泛型式依赖注入。这样对于那些基本的CRUD式代码,可以简化更多的代码。
如果大家用过Spring data jpa的话,以后注入的话也可以使用泛型限定式依赖注入 :
@Autowired private Repository<User> userRepository;
对于泛型依赖注入,最好使用setter注入,这样万一子类想变,比较容易切换。如果有多个实现时,子类可以使用@Qualifier
指定使用哪一个。