上一篇【線(xiàn)程、多線(xiàn)程與線(xiàn)程池總結】中主要記錄線(xiàn)程、多線(xiàn)程相關(guān)概念,側重于線(xiàn)程的Future使用與線(xiàn)程池的操作;同樣這一篇【反射、注解與依賴(lài)注入總結】依然著(zhù)重于相關(guān)概念與使用。
現在在我們構建自己或公司的項目中,或多或少都會(huì )依賴(lài)幾個(gè)流行比較屌的第三方庫,比如:Butter Knife、Retrofit、Dagger 2等,如果你沒(méi)用過(guò),那你需要找時(shí)間補一下啦;有時(shí)在使用后我們會(huì )好奇他們到底是怎么做到這種簡(jiǎn)潔、高效、松耦合等諸多優(yōu)點(diǎn)的,當然這里我不探討它們具體怎么實(shí)現的,而關(guān)心的是它們都用到同樣的技術(shù)反射和注解,并實(shí)現的依賴(lài)注入。
如果你好奇這些庫具體是怎么實(shí)現的,或者想了解他們實(shí)現的原理,這里向你推薦幾篇文章:
1、android注解Butterknife的使用及代碼分析
2、Retrofit源碼1: 為什么寫(xiě)一個(gè)interface就可以實(shí)現http請求
3、Retrofit分析-漂亮的解耦套路
4、Dagger 源碼解析
5、Android:dagger2讓你愛(ài)不釋手-基礎依賴(lài)注入框架篇
6、Android:dagger2讓你愛(ài)不釋手-重點(diǎn)概念講解、融合篇
7、Android:dagger2讓你愛(ài)不釋手-終結篇
這些好文章已經(jīng)幫你收藏了,下面直接進(jìn)入我的主題【反射、注解與依賴(lài)注入總結】。
反射的概念
主要是指程序可以訪(fǎng)問(wèn),檢測和修改它本身狀態(tài)或行為的一種能力,并能根據自身行為的狀態(tài)和結果,調整或修改應用所描述行為的狀態(tài)和相關(guān)的語(yǔ)義。
概念看著(zhù)就有些暈或不知所云啦,可以通過(guò)反射的作用理解它的概念。
反射的作用
反射可以讓我們在運行時(shí)獲取類(lèi)的屬性,方法,構造方法、父類(lèi)、接口等信息,通過(guò)反射還可以讓我們在運行期實(shí)例化對象、調用方法、即使方法或屬性是私有的的也可以通過(guò)反射的形式調用。
所有為什么第三方庫基本都會(huì )使用到反射,正是因為反射這種 “看透 Class” 的能力。
反射相關(guān)的類(lèi)、方法
要看透一個(gè)類(lèi),首先要獲取這個(gè)類(lèi)的對象,其它信息都是通過(guò)這個(gè)對象獲取的,下面的所有的示例具體操作代碼請參考 【個(gè)人學(xué)習項目DroidStudy】,我在這個(gè)工程下新建一個(gè) ReflectionActivity,包的路徑為 com.sun.study.ui.activity.ReflectionActivity,通過(guò)反射相關(guān)的類(lèi)、方法讓我看透這個(gè)類(lèi)。
1、獲取對象的三種方式:
第一種、知道一個(gè)類(lèi),直接獲取 Class 對象
Class<?> cls1 = ReflectionActivity.class; 第二種、如果已經(jīng)得到了某個(gè)對象,可以通過(guò)這個(gè)對象獲取 Class 對象
ReflectionActivity activity = new ReflectionActivity();Class<?> cls2 = activity.getClass(); 第三種、如果你在編譯期獲取不到目標類(lèi)型,但是你知道它的完整類(lèi)路徑,那么你可以通過(guò)如下的形式來(lái)獲取 Class 對象,這樣獲取可能會(huì )拋出異常 ClassNotFoundException。
try { Class<?> cls3 = Class.forName("com.sun.study.ui.activity.ReflectionActivity");} catch (ClassNotFoundException e) { e.printStackTrace();} 2、反射的相關(guān)方法和示例
列出反射的相關(guān)方法
getName():獲得類(lèi)的完整名字。 newInstance():通過(guò)類(lèi)的不帶參數的構造方法創(chuàng )建這個(gè)類(lèi)的一個(gè)對象。getFields():獲得類(lèi)的public類(lèi)型的屬性。 getDeclaredFields():獲得類(lèi)的所有屬性。getMethods():獲得類(lèi)的public類(lèi)型的方法。 getDeclaredMethods():獲得類(lèi)的所有方法。 getMethod(String name, Class[] parameterTypes):獲得類(lèi)的特定方法。getModifiers()和Modifier.toString():獲得屬修飾符,例如private,public,static等 getReturnType():獲得方法的返回類(lèi)型 getParameterTypes():獲得方法的參數類(lèi)型getConstructors():獲得類(lèi)的public類(lèi)型的構造方法。 getConstructor(Class[] parameterTypes):獲得類(lèi)的特定構造方法。getSuperclass():獲取某類(lèi)的父類(lèi) getInterfaces():獲取某類(lèi)實(shí)現的接口 示例一:獲得類(lèi)的所有方法(Method)信息
private void getMethodsInfo() { Class<ReflectionActivity> cls = ReflectionActivity.class; Method[] methods = cls.getDeclaredMethods(); if (methods == null) return; StringBuilder sb = new StringBuilder(); for (Method method:methods) { sb.append(Modifier.toString(method.getModifiers())).append(" "); sb.append(method.getReturnType()).append(" "); sb.append(method.getName()).append("("); Class[] parameters = method.getParameterTypes(); if (parameters != null) { for (int i=0; i<parameters.length; i++) { Class paramCls = parameters[i]; sb.append(paramCls.getSimpleName()); if (i < parameters.length - 1) sb.append(", "); } } sb.append(")\n\n"); } tvInfo.setText(sb.toString());} 運行結果如下圖:

reflection_icon1.png
示例一:獲得類(lèi)的所有屬性(Field)信息,并修改類(lèi)型Int屬性i的值
private void modifyFieldValue() { Class<ReflectionActivity> cls = ReflectionActivity.class; Field[] fields = cls.getDeclaredFields(); if (fields == null) return; StringBuilder sb = new StringBuilder(); sb.append("獲得類(lèi)的所有屬性信息:\n\n"); for (Field field:fields) { sb.append(Modifier.toString(field.getModifiers())).append(" "); sb.append(field.getType().getSimpleName()).append(" "); sb.append(field.getName()).append(";"); sb.append("\n\n"); } try { sb.append("屬性i的默認值:i = "); Field f = cls.getDeclaredField("i"); sb.append(f.getInt("i")).append("\n\n"); f.set("i", 100); sb.append("屬性i修改后的值:i = "); sb.append(f.getInt("i")).append("\n\n"); } catch (Exception e) { e.printStackTrace(); } tvInfo.setText(sb.toString()); toolbar.setSubtitle("修改類(lèi)型Int屬性i的值");} 運行結果如下圖:

reflection_icon2.png
更多示例請參考 【個(gè)人學(xué)習項目DroidStudy】
反射的相關(guān)內容先記錄到這,接下來(lái)看看注解相關(guān)概念與使用。
注解的概念
注解(Annotation),也叫元數據。一種代碼級別的說(shuō)明。它是JDK 1.5及以后版本引入的一個(gè)特性,與類(lèi)、接口、枚舉是在同一個(gè)層次。它可以聲明在包、類(lèi)、字段、方法、局部變量、方法參數等的前面,用來(lái)對這些元素進(jìn)行說(shuō)明,注釋。
注解的作用
1、標記作用,用于告訴編譯器一些信息讓編譯器能夠實(shí)現基本的編譯檢查,如@Override、Deprecated,看下它倆的源碼
@Target(ElementType.METHOD)@Retention(RetentionPolicy.SOURCE)public @interface Override {}@Documented@Retention(RetentionPolicy.RUNTIME)public @interface Deprecated {} 2、編譯時(shí)動(dòng)態(tài)處理,動(dòng)態(tài)生成代碼,如Butter Knife、Dagger 2
3、運行時(shí)動(dòng)態(tài)處理,獲得注解信息,如Retrofit
注解的分類(lèi)
注解的分類(lèi)有兩種分法:
第一種分法
1、基本內置注解,是指Java自帶的幾個(gè)Annotation,如@Override、Deprecated、@SuppressWarnings等
2、元注解(meta-annotation),是指負責注解其他注解的注解,JDK 1.5及以后版本定義了4個(gè)標準的元注解類(lèi)型,如下:
1、@Target2、@Retention3、@Documented4、@Inherited 3、自定義注解,根據需要可以自定義注解,自定義注解需要用到上面的meta-annotation
第二種分法,根據作用域分類(lèi)
1、源碼時(shí)注解(RetentionPolicy.SOURCE)
2、編譯時(shí)注解(RetentionPolicy.CLASS)
3、運行時(shí)注解(RetentionPolicy.RUNTIME)
注解相關(guān)知識點(diǎn)
1、元注解相關(guān)信息
@Target:指Annotation所修飾的對象范圍,通過(guò)ElementType取值有8種,如下
TYPE:類(lèi)、接口(包括注解類(lèi)型)或枚舉FIELD:屬性METHOD:方法PARAMETER:參數CONSTRUCTOR:構造函數LOCAL_VARIABLE:局部變量ANNOTATION_TYPE:注解類(lèi)型PACKAGE:包 @Retention:指Annotation被保留的時(shí)間長(cháng)短,通過(guò)RetentionPolicy取值有3種,如下:
SOURCE:在源文件中有效(即源文件保留) CLASS:在class文件中有效(即class保留) RUNTIME:在運行時(shí)有效(即運行時(shí)保留) @Documented:是一個(gè)標記注解,用于描述其它類(lèi)型的注解應該被作為被標注的程序成員的公共API,因此可以被例如javadoc此類(lèi)的工具文檔化。
@Inherited:也是一個(gè)標記注解,@Inherited闡述了某個(gè)被標注的類(lèi)型是被繼承的
2、注解定義格式
public @interface 注解名 { 定義體 } 3、注解參數可支持的數據類(lèi)型:
8種基本數據類(lèi)型 int、float、boolean、byte、double、char、long、short String、Class、enum、Annotation 以上所有類(lèi)型的數組 4、?注意:自定義注解如果只有一個(gè)參數成員,最好把定義體參數名稱(chēng)設為"value",如@Target
@Documented@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.ANNOTATION_TYPE)public @interface Target { ElementType[] value();} 看一個(gè)示例
具體要求和運行結果都在下面這張圖上顯示出來(lái)了,貼下圖

annotation_icon.png
再貼三塊代碼,首先是自定義注解代碼:
@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@Inherited@Documentedpublic @interface RequestAnnotation { boolean withDialog() default true; String withMessage() default "正在加載,請稍后...";} 其次是執行模擬的網(wǎng)絡(luò )請求,核心代碼是通過(guò)上面的反射和注解完成的;具體詳細代碼請參考 【個(gè)人學(xué)習項目DroidStudy】,下次使用動(dòng)態(tài)代理和Google的dexmaker完成這個(gè)功能,敬請關(guān)注,如果你對線(xiàn)程池還不清晰請參考我以前的文章【線(xiàn)程、多線(xiàn)程與線(xiàn)程池總結】。貼下核心代碼:
// 線(xiàn)程池private static ExecutorService pool = Executors.newCachedThreadPool();// 模擬處理網(wǎng)絡(luò )請求public boolean process(final Class<?> clazz, String methodName, final Object... args) throws Exception { Class[] argsClass = getClazzByArgs(args); final Method method = clazz.getDeclaredMethod(methodName, argsClass); if (method == null) { sendMsg(TYPE_ERROR); return false; } // 獲取注解信息 RequestAnnotation annotation = method.getAnnotation(RequestAnnotation.class); if (annotation != null && annotation.withDialog()) { loadingDialog.show(annotation.withMessage()); } pool.execute(new Runnable() { @Override public void run() { try { method.setAccessible(true); method.invoke(clazz.newInstance(), args); sendMsg(TYPE_SUCCESS); } catch (Exception e) { e.printStackTrace(); } } }); return true;} 最后是調用網(wǎng)絡(luò )請求接口:
@RequestAnnotation(withDialog = false, withMessage = "正在加載,請稍后...")public void apiTestFunc(String param1, String param2) { try { // 模擬網(wǎng)絡(luò )請求的耗時(shí)操作 Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); }}// 點(diǎn)擊執行的代碼DynamicProxyUtil proxyUtil = new DynamicProxyUtil(AnnotationActivity.this);proxyUtil.process(RequestNetworkApi.class, "apiTestFunc", "參數一", "參數二"); 依賴(lài)注入(Dependency Injection):可以通過(guò)這個(gè)服務(wù)來(lái)安全的注入組件到應用程序中,在應用程序部署的時(shí)候還可以選擇從特定的接口屬性進(jìn)行注入。
看完上面反射和注解的記錄后,可以更好的理解依賴(lài)注入,如果你不用那些第三方的注入庫你也在經(jīng)常用到依賴(lài)注入,比如下面這一段從codekk上截取的代碼:
public class Human { ... Father father; ... public Human(Father father) { this.father = father; }} 上面代碼中,我們將 father 對象作為構造函數的一個(gè)參數傳入。在調用 Human 的構造方法之前外部就已經(jīng)初始化好了 Father 對象。像這種非自己主動(dòng)初始化依賴(lài),而通過(guò)外部來(lái)傳入依賴(lài)的方式,我們就稱(chēng)為依賴(lài)注入。
依賴(lài)注入的實(shí)現有多種途徑,而在 Java 中,使用注解是最常用的。比如通過(guò)Butter Knife、Dagger依賴(lài)注入庫實(shí)現,都是使用注解來(lái)實(shí)現依賴(lài)注入,但它利用 APT(Annotation Process Tool) 在編譯時(shí)生成輔助類(lèi),這些類(lèi)繼承特定父類(lèi)或實(shí)現特定接口,程序在運行時(shí)加載這些輔助類(lèi),調用相應接口完成依賴(lài)生成和注入。
依賴(lài)注入在這里僅僅剖析下概念,有時(shí)間將會(huì )補一個(gè)例子,暫且到這吧。
文/孫福生(簡(jiǎn)書(shū)作者)
原文鏈接:http://www.jianshu.com/p/24820bf3df5c
聯(lián)系客服