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
在使用的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[] 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 getAnnotation(Class annotationClass) {//返回指定注解信息
throw new RuntimeException("Stub!");
}
@RecentlyNonNull
public Annotation[] getDeclaredAnnotations() {//返回存在所有注解信息
throw new RuntimeException("Stub!");
}
@RecentlyNonNull
public Annotation[][] getParameterAnnotations() {//返回参数注解信息
throw new RuntimeException("Stub!");
}
}
以上,代码主要在EventBus类中,就不放git地址了!