Java注解知识梳理—反射(运行时注解使用)

Java注解知识梳理—反射(运行时注解使用)

前面我们梳理了注解的知识以及注解处理器的使用,现在我们梳理一下运行时注解的使用。

一个运行时的注解,注解信息会被加载进JVM中,这个时候我们是没办法再根据具体运行的情况写代码进程处理的,所以只能依据代码可能运行的逻辑来获取相关注解元素信息,然后做一些处理,这需要用到java的反射,

反射是什么以及有什么用途

反射(Reflection)是Java的一个特性,通过反射可以获取Java任意对象的信息,获取了对象的信息之后可以做一下用途:

  • 判断该对象所属的类
  • 实例化该对象
  • 获取该对象下面的所有成员变量和方法
  • 调用该对象下的变量和方法

简而言之,就是通过反射,我们可以获取编译好的每一个类及其信息,然后实例化对象,调用方法。

我们在开发过程中,IDE自动的提示以及大型框架比如Spring等,都是利用反射的原理。Android中利用反射比较有名的框架就是EventBus框架。我们就模拟写一个简单的EventBus来具体理解反射的作用。

EventBus是做消息分发用的,作用就是我们在一个类中注解一个对象的监听方法之后,当有该对象的消息发出时,注册的地方可以接收到该消息。EventBus的用法如下:

  1. 定义发生改变需要通知的对象:

    public static class MessageEvent { /* Additional fields if needed */ }
    
  2. 定义一个注解,并标明是该消息通知的线程是否主线程

    @Subscribe(threadMode = ThreadMode.MAIN)  
    public void onMessageEvent(MessageEvent event) {/* Do something */};
    

    注册、取消注册需要接受消息变化的类

     @Override
     public void onStart() {
         super.onStart();
         EventBus.getDefault().register(this);
     }
    
     @Override
     public void onStop() {
         super.onStop();
         EventBus.getDefault().unregister(this);
     }
    
  3. 发送消息

     EventBus.getDefault().post(new MessageEvent());
    

跟着我左手右手一步一步写代码

代码中起的名字可以随意,这里和EventBus保持一致。

创建ThreadMode的枚举

根据@Subscribe(threadMode = ThreadMode.MAIN)这句可以分析出,我们需要给该注解一个枚举类型,来区分标记的方法是用在主线程还是子线程:

public enum ThreadMode {
    MAIN,//主线程
    OTHER//子线程
}

创建注解Subscribe

该注解有以下特性:

  • 运行时注解
  • 有一个属性叫做threadMode,参数为ThreadMode的枚举
  • 该注解标注在方法上
@Target(ElementType.METHOD)//标注的类型在方法上
@Retention(RetentionPolicy.RUNTIME)//运行时注解
public @interface Subscribe {
    //一个线程属性,默认是在主线程运行
    ThreadMode threadMode() default ThreadMode.Main;
}

创建一个需要通知的对象MessageEvent

/**
 * 通知的对象
 * AnnotationApplication
 * Created by anonyper on 2019/6/6.
 */
public class MessageEvent {
    /**
     * 一个消息变量
     */
    String message;

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

   

    public MessageEvent(String message) {
        this.message = message;
    }
}

创建EventBus类相关代码

EventBus类的功能主要有以下两点:

管理注册的对象

根据要通知的消息bean,获取关注了该消息bean的对象,然后调用该对象下的方法发出通知

根据功能分析:

1、EventBus全局通用,需要是一个单例模式,项目中仅存在唯一一个对象。

2、发送消息通知时,找出接受该消息变化的方法,然后执行。

3、同时会有多个对象可能需要监听同一个消息的变化(MainActivity和SetActivity都监听短信消息)

4、一个对象可能需要监听多个消息的变化(一个Activity中监听网络消息、短信消息)

5、所以注册的时候,需要找到该对象下所有标注了@Subscribe注解的方法

6、所以找到标注了@Subscribe注解的方法需要保存起来,需要有一个Bean对象承载改方法的信息。

7、方法的执行通过反射来执行,method.invoke(Object obj,Object... obj2),第一个是该方法所属的对象,第二个是方法的参数。

8、承载方法信息的Bean我们定义为MethodBean,具体内容如下:

/**
 * 方法bean
 * AnnotationApplication
 * Created by anonyper on 2019/6/6.
 */
public class MethodBean {
    /**
     * 该方法对应的类对象
     */
    Object object;
    /**
     * 方法对象
     */
    Method method;
    /**
     * 运行线程指定
     */
    ThreadMode threadMode;
    /**
     * 参数类型
     */
    Class<?> eventType;

    public Object getObject() {
        return object;
    }

    public void setObject(Object object) {
        this.object = object;
    }

    public Method getMethod() {
        return method;
    }

    public void setMethod(Method method) {
        this.method = method;
    }

    public ThreadMode getThreadMode() {
        return threadMode;
    }

    public void setThreadMode(ThreadMode threadMode) {
        this.threadMode = threadMode;
    }

    public Class<?> getEventType() {
        return eventType;
    }

    public void setEventType(Class<?> eventType) {
        this.eventType = eventType;
    }
}

综上分析,所以我们的EventBus类基本写法如下:

package com.anonyper.annotationapplication.eventbus;

import android.os.Handler;
import android.os.Looper;

import com.anonyper.annotationapplication.bean.MethodBean;
import com.anonyper.annotationapplication.util.Loger;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * AnnotationApplication
 * Created by anonyper on 2019/6/6.
 */
public class EventBus {
    private static EventBus eventBus;

    private EventBus() {

    }

    public static EventBus getDefault() {
        synchronized (EventBus.class) {
            if (eventBus == null) {
                eventBus = new EventBus();
            }
        }
        return eventBus;
    }


    /**
     * 便于在主线程中发送消息
     */
    Handler handler = new Handler(Looper.getMainLooper());

    /**
     * 存储方法的容器
     */
    Map<Object, List<MethodBean>> methodBeanMap = new HashMap<>();

    /**
     * 针对每一个要发送消息存储对应的方法 便于查找
     */
    Map<String, List<MethodBean>> postMethodMap = new HashMap<>();

    /**
     * 注册对象
     *
     * @param object
     */
    public void register(Object object) {
        if (object != null) {
            List<MethodBean> methodBeanList = findMethodBean(object);
            if (methodBeanList != null) {
                methodBeanMap.put(object, methodBeanList);
            }
        }
    }


    /**
     * 取消注册对象
     *
     * @param object
     */
    public void unregister(Object object) {
        if (object != null && methodBeanMap.containsKey(object)) {
            List<MethodBean> methodBeanList = methodBeanMap.remove(object);
            for (MethodBean methodBean : methodBeanList) {
                String eventName = methodBean.getEventType().getName();
                removeEventMethod(eventName, methodBean);
                methodBean = null;
            }
            methodBeanList = null;
        }
    }

    /**
     * 根据对象找到标记了Subscribe注解的方法
     *
     * @param object
     * @return
     */
    private List<MethodBean> findMethodBean(Object object) {
        if (object == null) {
            return null;
        }
        List<MethodBean> methodBeanList = new ArrayList<>();
        //通过反射 获取该对象下面的所有方法,判断哪些是被特殊的注解标注了
        Class myclass = object.getClass();
        Method[] methods = myclass.getDeclaredMethods();//获取所有的方法 包括private的
        for (Method method : methods) {
            //获取Subscribe注解值
            if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
                int count = method.getParameterCount();
                if (count <= 0) {
                    continue;
                }
            }
            //获取参数类型
            Class<?>[] parameterClasses = method.getParameterTypes();
            if (parameterClasses.length != 1) {
                continue;
            }
            //获取注解的信息
            Subscribe methodInfo = method.getAnnotation(Subscribe.class);
            if (methodInfo != null) {
                Class eventClass = parameterClasses[0];
                MethodBean methodBean = new MethodBean();
                methodBean.setObject(object);
                methodBean.setMethod(method);
                methodBean.setThreadMode(methodInfo.threadMode());
                methodBean.setEventType(eventClass);
                methodBeanList.add(methodBean);
                String eventName = eventClass.getName();
                addEventMethod(eventName, methodBean);
            }
        }
        return methodBeanList;
    }

    /**
     * 添加方法事件对应的方法
     *
     * @param eventName  要发送的消息名称
     * @param methodBean 该消息对饮的methodBean
     */
    private void addEventMethod(String eventName, MethodBean methodBean) {
        if (eventName == null || methodBean == null) {
            return;
        }
        if (!postMethodMap.containsKey(eventName)) {
            List<MethodBean> methodBeanList = new ArrayList<>();
            postMethodMap.put(eventName, methodBeanList);
        }
        List<MethodBean> methodBeanList = postMethodMap.get(eventName);
        if (methodBeanList == null) {
            methodBeanList = new ArrayList<>();
        }
        if (!methodBeanList.contains(methodBean)) {
            methodBeanList.add(methodBean);
        }

    }

    /**
     * 移除该对象的对应方法
     *
     * @param eventName
     * @param methodBean
     */
    private void removeEventMethod(String eventName, MethodBean methodBean) {
        if (eventName == null || methodBean == null) {
            return;
        }
        List<MethodBean> methodBeanList = postMethodMap.get(eventName);
        methodBeanList.remove(methodBean);
        if(methodBeanList.size()<=0){
            postMethodMap.remove(eventName);
            methodBeanList.clear();
            methodBeanList = null;
        }
    }


    /**
     * 找到监听了该对象的所有方法,然后执行方法
     *
     * @param object
     */
    public void post(Object object) {
        if (object != null) {
            List<MethodBean> methodBeanList = postMethodMap.get(object.getClass().getName());
            if (methodBeanList == null) {
                return;
            }
            for (MethodBean methodBean : methodBeanList) {
                Loger.i(methodBean.getMethod().getName());
                ThreadMode threadMode = methodBean.getThreadMode();
                if (threadMode == ThreadMode.MAIN) {
                    //主线程发送
                    handler.post(new Runnable() {
                        @Override
                        public void run() {
                            invokeMethod(methodBean, object);
                        }
                    });
                } else {
                    //直接发送
                    invokeMethod(methodBean, object);
                }
            }
        }
    }

    /**
     * 执行发放
     *
     * @param methodBean
     * @param object     参数
     */
    void invokeMethod(MethodBean methodBean, Object object) {
        if (methodBean != null && object != null) {
            Method method = methodBean.getMethod();
            int modifiers = method.getModifiers();
            try {
                if (modifiers == Modifier.PRIVATE) {
                    method.setAccessible(true);
                }
                method.invoke(methodBean.getObject(), object);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }
    }
}

在使用的Activity中:

package com.anonyper.annotationapplication;

import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;

import com.anonyper.annotation.TestAnnotation;
import com.anonyper.annotationapplication.bean.MessageEvent;
import com.anonyper.annotationapplication.eventbus.EventBus;
import com.anonyper.annotationapplication.eventbus.Subscribe;
import com.anonyper.annotationapplication.eventbus.ThreadMode;
import com.anonyper.annotationapplication.util.Loger;

/**
 * Eventbus 测试类
 */
public class EventBusActivity extends AppCompatActivity {
    public static final String TAG = "EventBusActivity >>> ";
    TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = (TextView) this.findViewById(R.id.show_text);
        textView.setOnClickListener(o -> {
            EventBus.getDefault().post(new MessageEvent("这里是测试消息"));
        });
    }

    @Override
    protected void onStart() {
        super.onStart();
        EventBus.getDefault().register(this);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        EventBus.getDefault().unregister(this);
    }

    /**
     * 私有方法
     *
     * @param messageEvent
     */
    @Subscribe(threadMode = ThreadMode.MAIN)
    private void testPrivateMethod(MessageEvent messageEvent) {
        Loger.i(TAG + " PrivateMethod >>> " + messageEvent.getMessage());
        textView.setText(messageEvent.getMessage());
    }

    /**
     * public方法
     *
     * @param messageEvent
     */
    @Subscribe(threadMode = ThreadMode.OTHER)
    public void testPublicMethod(MessageEvent messageEvent) {
        Loger.i(TAG + " PublicMethod >>> " + messageEvent.getMessage());

    }


    /**
     * 没有添加注解的方法
     *
     * @param messageEvent
     */
    public void publicMethod(MessageEvent messageEvent) {
        Loger.i(TAG + " 该方法不会被调用 >>> " + messageEvent.getMessage());
    }


}

点击发送测试结果:

06-06 17:43:31.035 30025-30025/com.anonyper.annotationapplication I/Anonyper >>>: EventBusActivity >>>  PublicMethod >>> 这里是测试消息
06-06 17:43:31.035 30025-30025/com.anonyper.annotationapplication I/Anonyper >>>: EventBusActivity >>>  PrivateMethod >>> 这里是测试消息

总结

EventBus中反射用到的方法有Class、Method等,我们一一列出他们的具体知识点:

Class用法

Class类是用来记录每一个对象所属类的信息,是独一无二的。在JVM装载所需要的类的时候,如果没有加载过该对象,那么就绪根据类名查找.class文件,然后将其Class对象加载进去。

Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的,因此不能显式地声明一个Class对象。

创建Class对象的方法
  • 调用实例化对象的.getClass()方法
  • 使用Class.forName("com.XXX.XXX.ClassName")
  • 一个Java类Test.java的类对象是Test.class
Class中的方法
  • forName(String classname)
    该方法返回给定串名相应的Class对象。

  • getClassLoader()
    获取该类的类装载器。

  • getComponentType()
    如果当前类表示一个数组,则返回表示该数组组件的Class对象,否则返回null。

  • getConstructor(Class[])
    返回当前Class对象表示的类的指定的公有构造子对象。

  • getConstructors()
    返回当前Class对象表示的类的所有公有构造子对象数组。

  • getDeclaredConstructor(Class[])
    返回当前Class对象表示的类的指定已说明的一个构造子对象。

  • getDeclaredConstructors()
    返回当前Class对象表示的类的所有已说明的构造子对象数组。

  • getDeclaredField(String)
    返回当前Class对象表示的类或接口的指定已说明的一个域对象。

  • getDeclaredFields()
    返回当前Class对象表示的类或接口的所有已说明的域对象数组。

  • getDeclaredMethod(String,Class[])
    返回当前Class对象表示的类或接口的指定已说明的一个方法对象。

  • getDeclaredMethods()
    返回Class对象表示的类或接口的所有已说明的方法数组。

  • getField(String)
    返回当前Class对象表示的类或接口的指定的公有成员域对象。

  • getFields()
    返回当前Class对象表示的类或接口的所有可访问的公有域对象数组。

  • getInterfaces()
    返回当前对象表示的类或接口实现的接口。

  • getMethod(String,Class[])
    返回当前Class对象表示的类或接口的指定的公有成员方法对象。

  • getMethods()
    返回当前Class对象表示的类或接口的所有公有成员方法对象数组,包括已声明的和从父类继承的方法。

  • getModifiers()
    返回该类或接口的Java语言修改器代码。

  • getName()
    返回Class对象表示的类型(类、接口、数组或基类型)的完整路径名字符串。

  • getResource(String)
    按指定名查找资源。

  • getResourceAsStream(String)
    用给定名查找资源。

  • getSigners()
    获取类标记。

  • getSuperclass()
    如果此对象表示除Object外的任一类,那么返回此对象的父类对象。

  • isArray()
    如果Class对象表示一个数组则返回true,否则返回false。

  • isAssignableFrom(Class)
    判定Class对象表示的类或接口是否同参数指定的Class表示的类或接口相同,或是其父类。

  • isInstance(Object)
    此方法是Java语言instanceof操作的动态等价方法。

  • isInterface()
    判定指定的Class对象是否表示一个接口类型。

  • isPrimitive()
    判定指定的Class对象是否表示一个Java的基类型。

  • newInstance()
    创建类的新实例。

Method用法

通过class我们可以获取到该Class 对象下Method,然后我们就可以调用方法来执行。

Method方法
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package java.lang.reflect;

import androidx.annotation.RecentlyNonNull;
import java.lang.annotation.Annotation;

public final class Method extends Executable {
    Method() {//构造方法
        throw new RuntimeException("Stub!");
    }

    @RecentlyNonNull
    public Class<?> getDeclaringClass() {//该方法的Class对象
        throw new RuntimeException("Stub!");
    }

    public String getName() {//方法名字
        throw new RuntimeException("Stub!");
    }

    public int getModifiers() {//修饰符
        throw new RuntimeException("Stub!");
    }

    @RecentlyNonNull
    public TypeVariable<Method>[] getTypeParameters() {//和这个类泛型有关吧,没太懂
        throw new RuntimeException("Stub!");
    }
  //List.class.getTypeParameters() 输出:E



    @RecentlyNonNull
    public Class<?> getReturnType() {//返回类型 Class对象
        throw new RuntimeException("Stub!");
    }

    @RecentlyNonNull
    public Type getGenericReturnType() {//返回类型 Type对象
        throw new RuntimeException("Stub!");
    }

    @RecentlyNonNull
    public Class<?>[] getParameterTypes() {//参数类型 Class数组
        throw new RuntimeException("Stub!");
    }

    public int getParameterCount() {//参数个数
        throw new RuntimeException("Stub!");
    }

    @RecentlyNonNull
    public Type[] getGenericParameterTypes() {//参数类型 Type数组
        throw new RuntimeException("Stub!");
    }

    @RecentlyNonNull
    public native Class<?>[] getExceptionTypes();//异常类型 Class数组

    @RecentlyNonNull
    public Type[] getGenericExceptionTypes() {//异常类型 Type数组
        throw new RuntimeException("Stub!");
    }

    public boolean equals(Object obj) {
        throw new RuntimeException("Stub!");
    }

    public int hashCode() {
        throw new RuntimeException("Stub!");
    }

    @RecentlyNonNull
    public String toString() {
        throw new RuntimeException("Stub!");
    }

    @RecentlyNonNull
    public String toGenericString() {//描述此方法的字符串,包括类型参数 示例:public void com.yiibai.SampleClass.setSampleField(java.lang.String)
        throw new RuntimeException("Stub!");
    }

    //反射调用该方法 传入参数var1 是该方法所属对象 var2 参数(可变形参)
    public native Object invoke(Object var1, Object... var2) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException;

    public boolean isBridge() {//是否是桥接方法
        throw new RuntimeException("Stub!");
    }

    public boolean isVarArgs() {//是否是可变参数
        throw new RuntimeException("Stub!");
    }

    public boolean isSynthetic() {//是否是合成方法
        throw new RuntimeException("Stub!");
    }

    public boolean isDefault() {//该方法是否是一个注解的属性
        throw new RuntimeException("Stub!");
    }

    public native Object getDefaultValue();//返回注解的默认值
    //注解方法属性示例
    //public String stringValue() default "string default value";

    public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {//返回指定注解信息
        throw new RuntimeException("Stub!");
    }

    @RecentlyNonNull
    public Annotation[] getDeclaredAnnotations() {//返回存在所有注解信息
        throw new RuntimeException("Stub!");
    }

    @RecentlyNonNull
    public Annotation[][] getParameterAnnotations() {//返回参数注解信息
        throw new RuntimeException("Stub!");
    }
}

以上,代码主要在EventBus类中,就不放git地址了!

转载于:https://www.jianshu.com/p/0cbc43383b81

猜你喜欢

转载自blog.csdn.net/weixin_33713350/article/details/91074800