Java注解知識梳理—反射(運行時注解使用)

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的用法如下:

  1. 定義發(fā)生改變需要通知的對象:

    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. 發(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地址了!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容