Java注解知识梳理—反射(运行时注解使用)
前面我们梳理了注解的知识以及注解处理器的使用,现在我们梳理一下运行时注解的使用。
一个运行时的注解,注解信息会被加载进JVM中,这个时候我们是没办法再根据具体运行的情况写代码进程处理的,所以只能依据代码可能运行的逻辑来获取相关注解元素信息,然后做一些处理,这需要用到java的反射,
反射是什么以及有什么用途
反射(Reflection)是Java的一个特性,通过反射可以获取Java任意对象的信息,获取了对象的信息之后可以做一下用途:
- 判断该对象所属的类
- 实例化该对象
- 获取该对象下面的所有成员变量和方法
- 调用该对象下的变量和方法
简而言之,就是通过反射,我们可以获取编译好的每一个类及其信息,然后实例化对象,调用方法。
我们在开发过程中,IDE自动的提示以及大型框架比如Spring等,都是利用反射的原理。Android中利用反射比较有名的框架就是EventBus框架。我们就模拟写一个简单的EventBus来具体理解反射的作用。
EventBus是做消息分发用的,作用就是我们在一个类中注解一个对象的监听方法之后,当有该对象的消息发出时,注册的地方可以接收到该消息。EventBus的用法如下:
-
定义发生改变需要通知的对象:
public static class MessageEvent { /* Additional fields if needed */ }
-
定义一个注解,并标明是该消息通知的线程是否主线程
@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); }
-
发送消息
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 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();
while (myclass != null) {//循环父类方法,父类方法中使用Subscrib注解的也会被加入进来
String className = myclass.getName();
//系统的代码不做处理
if (className.startsWith("java.") || className.startsWith("javax.") || className.startsWith("android.")) {
break;
}
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);
}
}
myclass = myclass.getSuperclass();
}
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) {
ThreadMode threadMode = methodBean.getThreadMode();
if (threadMode == ThreadMode.MAIN) {
//需要主线程发送
if(Looper.myLooper() == Looper.getMainLooper()){
//当前线程就是主线程
invokeMethod(methodBean, object);
}else{
//当前线程是子线程
handler.post(() -> 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的基类型。isAnonymousClass()
判定指定的Class对象是否是一个匿名内部类。isLocalClass()
判定指定的Class对象是否是一个局部类。isMemberClass()
判定指定的Class对象是否是一个成员类。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地址了!