一直对spring中的泛型注入好奇,比如:
abstract public class BaseService<T, ID extends Serializable, R extends BaseRepository<T, ID>> {
@Autowired
protected R baseRepository;
}
spring具体实现还未知。先猜一下其实现。
由于Java泛型的引入,各种场景(虚拟机解析、反射等)下的方法调用都可能对原有的基础产生影响和新的需求,如在泛型类中如何获取传入的参数化类型 等。所以JCP组织对虚拟机规范做出了相应的修改,引入了诸如Signature、LocalVariableTypeTable等新的属性用于解决伴随 泛型而来的参数类型的识别问题,Signature是其中最重要的一项属性,它的作用就是存储一个方法在字节码层面的特征签名,这个属性中保存的参数类型并不是原生类型,而是包括了参数化类型的信息。修改后的虚拟机规范要求所有能识别49.0以上版本的Class文件的虚拟机都要能正确地识别Signature参数。
(注3:在《Java虚拟机规范第二版》 (JDK 1.5修改后的版本)的“§4.4.4Signatures”章节及《Java语言规范第三版》的“§8.4.2 MethodSignature”章节中分别都定义了字节码层面的方法特征签名,以及Java代码层面的方法特征签名,特征签名最重要的任务就是作为方法独一无二不可重复的ID,在Java代码中的方法特征签名只包括了方法名称、参数顺序及参数类型,而在字节码中的特征签名还包括方法返回值及受查异常表,本书中如果指的是字节码层面的方法签名,笔者会加入限定语进行说明,也请读者根据上下文语境注意区分。)
从上面的例子可以看到擦除法对实际编码带来的影响,由于List<String>和List<Integer>擦除后是同一 个类型,我们只能添加两个并不需要实际使用到的返回值才能完成重载,这是一种毫无优雅和美感可言的解决方案。同时,从Signature属性的出现我们还 可以得出结论,擦除法所谓的擦除,仅仅是对方法的Code属性中的字节码进行擦除,实际上元数据中还是保留了泛型信息,这也是我们能通过反射手段取得参数化类型的根本依据。
可知尽管到了字节码中有泛型擦除,但还是保存着泛型信息的。看了网上的实现,似乎,只能拿到父类泛型的信息。
总体思路:
在子类层面指定了父类的实际类型,所以在通过son.getClass().getGenericSuperclass();在子类层面拿到实际父类的类型,
在通过方法son.getClass().getSuperclass().getTypeParameters();在父类层面拿到父类的泛型,再将他们对应。
父类:
class Foo<T, E> {
T t;
public Class<T> getTClass() {
Class<T> tClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
return tClass;
}
}
拿到泛型信息的代码片段:
Foo<String, Date> foo = new Foo<String, Date>() {};
// 在类的外部这样获取
Type superclass = foo.getClass().getGenericSuperclass();
if (superclass instanceof ParameterizedType) {
Type[] actualTypeArguments = ((ParameterizedType) superclass).getActualTypeArguments();
for (Type type : actualTypeArguments) {
System.out.println(type.getTypeName());
}
}
运行结果:
java.lang.String
java.util.Date
Class.getGenericSuperclass()是1.5之后的api。明显感到1.5之后很多方法和泛型信息有关,比如
Class.getAnnotatedSuperclass();Method.getAnnotatedReturnType();
这里虽然拿到了泛型的实际类型,但这么和泛型T,E对应起来呢?如果不对应起来应该不能向上面那样注入的。拿到T,E类型应该找得到其他api。
Class<?> superclass = foo.getClass().getSuperclass();
TypeVariable<? extends Class<?>>[] typeParameters = superclass.getTypeParameters();
for(TypeVariable typeVariable:typeParameters){
System.out.println(typeVariable.getName());
System.out.println(typeVariable.getTypeName());
}
运行结果就是T E。这里就把实际类型和泛型对应起来了。拿这么拿到字段的泛型(T)呢?
field.getGenericType(),返回的就是T。一切都串起来了。但spring的泛型注入还有待看源码。
参考
Java获取泛型T的类型 T.class_hellozhxy的博客-优快云博客
写了个自定义的泛型注入。
package com.example.demo76;
import org.springframework.beans.factory.annotation.Autowired;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
public class Demo2 {
HashMap<String, Object> beans = new HashMap<>();
HashMap<String, String> map = new HashMap<>();
//根据实际情况获得类的标识,避免重复
String className = "";
{
beans.put("java.lang.String", "注入的字符串");
beans.put("java.util.Date", new Date());
}
public static void main(String[] args) throws Exception {
new Demo2().run();
}
public void run() throws IllegalAccessException {
Fu<String, Date> zi = new Fu<String, Date>() {
};
//分别获取父类实际类型和泛型类型信息
Class<?> fuclass = zi.getClass().getSuperclass();
Type futype = zi.getClass().getGenericSuperclass();
TypeVariable<? extends Class<?>>[] typeParameters = fuclass.getTypeParameters();
Type[] actualTypeArguments = ((ParameterizedType) futype).getActualTypeArguments();
//把对应的类型写入map
int len = typeParameters.length;
for (int i = 0; i < len; i++) {
map.put(className + "." + typeParameters[i].getName(), actualTypeArguments[i].getTypeName());
}
Field[] fields = fuclass.getDeclaredFields();
for (Field field : fields) {
Annotation[] annotations = field.getDeclaredAnnotations();
for (Annotation annotation : annotations) {
if (annotation.annotationType().equals(MyAutowired.class)) {
//代表注入的字段
String fieldType = className + "." + field.getGenericType().getTypeName();
field.setAccessible(true);
String actualType = map.get(fieldType);
System.out.println(actualType);
field.set(zi, beans.get(actualType));
break;
}
}
}
System.out.println(zi);
}
}
class Fu<T, E> {
@MyAutowired
private T t;
@MyAutowired
private E e;
private T t2;
@Override
public String toString() {
return t.toString() + "," + e.toString();
}
}
package com.example.demo76;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAutowired {
}
================================2020.12.30===============================
Class是Type的实现类,所以能强转。可以简化上面的写法
Class clazz = this.getClass();
// 获取该类直接父类的类型
Type type = clazz.getGenericSuperclass();
// 通过ParameterizedType获取 此类型实际类型参数的 Type对象的数组
ParameterizedType pType = (ParameterizedType) type;
Type[] types = pType.getActualTypeArguments();
// Type类型所有已知实现类: Class,所以可以强制转换
this.cClass = (Class) types[0];
System.out.println(this.cClass);
System.out.println(types[0].getClass());