Java注解知識梳理—反射(運行時注解使用)
前面我們梳理了注解的知識以及注解處理器的使用,現(xiàn)在我們梳理一下運行時注解的使用。
一個運行時的注解,注解信息會被加載進JVM中,這個時候我們是沒辦法再根據(jù)具體運行的情況寫代碼進程處理的,所以只能依據(jù)代碼可能運行的邏輯來獲取相關注解元素信息,然后做一些處理,這需要用到java的反射,
反射是什么以及有什么用途
反射(Reflection)是Java的一個特性,通過反射可以獲取Java任意對象的信息,獲取了對象的信息之后可以做一下用途:
- 判斷該對象所屬的類
- 實例化該對象
- 獲取該對象下面的所有成員變量和方法
- 調(diào)用該對象下的變量和方法
簡而言之,就是通過反射,我們可以獲取編譯好的每一個類及其信息,然后實例化對象,調(diào)用方法。
我們在開發(fā)過程中,IDE自動的提示以及大型框架比如Spring等,都是利用反射的原理。Android中利用反射比較有名的框架就是EventBus框架。我們就模擬寫一個簡單的EventBus來具體理解反射的作用。
EventBus是做消息分發(fā)用的,作用就是我們在一個類中注解一個對象的監(jiān)聽方法之后,當有該對象的消息發(fā)出時,注冊的地方可以接收到該消息。EventBus的用法如下:
-
定義發(fā)生改變需要通知的對象:
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); } -
發(fā)送消息
EventBus.getDefault().post(new MessageEvent());
跟著我左手右手一步一步寫代碼
代碼中起的名字可以隨意,這里和EventBus保持一致。
創(chuàng)建ThreadMode的枚舉
根據(jù)@Subscribe(threadMode = ThreadMode.MAIN)這句可以分析出,我們需要給該注解一個枚舉類型,來區(qū)分標記的方法是用在主線程還是子線程:
public enum ThreadMode {
MAIN,//主線程
OTHER//子線程
}
創(chuàng)建注解Subscribe
該注解有以下特性:
- 運行時注解
- 有一個屬性叫做threadMode,參數(shù)為ThreadMode的枚舉
- 該注解標注在方法上
@Target(ElementType.METHOD)//標注的類型在方法上
@Retention(RetentionPolicy.RUNTIME)//運行時注解
public @interface Subscribe {
//一個線程屬性,默認是在主線程運行
ThreadMode threadMode() default ThreadMode.Main;
}
創(chuàng)建一個需要通知的對象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;
}
}
創(chuàng)建EventBus類相關代碼
EventBus類的功能主要有以下兩點:
管理注冊的對象
根據(jù)要通知的消息bean,獲取關注了該消息bean的對象,然后調(diào)用該對象下的方法發(fā)出通知
根據(jù)功能分析:
1、EventBus全局通用,需要是一個單例模式,項目中僅存在唯一一個對象。
2、發(fā)送消息通知時,找出接受該消息變化的方法,然后執(zhí)行。
3、同時會有多個對象可能需要監(jiān)聽同一個消息的變化(MainActivity和SetActivity都監(jiān)聽短信消息)
4、一個對象可能需要監(jiān)聽多個消息的變化(一個Activity中監(jiān)聽網(wǎng)絡消息、短信消息)
5、所以注冊的時候,需要找到該對象下所有標注了@Subscribe注解的方法
6、所以找到標注了@Subscribe注解的方法需要保存起來,需要有一個Bean對象承載改方法的信息。
7、方法的執(zhí)行通過反射來執(zhí)行,method.invoke(Object obj,Object... obj2),第一個是該方法所屬的對象,第二個是方法的參數(shù)。
8、承載方法信息的Bean我們定義為MethodBean,具體內(nèi)容如下:
/**
* 方法bean
* AnnotationApplication
* Created by anonyper on 2019/6/6.
*/
public class MethodBean {
/**
* 該方法對應的類對象
*/
Object object;
/**
* 方法對象
*/
Method method;
/**
* 運行線程指定
*/
ThreadMode threadMode;
/**
* 參數(shù)類型
*/
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;
}
/**
* 便于在主線程中發(fā)送消息
*/
Handler handler = new Handler(Looper.getMainLooper());
/**
* 存儲方法的容器
*/
Map<Object, List<MethodBean>> methodBeanMap = new HashMap<>();
/**
* 針對每一個要發(fā)送消息存儲對應的方法 便于查找
*/
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;
}
}
/**
* 根據(jù)對象找到標記了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) {//循環(huán)父類方法,父類方法中使用Subscrib注解的也會被加入進來
String className = myclass.getName();
//系統(tǒng)的代碼不做處理
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;
}
}
//獲取參數(shù)類型
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 要發(fā)送的消息名稱
* @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;
}
}
/**
* 找到監(jiān)聽了該對象的所有方法,然后執(zhí)行方法
*
* @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) {
//需要主線程發(fā)送
if(Looper.myLooper() == Looper.getMainLooper()){
//當前線程就是主線程
invokeMethod(methodBean, object);
}else{
//當前線程是子線程
handler.post(() -> invokeMethod(methodBean, object));
}
} else {
//直接發(fā)送
invokeMethod(methodBean, object);
}
}
}
}
/**
* 執(zhí)行發(fā)放
*
* @param methodBean
* @param object 參數(shù)
*/
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 + " 該方法不會被調(diào)用 >>> " + messageEvent.getMessage());
}
}
點擊發(fā)送測試結(jié)果:
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 >>> 這里是測試消息
總結(jié)
EventBus中反射用到的方法有Class、Method等,我們一一列出他們的具體知識點:
Class用法
Class類是用來記錄每一個對象所屬類的信息,是獨一無二的。在JVM裝載所需要的類的時候,如果沒有加載過該對象,那么就緒根據(jù)類名查找.class文件,然后將其Class對象加載進去。
Class 沒有公共構(gòu)造方法。Class 對象是在加載類時由 Java 虛擬機以及通過調(diào)用類加載器中的 defineClass 方法自動構(gòu)造的,因此不能顯式地聲明一個Class對象。
創(chuàng)建Class對象的方法
- 調(diào)用實例化對象的.getClass()方法
- 使用Class.forName("com.XXX.XXX.ClassName")
- 一個Java類Test.java的類對象是Test.class
Class中的方法
forName(String classname)
該方法返回給定串名相應的Class對象。getClassLoader()
獲取該類的類裝載器。getComponentType()
如果當前類表示一個數(shù)組,則返回表示該數(shù)組組件的Class對象,否則返回null。getConstructor(Class[])
返回當前Class對象表示的類的指定的公有構(gòu)造子對象。getConstructors()
返回當前Class對象表示的類的所有公有構(gòu)造子對象數(shù)組。getDeclaredConstructor(Class[])
返回當前Class對象表示的類的指定已說明的一個構(gòu)造子對象。getDeclaredConstructors()
返回當前Class對象表示的類的所有已說明的構(gòu)造子對象數(shù)組。getDeclaredField(String)
返回當前Class對象表示的類或接口的指定已說明的一個域?qū)ο蟆?/p>getDeclaredFields()
返回當前Class對象表示的類或接口的所有已說明的域?qū)ο髷?shù)組。getDeclaredMethod(String,Class[])
返回當前Class對象表示的類或接口的指定已說明的一個方法對象。getDeclaredMethods()
返回Class對象表示的類或接口的所有已說明的方法數(shù)組。getField(String)
返回當前Class對象表示的類或接口的指定的公有成員域?qū)ο蟆?/p>getFields()
返回當前Class對象表示的類或接口的所有可訪問的公有域?qū)ο髷?shù)組。getInterfaces()
返回當前對象表示的類或接口實現(xiàn)的接口。getMethod(String,Class[])
返回當前Class對象表示的類或接口的指定的公有成員方法對象。getMethods()
返回當前Class對象表示的類或接口的所有公有成員方法對象數(shù)組,包括已聲明的和從父類繼承的方法。getModifiers()
返回該類或接口的Java語言修改器代碼。getName()
返回Class對象表示的類型(類、接口、數(shù)組或基類型)的完整路徑名字符串。getResource(String)
按指定名查找資源。getResourceAsStream(String)
用給定名查找資源。getSigners()
獲取類標記。getSuperclass()
如果此對象表示除Object外的任一類,那么返回此對象的父類對象。isArray()
如果Class對象表示一個數(shù)組則返回true,否則返回false。isAssignableFrom(Class)
判定Class對象表示的類或接口是否同參數(shù)指定的Class表示的類或接口相同,或是其父類。isInstance(Object)
此方法是Java語言instanceof操作的動態(tài)等價方法。isInterface()
判定指定的Class對象是否表示一個接口類型。isPrimitive()
判定指定的Class對象是否表示一個Java的基類型。isAnonymousClass()
判定指定的Class對象是否是一個匿名內(nèi)部類。isLocalClass()
判定指定的Class對象是否是一個局部類。isMemberClass()
判定指定的Class對象是否是一個成員類。newInstance()
創(chuàng)建類的新實例。
Method用法
通過class我們可以獲取到該Class 對象下Method,然后我們就可以調(diào)用方法來執(zhí)行。
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() {//構(gòu)造方法
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() {//參數(shù)類型 Class數(shù)組
throw new RuntimeException("Stub!");
}
public int getParameterCount() {//參數(shù)個數(shù)
throw new RuntimeException("Stub!");
}
@RecentlyNonNull
public Type[] getGenericParameterTypes() {//參數(shù)類型 Type數(shù)組
throw new RuntimeException("Stub!");
}
@RecentlyNonNull
public native Class<?>[] getExceptionTypes();//異常類型 Class數(shù)組
@RecentlyNonNull
public Type[] getGenericExceptionTypes() {//異常類型 Type數(shù)組
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() {//描述此方法的字符串,包括類型參數(shù) 示例:public void com.yiibai.SampleClass.setSampleField(java.lang.String)
throw new RuntimeException("Stub!");
}
//反射調(diào)用該方法 傳入?yún)?shù)var1 是該方法所屬對象 var2 參數(shù)(可變形參)
public native Object invoke(Object var1, Object... var2) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException;
public boolean isBridge() {//是否是橋接方法
throw new RuntimeException("Stub!");
}
public boolean isVarArgs() {//是否是可變參數(shù)
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() {//返回參數(shù)注解信息
throw new RuntimeException("Stub!");
}
}
以上,代碼主要在EventBus類中,就不放git地址了!