• 注解与反射


    注解与反射

    注解的作用或意义是什么?

    注解本身没有任何意义,单独的注解就是一种注释。它需要结合如 反射、插桩等技术才有意义

    Java注解(Annotation)又称Java标注,是元数据的一种形式,提供有关程序但不属于程序本身的数据注解对他们注解的代码没有直接影响。

    注解初探

    注解定义:
    @Target/@Retention元注解:
    元注解:注解上的注解 指示当前声明的注解可以作用那些位置
    
    @Target(ElementType.TYPE) 如果没有指定@Target的话 默认全部(TYPE->类 属性 METHOD->方法 构造方法 FIELD->属性 PARAMETER->参数)可以指示多个 ElementType.FIELD,ElementType.PARAMETER
    
    @Retention:保留级别 保留这个注解到什么时候   
    @Retention(RetentionPolicy.SOURCE)(SOURCE->仅保留到源码阶段(javac编译成class之后就会被抹除),CLASS->保留在class文件中 但是在JVM加载class文件时会被忽略,RUNTIME->运行时 由JVM保留运行结果让我们获取这个注解 反射技术)
    SOURCE<CLASS<RUNTIMEclass包含了SOURCE,RUNTIME包含了SOURCECLASS
    public @interface Lance{
        //元素 可以指定基本数据类型 注解类型 引用类型
        String value() default "";
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    @Target

    注解标记另一个注解,以限制可以应用注解的 Java 元素类型。目标注解指定以下元素类型之一作为其值:

    • ElementType.ANNOTATION_TYPE 可以应用于注解类型。
    • ElementType.CONSTRUCTOR 可以应用于构造函数。
    • ElementType.FIELD 可以应用于字段或属性。
    • ElementType.LOCAL_VARIABLE 可以应用于局部变量。
    • ElementType.METHOD 可以应用于方法级注解。
    • ElementType.PACKAGE 可以应用于包声明。
    • ElementType.PARAMETER 可以应用于方法的参数。
    • ElementType.TYPE 可以应用于类的任何元素。
    注解使用:
    @Lance("")
    public class MainActivity extends AppcompatActivity{
        
    } 
    
    • 1
    • 2
    • 3
    • 4

    APT技术

    APT(Annotation Processor Tools) 注解处理器:

    源码级别,在编译期RetentionPolicy.SOURCE能够获取注解与注解声明的包括类中的所有成员信息,一般用于生成额外的辅助类。

    1.创建一个Java Module

    JVM虚拟机不认识java文件,由javac编译成.class文件

    .java->javac->.class

    javac解析要编译的java类 就要采集到所有的注解信息 然后将所有注解信息包装成节点->Element->由javac调起注解处理器

    由javac调用注解处理器 不用我们手动调用 只需要给javac指明要处理的注解处理程序

    直接收特定注解
    @SupportedAnnotationTypes("com.enjoy.annotation.Lance")
    AbstractProcessor:JDK给我们提供的 LanceProcessor就是一个注解处理程序 javac会调用里面的process方法
    public class LanceProcessor extends AbstractProcessor{
        @Override
        public boolean process(Set<? extends TypeElement> set,RoundEnvironment roundEnvironment){
            //AbstractProcessor内部属性 processingEnv
            Messager messager = processingEnv.getMessager();
            messager.printMessage(Dialogstic.Kind.NOTE,"========");
            //会执行两次 可能与返回值有关 
            return false;
        }
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    2.在resources里进行注册

    名字是固定的 必须是下面

    新建Direcory->resources/META-INF.services/javax.annotation.processing.Processor

    注解处理程序的全类名
    com.enjoy.compiler.LanceProcessor
    
    • 1
    • 2

    字节码增强

    在字节码中写代码

    .class->格式 数据按照特定的方式记录与排列

    .class->IO->byte[]->不能乱改

    反编译后的class文件

    public class MainActivity extends AppcompatActivity{
        public MainActivity(){
            
        }
        
        @InjectTime
        protected void onCreate(Bundle savedInstanceState){
            super.onCreate(savedInstanceState);
            this.setContentView(2131296285);
            this.a();
        }
        
        @InjectTime
        void a(){
            try{
                Thread.sleep(2000L);
            }catch(InterruptedException var2){
                var2.prointStackTrace();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    想要在@InjectTime注解的方法上第一行和最后一行都插入当前系统时间并算出两者相减的时间

    注解的意义是 你是都要对这个方法进行增强

    反射

    运行时级别,在程序运行期间,通过反射技术动态获取注解与其元素,从而完成不同的逻辑判定。

    反射就是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和
    方法;对于任意一个对象,都能够调用它的任意方法和属性;并且能改变它的属性。是Java被视为动态语言的关键。

    Java反射机制主要提供了以下功能:

    • 在运行时构造任意一个类的对象
    • 在运行时获取或者修改任意一个类所具有的成员变量和方法
    • 在运行时调用任意一个对象的方法(属性);
    Class

    反射始于Class,Class是一个类,封装了当前对象所对应的类的信息。一个类中有属性,方法,构造器等,比如说
    有一个Person类,一个Order类,一个Book类,这些都是不同的类,现在需要一个类,用来描述类,这就是
    Class,它应该有类名,属性,方法,构造器等。Class是用来描述类的类

    获得Class对象

    获取class对象的三种方式

    1. 通过类名获取 类名.class
    2. 通过对象获取 对象名.getClass()
    3. 通过全类名获取 Class.forName(全类名) / classLoader.loadClass(全类名)
    • 使用Class类的 forName 静态方法
    public static Class<?> forName(String className)
    
    • 1
    • 直接获取某一个对象的class
    Class<?> kclass = int.class;
    Class<?> classInt = Integet.TYPE
    • 1
    • 2
    • 调用某个对象的getClass方法
    StringBuilder str = new StringBuilder("123");
    Class<?> kclass = str.getClass();
    
    • 1
    • 2
    判断是 否为某个类的实例

    一般的 我们用instanceof关键字来判断是否为某个类的实例,同时我们也可以借助反射中Class对象的 isInstance()方法来判断是否为某个类的实例 它是一个native方法:

    public native boolean isInstance(Object obj);
    
    • 1

    判断是否为某个类的类型

    public boolean isAssignableForm(Class<?> cls)
    
    • 1
    创建实例

    通过反射来生成对象主要有两种方式

    • 使用Class对象的newInstance()方法来创建Class对象对应类的实例
    Class<?> c = String.class;
    Object str = c.newInstance();
    
    • 1
    • 2
    • 先通过Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()来创建实例。这种方法可以用指定的构造器构造类的实例。
    //获取String所对应的Class对象
    Class<?> c= String.class;
    //获取String类带一个String参数的构造器
    Constructor constructor = c.getConstructor(String.class);
    //根据构造器创建实例
    Object obj = constructor.newInstance("2333");
    System.out.println(obj);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    获取构造器信息

    得到构造器的方法

    Constructor getConstructor(Class[] params) -- 获得使用特殊的参数类型的public构造函数(包括父类)
    Constructor[] getConstructors() -- 获得类的所有公共构造函数
    Constructor getDeclaredConstructor(Class[] params) -- 获得使用特定参数类型的构造函数(包括私有)
    Constructor[] getDeclaredConstructors() -- 获得类的所有构造函数(与接入级别无关)                                                 
    
    • 1
    • 2
    • 3
    • 4

    获取类构造器的用法与上述获取方法的用法类似。主要是通过Class类的getConstructor方法得到Constructor类的
    一个实例,而Constructor类有一个newInstance方法可以创建一个对象实例:

    public T newInstance(Object ... initargs)
    
    • 1
    获取类的成员变量(字段)信息

    获取字段信息的方法

    Field getField(String name)--获取命名的公共字段+父类成员 (不包括private只能是public)
    Field[] getFields()--获取类的公共字段
    Field getDeclaredField(String name)--获取类声明的命名的字段
    Field[] getDeclaredFields()--获取类声明的所有字段  不包括父类 所有作用域 
    
    • 1
    • 2
    • 3
    • 4
    调用方法

    获取方法信息的方法

    Method getMethod(String name,Class[] params)--使用特定的参数类型 获得命名的公共方法
    Method[] getMethods() --获得类的所有公共方法
    Method getDeclaredMethod(String name,Class[] params)--使用特定的参数类声明的命名的方法 Method[] getDeclaredMethods()--获得类声明的所有方法  
    
    • 1
    • 2
    • 3

    当我们从类中获取了一个方法后,我们可以用invoke方法来调用这个 invoke方法原型为

    public Object invoke(Obejct obj,Object ... args)
    
    • 1
    利用反射创建数组

    数组在java里是比较特殊的一种类型。它可以赋值给一个Object Reference其中的Array类为java.lang.reflect.Array类 我们通过Array.newInstance()创建数组对象 它的原型是

    public static Object newInstance(Class<?> componentType,int length);
    
    • 1
    反射获取泛型真实类型

    当我们对一个泛型类型进行反射时,需要得到泛型中的真实数据类型,来完成如json反序列化的操作,此时需要通过 type体系来完成,type接口包含了一个实现类(class对象)和四个实现接口,

    public final class Class implements Type{

    }

    他们分别是:

    • TypeVariable 泛型类型变量,可以泛型上下限等信息
    • ParamterizedType 具体泛型类型 可以获得元数据中泛型签名(泛型真实类型)
    • GenericArrayType 当需要描述的类型是 泛型类的数组 如List[],map[] 此接口会作为type实现
    • WildcardType 通配符泛型,获得上下限信息

    Json反序列化操作

    public class Deserialize{
        
        static class Response<T>{
            T data;
            int code;
            String message;
            
            public Response(T data,int code,String message){
                this.data = data;
                this.code = code;
                this.message = message;
            }
        }
        
        static class Data{
            String result;
            
            public Data(String result){
                this.result = result;
            }
        }
    }
    
    //test
    public static void main(String[] args){
        Response<Data> dataResponse = new Response(new Data("数据"),1,"成功");
        
        Gson gson = new Gson();
        String json = gson.toJson(dataResponse);
        
        //反序列化
        Type type = new TypeToken<Response<Data>>(){}.getType();
        Response<Data> response = gson.fromJson(json,type);
        System.out.println(response.data.getClass());
        
        //自定义一个TypeToken 为什么人家写protecte 不同的包下protected必须加{}  
        //有花括号: 代表匿名内部类 没有花括号就是对象 对象没有记录Response东西 可在字节码中查看 
        //加{}是 class 匿名内部类 编译后会多一个 类名$1.class 匿名内部类保存了泛型的具体类型
        Type type = TypeRefrence<Response<Data>>(){}.getType();
        
    }
    
    static class TypeRefrence<T>{
        Type type;
        
        public TypeRefrence(){
            //获得泛型类型 
            Type genericSuperclass = getClass().getGenericSuperclass();
            ParameterizedType type = (ParamterizedType) genericSuperclass;
            //可以有多个泛型 
            Type[] actualTypeArguments = type.getActualTypeArguments();
            type = actualTypeArguments[0];
        }
        
        public Type getType(){
            return type;
        }
    }
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    >>>>>>>>>>TypeToken源码解析
    public class TypeToken<T>{
        final Class<? super T> rawType;
        final Type type;
        final int hashCode;
        
        protected TypeToken(){
            this.type = getSuperclassTypeParameter(this.getClass());
            this.rawType = Types.getRawType(this.type);
            this.hashCode = this.type.hashCode();
        }
        
        static Type getSuperclassTypeParameter(Class<?> subclass){
            //获取当前对象的泛型类型
            Type superclass = subclass.getGenericSuperclass();
            if(superclass instanceof Class){
                throw new RuntimeException("Missing type parameter");
            }else{
                //具体泛型类型
                ParameterizedType parameterized = (ParameterizedType) superclass;
                return Types.canonicalize(parameterized.getActualTypeArguments()[0]);
            }
        }
        
        
    }   
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    demo
    public static void injectView(Activity activity){
        //当前activity对应的class对象
        Class<? extends Activity> cls = activity.getClass();
        cls.getSuperclass();//父类的class对象 
        
        //获得此类所有的成员
        Field[] declaredFields = cls.getDeclaredFields();
        for(Field declaredField : declaredFields){
            if(declaredField.isAnnotationPresent(InjectView.class)){//是否被该注解声明 
               InjectView injectView = declaredField.getAnnotation(InjectView.class);
               //获得注解中设置的id
                int id = injectView.value();
                View view = activity.findViewById(id);//拿到当前view 
                //反射设置 属性的值
                declaredField.setAccessible(true);//允许访问 如果为private 设置访问权限 
                try{
                    //反射赋值 declaredField->TextView 吧view的值赋给给MainActivity中的Tv
                   declaredField.set(activity,view);
                    //设置activity中的TextView吧值设为view 
                }catch(IllegalAccessException e){
                    e.printStackTrace();
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    打标签

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.Field)
    public @interface InjectView{
        @IdRes int value();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    使用

    @InjectView(R.id.textView)
    TextView tv;
    
    • 1
    • 2
    课后作业

    getIntent()拿到上一个页面传递过来的数据

    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Autowired{
        String value() default "";
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    public static void injectAutowired(Activity activity){
       Class<? extends Activity> cls =  activity.getClass();
       //获得数据
       Intent intent = activity.getIntent();
       Bundle extras = intent.getExtras();
       if(extras == null){
           return;
       } 
       
        //获得此类所有成员变量
        Field[] declaredFields = cls.getDeclaredFields();
        for(Field field:declaredFields){
            if(field.isAnnotationPresent(Autowired.class)){
                Autowired autowired = field.getAnnotation(Autowired.class);
                //获得key
                String key = TextUtils.isEmpty(autowired.value())?field.getName():autowired.value();
                if(extras.containsKey(key)){
                    Object obj = extras.get(key);
                    //Parcelable数组类型不能直接设置 其他都可以
                    //获得单个元素类型
                    Class<?> componentType = field.getType().getComponentType();
                    //当前属性是数组并且是Parcelable(子类)数组
                    if(field.getType().isArray() && Paracelable.class.isAssignableFrom(componentType)){
                        Object[] objs = (Object[]) obj;
                        //创建对应类型的数组并由objs拷贝
                        Object[] objects = Arrays.copyOf(objs,objs.length,(Class<? extends Object[]>) field.getType());
                        obj = objects;
                    }
                    
                    field.setAccessible(true);
                    try{
                       field.set(activity,obj);
                    }catch(IllegaAccessException e){
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
  • 相关阅读:
    AI赋能目标跟踪 智算加速“视”不可挡
    VSCode不同窗口设置不同颜色
    基因对疾病的影响规律--读论文
    es 读流程源码解析
    Java(十八)---单链表
    【方向盘】升级到IDEA 2022.1版本后,我把Maven Helper卸载了
    基于JSP技术的人事管理系统
    pytest学习和使用8-fixture如何实现teardown功能?(yield的使用)
    长春理工大学计算机考研资料汇总
    猿创征文|瑞吉外卖——移动端_订单明细
  • 原文地址:https://blog.csdn.net/yuhang01/article/details/134431640