java泛型记录,自定义版本泛型注入

一直对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的博客-CSDN博客

写了个自定义的泛型注入。

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());

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值