註解咱們常用它,不少框架也提供了不少註解給咱們使用,如 ARouter
的 @Route(path = "/test/activity")
、butterknife
的 @BindView(R.id.user) EditText username;
等,可是,你有沒有自定義過註解,寫過本身的註解處理器呢?反射聽起來很高大上,可是實際上你真的瞭解他以後,只是一些API的調用而已;動態代理其實只是在靜態代理(代理模式)基礎上使用了反射技術;本篇文章將帶領你們對註解、反射及動態代理有更清晰的認知。html
本篇文章的示例代碼放在 Github 上,全部知識點,如圖:java
註解(Annotations),元數據的一種形式,提供有關於程序但不屬於程序自己的數據。註解對它們註解的代碼的操做沒有直接影響。android
註解有多種用途,例如:git
註解的格式以下:github
@Persilee class MyClass { ... }
註解已 @
開頭後面跟上內容,註解能夠包含元素,例如:緩存
@Persilee(id=666, value = "lsy") class MyClass { ... }
若是,只有一個 value
元素,則能夠省略該名稱,若是,沒有元素,則能夠省略括號,例如bash
@Persilee("lsy") // 只有一個 value 元素 class MyClass { ... } @Persilee // 沒有元素 class MyClass { ... }
若是,註解有相同的類型,則是重複註解,如app
@Persilee("lsy") @Persilee("zimu") class MyClass { ... }
註解的定義相似於接口的定義,在關鍵字 interface
前加上 @
,如:框架
@interface Persilee { int id(); String value(); }
int id()
和 String value()
是註解類型(annotation type),它們也能夠定義可選的默認值,如:ide
@interface Persilee { int id(); String value() default "lsy"; }
在使用註解時,若是定義的註解的註解類型沒有默認值,則必須進行賦值,如:
@Persilee(id = 666) // id 必需要賦值,如,@Persilee 會提示 id 必須賦值 class MyClass { ... }
在註解上面的註解稱爲元註解(meta-annotations),如
@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.SOURCE) @interface Persilee { int id(); String value() default "lsy"; }
在 java.lang.annotation
中定義了幾種元註解類型(常使用的是 @Retention、@Target),如
@Retention 指定註解的存儲方式,咱們由 RetentionPolicy.java
(是一個枚舉)可知,如:
public enum RetentionPolicy { SOURCE, // 標記的註解僅保留在源級別中,並被編譯器忽略。 CLASS, // 標記的註解在編譯時由編譯器保留,但 Java 虛擬機(JVM)會忽略。 RUNTIME // 標記的註解由 JVM 保留,所以運行時環境可使用它。 }
@Target 指定註解可使用的範圍,咱們由 ElementType.java
(是一個枚舉)可知使用範圍,以下:
public enum ElementType { TYPE, // 類 FIELD, // 字段或屬性 METHOD, // 方法 PARAMETER, // 參數 CONSTRUCTOR, // 構造方法 LOCAL_VARIABLE, // 局部變量 ANNOTATION_TYPE, // 也可使用在註解上 PACKAGE, // 包 TYPE_PARAMETER, // 類型參數 TYPE_USE // 任何類型 }
對於 TYPE_PARAMETER
(類型參數) 、 TYPE_USE
(任何類型名稱) 可能不是很好理解,若是把 Target
設置成 @Target({ElementType.TYPE_PARAMETER})
,表示可使用在泛型(上篇文章有介紹過泛型)的類型參數上,如:
public class TypeParameterClass<@Persilee T> { public <@Persilee T> T foo(T t) { return null; } }
若是把 Target
設置成 @Target({ElementType.TYPE_USE})
,表示可使用在任何類型上,如:
TypeParameterClass<@Persilee String> typeParameterClass = new TypeParameterClass<>(); @Persilee String text = (@Persilee String)new Object();
@Documented 註解表示使用了指定的註解,將使用 Javadoc 工具記錄這些元素。
@Inherited 註解表示註解類型能夠從超類繼承。
@Repeatable 註解代表標記的註解能夠屢次應用於同一聲明或類型使用。
根據 @Retention
元註解定義的存儲方式,註解通常可使用在如下3種場景中,如:
級別 | 技術 | 說明 |
---|---|---|
源碼 | APT | 在編譯期能獲取註解與註解聲明的類和類中全部成員信息,通常用於生成額外的輔助類。 |
字節碼 | 字節碼加強 | 在編譯出Class後,經過修改Class數據以實現修改代碼邏輯目的,對因而否須要修改的區分或者修改成不一樣邏輯的判斷可使用註解。 |
運行時 | 反射 | 在程序運行時,經過反射技術動態獲取註解與其元素,從而完成不一樣的邏輯判斷。 |
咱們定義一個 weekDay
字段,類型是 WeekDay
枚舉類型,方便咱們設置枚舉中指定的值,如:
class WeekDayDemo { private static WeekDay weekDay; enum WeekDay { SATURDAY,SUNDAY } public static WeekDay getWeekDay() { return weekDay; } public static void setWeekDay(WeekDay weekDay) { WeekDayDemo.weekDay = weekDay; } public static void main(String[] args) { setWeekDay(WeekDay.SATURDAY); System.out.println(getWeekDay()); } }
衆所周知,在 Java 中枚舉的實質是特殊的靜態成員變量,在運行時候,全部的枚舉會做爲單例加載到內存中,很是消耗內存,那麼,有沒有什麼優化的方案呢,在此,咱們使用註解來取代枚舉。
咱們使用常量和 @intDef
(語法檢查)元註解去代替枚舉,如:
class IntdefDemo { private static final int SATURDAY = 0; private static final int SUNDAY = 1; private static int weekDay; @IntDef({SATURDAY, SUNDAY}) @Target({ElementType.FIELD, ElementType.PARAMETER}) @Retention(RetentionPolicy.SOURCE) @interface WeekDay { //自定義一個 WeekDay 註解 } public static void setWeekDay(@WeekDay int weekDay) { // 使用 WeekDay 註解限制參數類型 IntdefDemo.weekDay = weekDay; } public static void main(String[] args) { setWeekDay(SATURDAY); // 只能 傳入 SATURDAY, SUNDAY } }
APT(Annotation Processor Tools) 註解處理器,用於處理註解,編寫好的 Java 文件,須要通過 Javac 的編譯,編譯爲虛擬機可以加載的字節碼(Class)文件,註解處理器是 Javac 自帶的一個工具,用來在編譯時期處理註解信息。
上文中咱們已自定義好了 @Persilee
註解,下面咱們來編寫一個簡單的註解處理器來處理 @Persilee
註解,咱們能夠新建一個 Java 的 Module,建立一個 PersileeProcessor
的類,如:
@SupportedAnnotationTypes("net.lishaoy.anreprdemo.Persilee") //指定要處理的註解 public class PersileeProcessor extends AbstractProcessor { @Override public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) { Messager messager = processingEnv.getMessager(); // messager.printMessage(Diagnostic.Kind.NOTE, "APT working ..."); for (TypeElement typeElement: set) { messager.printMessage(Diagnostic.Kind.NOTE,"===>" + typeElement.getQualifiedName()); Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(typeElement); for (Element element: elements) { messager.printMessage(Diagnostic.Kind.NOTE,"===>" + element.getSimpleName()); } } return false; } }
而後,在 main
目錄下新建 resources
目錄,如圖:
這個目錄結構是規定死的,必須這樣寫,而後在 javax.annotation.processing.Processor
文件裏註冊須要處理的註解處理器,如
net.lishaoy.aptlib.PersileeProcessor
最後,在 app
的 build.gradle
文件引入模塊,如
dependencies { ... annotationProcessor project(':aptlib') }
在你 Build 工程時候,會在 Task :app:compileDebugJavaWithJavac
任務打印咱們在註解處理程序的日誌信息,如:
注: APT working ... 注: ===>net.lishaoy.anreprdemo.Persilee 注: ===>MainActivity
由於,咱們只在 MainActivity
中使用了 @Persilee
註解,以下:
@Persilee(id = 666, value = "lsy") public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } }
通常狀況下,咱們使用某個類時一定知道它是什麼類,用來作什麼的。因而咱們直接對這個類進行實例化,以後使用這個類對象進行操做。
Cook cook = new Cook(); // 實例化一個對象,標準用法 cook.cookService("🍅");
反射是一開始並不知道初始化的類對象是什麼,也不能使用 new
關鍵字來建立對象,反射是在運行的時才知道要操做的類是什麼,而且能夠在運行時獲取類的完整構造,調用對應的方法。
Java 反射機制主要提供瞭如下功能:
Class是一個類,封裝了當前對象所對應的類的信息,咱們寫的每個類均可以當作一個對象,是 java.lang.Class 類的對象,Class是用來描述類的類。
Class對象的獲取有3種方式,以下:
Cook cook = new Cook(); Class cookClass = Cook.class; Class cookClass1 = cook.getClass(); Class cookClass2 = Class.forName("net.lishaoy.reflectdemo.Cook");
咱們能夠經過反射來生成對象的實例,如:
Class cookClass = Cook.class; Cook cook1 = (Cook) cookClass.newInstance();
獲取構造器的方法有,以下:
咱們來新建一個 Person
,以便咱們的演示,如:
public class Person { public String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } public Person() { super(); } public String getName() { System.out.println("get name: " + name); return name; } public void setName(String name) { this.name = name; System.out.println("set name: " + this.name); } public int getAge() { System.out.println("get age: " + age); return age; } public void setAge(int age) { this.age = age; System.out.println("set age: " + this.age); } private void privateMethod(){ System.out.println("the private method!"); } }
很常規的一個類,裏面有私有的屬性和方法。
下面,咱們新建一個 GetConstructor
的類來演示,獲取構造器方法如何使用,如:
class GetConstructor { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { String className = "net.lishaoy.reflectdemo.entity.Person"; Class<Person> personClass = (Class<Person>) Class.forName(className); //獲取所有的constructor對象 Constructor<?>[] constructors = personClass.getConstructors(); for (Constructor<?> constructor: constructors) { System.out.println("獲取所有的constructor對象: " + constructor); } //獲取某一個constructor對象 Constructor<Person> constructor = personClass.getConstructor(String.class, int.class); System.out.println("獲取某一個constructor對象: " + constructor); //調用構造器的 newInstance() 方法建立對象 Person person = constructor.newInstance("lsy", 66); System.out.println(person.getName() + ", " + person.getAge() ); } }
輸出結果,以下:
獲取所有的constructor對象: public net.lishaoy.reflectdemo.entity.Person(java.lang.String,int) 獲取所有的constructor對象: public net.lishaoy.reflectdemo.entity.Person() 獲取某一個constructor對象: public net.lishaoy.reflectdemo.entity.Person(java.lang.String,int) lsy, 66
獲取方法的方法有,以下:
咱們新建立一個 GetMethod
來演示如何來獲取和調用方法,如:
class GetMethod { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException { Class<?> aClass = Class.forName("net.lishaoy.reflectdemo.entity.Person"); //獲取全部的public方法(包含從父類繼承的方法) Method[] methods = aClass.getMethods(); for (Method method: methods) { System.out.println("獲取全部public方法: " + method.getName() + "()"); } System.out.println("==========================="); //獲取全部方法(不包含父類方法) methods = aClass.getDeclaredMethods(); for (Method method: methods) { System.out.println("獲取全部方法: " + method.getName() + "()"); } System.out.println("==========================="); //獲取指定的方法 Method method = aClass.getDeclaredMethod("setAge", int.class); System.out.println("獲取指定的方法:" + method); //調用方法 Object instance = aClass.newInstance(); method.invoke(instance, 66); //調用私有方法 method = aClass.getDeclaredMethod("privateMethod"); method.setAccessible(true); // 須要調用此方法且設置爲 true method.invoke(instance); } }
運行結果,以下:
獲取全部public方法: getName() 獲取全部public方法: setName() 獲取全部public方法: setAge() 獲取全部public方法: getAge() 獲取全部public方法: wait() 獲取全部public方法: wait() 獲取全部public方法: wait() 獲取全部public方法: equals() 獲取全部public方法: toString() 獲取全部public方法: hashCode() 獲取全部public方法: getClass() 獲取全部public方法: notify() 獲取全部public方法: notifyAll() =========================== 獲取全部方法: getName() 獲取全部方法: setName() 獲取全部方法: setAge() 獲取全部方法: privateMethod() 獲取全部方法: getAge() =========================== 獲取指定的方法:public void net.lishaoy.reflectdemo.entity.Person.setAge(int) set age: 66 the private method! BUILD SUCCESSFUL in 395ms
獲取成員變量的方法有,以下:
咱們再來新建一個 GetField
的類來演示如何獲取成員變量,以下:
class GetField { public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, InstantiationException { Class<?> aClass = Class.forName("net.lishaoy.reflectdemo.entity.Person"); // 獲取全部字段(不包含父類字段) Field[] fields = aClass.getDeclaredFields(); for (Field field: fields) { System.out.println("獲取全部字段: " + field.getName()); } System.out.println("================"); // 獲取指定字段 Field name = aClass.getDeclaredField("name"); System.out.println("獲取指定字段: " + name.getName()); // 設置指定字段的值 Object instance = aClass.newInstance(); name.set(instance, "per"); // 獲取指定字段的值 Object o = name.get(instance); System.out.println("獲取指定字段的值: " + o); // 設置和獲取私有字段的值 Field age = aClass.getDeclaredField("age"); age.setAccessible(true); // 須要調用此方法且設置爲 true age.set(instance, 66); System.out.println("獲取私有字段的值: " + age.get(instance)); } }
運行結果,以下:
獲取全部字段: name 獲取全部字段: age ================ 獲取指定字段: name 獲取指定字段的值: per 獲取私有字段的值: 66 BUILD SUCCESSFUL in 395ms
咱們已經對註解和反射有了更清晰的認知,下面咱們經過一個小案例來鞏固咱們的學習:使用註解和反射完成相似 butterknife
的自動 findViewById
的功能。
新建一個空的 Android 工程,在工程目錄下新建 inject 目錄,在此目錄下新建一個 InjectView
的類和 BindView
的自定義註解,如:
InjectView
類經過反射完成 findViewById
功能:
public class InjectView { public static void init(Activity activity) { // 獲取 activity 的 class 對象 Class<? extends Activity> aClass = activity.getClass(); // 獲取 activity 的因此成員變量 Field[] declaredFields = aClass.getDeclaredFields(); // 變量因此成員變量 for (Field field: declaredFields) { // 判斷屬性是否加上了 @BindView 註解 if(field.isAnnotationPresent(BindView.class)){ // 獲取註解 BindView 對象 BindView bindView = field.getAnnotation(BindView.class); // 獲取註解類型元素 id int id = bindView.value(); // 經過資源 id 找到對應的 view View view = activity.findViewById(id); // 設置能夠訪問私有字段 field.setAccessible(true); try { // 給字段賦值 field.set(activity,view); } catch (IllegalAccessException e) { e.printStackTrace(); } } } } }
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface BindView { @IdRes int value(); // @IdRes 只能傳 id 資源 }
MainActivity
裏使用 @BindView
註解,如:
public class MainActivity extends AppCompatActivity { // 使用註解 @BindView(R.id.text_view) private TextView textView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 初始化 InjectView,完成自動 findViewById 功能 InjectView.init(this); // 測試 R.id.text_view 是否自動賦值給 textView textView.setText("經過 @BindView 註解自動完成 findViewById"); } }
運行結果,如圖:
是否是很簡單,一個類就完成了自動 findViewById
的功能。
在瞭解動態代理以前,咱們先來回顧下靜態代理。
代理模式給某一個對象提供一個代理對象,並由代理對象控制對原對象的引用,如,咱們生活中常見的中介。
代理模式通常會有3個角色,如圖:
場景以下:
小明能夠在某網站上購買國內的東西,可是,不能買海外的東西,因而,他找了海外代購幫他買東西。
如何用代碼描述呢?根據代理模式的3個角色,咱們分別定義1個接口2個類,如:OrderService
接口(抽象角色)、ImplJapanOrderService
類(真實角色)、ProxyJapanOrder
類(代理角色)
OrderService
接口(抽象角色),代碼以下:
public interface OrderService { int saveOrder(); }
ImplJapanOrderService
類(真實角色),代碼以下:
// 實現抽象角色接口 public class ImplJapanOrderService implements OrderService { @Override public int saveOrder() { System.out.println("下單成功,訂單號爲:888888"); return 888888; } }
ProxyJapanOrder
類(代理角色),代碼以下:
// 實現抽象角色接口 public class ProxyJapanOrder implements OrderService { private OrderService orderService; // 持有真實角色 public OrderService getOrderService() { return orderService; } public void setOrderService(OrderService orderService) { this.orderService = orderService; } @Override public int saveOrder() { System.out.print("日本代購訂單,"); return orderService.saveOrder(); // 調用真實角色的行爲方法 } }
在建立一個 Client
類來測試咱們的代碼,以下:
public class Client { public static void main(String[] args) { // 日本代購訂單 OrderService orderJapan = new ImplJapanOrderService(); ProxyJapanOrder proxyJapanOrder = new ProxyJapanOrder(); proxyJapanOrder.setOrderService(orderJapan); proxyJapanOrder.saveOrder(); } }
運行結果,以下:
日本代購訂單,下單成功,訂單號爲:888888 BUILD SUCCESSFUL in 1s
若是,須要購買韓國的東西,須要新增一個 ImplKoreaOrderService
類(韓國服務商) 和 ProxyKoreaOrder
類(韓國代理),如還須要購買其餘國家的東西,須要新增不一樣的類,則會出現靜態代理對象量多、代碼量大,從而致使代碼複雜,可維護性差的問題,如是,咱們須要使用動態代理。
動態代理是在運行時才建立代理類和其實例,所以,咱們能夠傳不一樣的真實角色,實現一個代理類完成多個真實角色的行爲方法,固然,其效率比靜態代理低。那麼如何實現動態代理呢,JDK已爲咱們提供了 Proxy
類 和 InvocationHandler
接口來完成這件事情。
咱們來建立一個 ProxyDynamicOrder
類(動態代理類),代碼以下:
public class ProxyDynamicOrder implements InvocationHandler { private Object orderService; // 持有真實角色 public Object getOrderService() { return orderService; } public void setOrderService(Object orderService) { this.orderService = orderService; } // 經過 Proxy 動態建立真實角色 public Object getProxyInstance(){ return Proxy.newProxyInstance( orderService.getClass().getClassLoader(), orderService.getClass().getInterfaces(), this ); } @Override public Object invoke(Object o, Method method, Object[] objects) throws Throwable { return method.invoke(orderService, objects); // 經過反射執行真實角色的行爲方法 } }
在來看看,Client
類裏如何調用,代碼以下:
public class Client { public static void main(String[] args) { // 靜態代理模式 // 國內訂單 OrderService order = new ImplOrderService(); order.saveOrder(); // 日本代購訂單 OrderService orderJapan = new ImplJapanOrderService(); ProxyJapanOrder proxyJapanOrder = new ProxyJapanOrder(); proxyJapanOrder.setOrderService(orderJapan); proxyJapanOrder.saveOrder(); // 韓國代購訂單 OrderService orderKorea = new ImplKoreaOrderService(); ProxyKoreaOrder proxyKoreaOrder = new ProxyKoreaOrder(); proxyKoreaOrder.setOrderService(orderKorea); proxyKoreaOrder.saveOrder(); // 動態代理模式 // 國內訂單 ProxyDynamicOrder proxyDynamicOrder = new ProxyDynamicOrder(); OrderService orderService = new ImplOrderService(); proxyDynamicOrder.setOrderService(orderService); OrderService orderService1 = (OrderService) proxyDynamicOrder.getProxyInstance(); orderService1.saveOrder(); // 日本代購訂單 OrderService japanOrderService = new ImplJapanOrderService(); proxyDynamicOrder.setOrderService(japanOrderService); OrderService japanOrderService1 = (OrderService) proxyDynamicOrder.getProxyInstance(); japanOrderService1.saveOrder(); // 韓國代購訂單 OrderService koreaOrderService = new ImplKoreaOrderService(); proxyDynamicOrder.setOrderService(koreaOrderService); OrderService koreaOrderService1 = (OrderService) proxyDynamicOrder.getProxyInstance(); koreaOrderService1.saveOrder(); // 生成動態代理生成的class文件 //ProxyUtil.generateClassFile(koreaOrderService.getClass(), koreaOrderService1.getClass().getSimpleName()); } }
運行結果,以下:
下單成功,訂單號爲:666666 日本代購訂單,下單成功,訂單號爲:888888 韓國代購訂單,下單成功,訂單號爲:666888 下單成功,訂單號爲:666666 下單成功,訂單號爲:888888 下單成功,訂單號爲:666888 BUILD SUCCESSFUL in 1s
只須要一個 ProxyDynamicOrder
代理類便可完成 ImplOrderService
、 ImplJapanOrderService
、ImplKoreaOrderService
真實角色提供的服務。
咱們在 proxyDynamicOrder.getProxyInstance()
代碼上打個斷點,經過調試模式發現,如圖:
代理類的名字是 $Proxy0@507
,爲何是這個名字,咱們在編譯後的目錄裏也找不到 $Proxy0@507
類文件,如圖:
咱們經過查看 Proxy.newProxyInstance
方法源碼,可知,如:
@CallerSensitive public static Object newProxyInstance(ClassLoader var0, Class<?>[] var1, InvocationHandler var2) throws IllegalArgumentException { Objects.requireNonNull(var2); Class[] var3 = (Class[])var1.clone(); SecurityManager var4 = System.getSecurityManager(); if (var4 != null) { checkProxyAccess(Reflection.getCallerClass(), var0, var3); } // 獲取代理類的 class 對象 Class var5 = getProxyClass0(var0, var3); try { if (var4 != null) { checkNewProxyPermission(Reflection.getCallerClass(), var5); } // 獲取代理類的構造器 final Constructor var6 = var5.getConstructor(constructorParams); if (!Modifier.isPublic(var5.getModifiers())) { AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { var6.setAccessible(true); return null; } }); } // 建立代理類的示例 return var6.newInstance(var2); } catch (InstantiationException | IllegalAccessException var8) { throw new InternalError(var8.toString(), var8); } catch (InvocationTargetException var9) { Throwable var7 = var9.getCause(); if (var7 instanceof RuntimeException) { throw (RuntimeException)var7; } else { throw new InternalError(var7.toString(), var7); } } catch (NoSuchMethodException var10) { throw new InternalError(var10.toString(), var10); } }
而後,跟進 getProxyClass0(var0, var3)
看看是如何獲取代理類的 class 對象的,點擊進入,以下:
private static Class<?> getProxyClass0(ClassLoader var0, Class<?>... var1) { if (var1.length > 65535) { throw new IllegalArgumentException("interface limit exceeded"); } else { // 緩存了代理類的 class 對象 return (Class)proxyClassCache.get(var0, var1); } }
而後,咱們來看看這個 var1
是個什麼東西,咱們往上找了找,果真發現,以下:
// var1 就是咱們實現的 InvocationHandler 接口 protected Proxy(InvocationHandler var1) { Objects.requireNonNull(var1); this.h = var1; }
而後,咱們點進 proxyClassCache.get(var0, var1)
方法,如圖:
使用關鍵代碼 this.subKeyFactory.apply(var1, var2)
去獲取咱們的代理類的 class 對象,咱們進入 apply
實現類 ProxyClassFactory
,如:
public Class<?> apply(ClassLoader var1, Class<?>[] var2) { IdentityHashMap var3 = new IdentityHashMap(var2.length); Class[] var4 = var2; int var5 = var2.length; ... if (var16 == null) { var16 = "com.sun.proxy."; } long var19 = nextUniqueNumber.getAndIncrement(); // 生成代理類的類名 String var23 = var16 + "$Proxy" + var19; // 生成代理類的字節碼 byte[] var22 = ProxyGenerator.generateProxyClass(var23, var2, var17); try { // 生成代理類的 class 對象 return Proxy.defineClass0(var1, var23, var22, 0, var22.length); } catch (ClassFormatError var14) { throw new IllegalArgumentException(var14.toString()); } }
而後,咱們點進 Proxy.defineClass0
方法,以下:
private static native Class<?> defineClass0(ClassLoader var0, String var1, byte[] var2, int var3, int var4);
是一個 native
方法,因此涉及到 C 或 C++ ,咱們就不日後追蹤。
那麼,代理的 Class 文件到底存在哪兒呢,由一個類的生命週期,如圖:
代理的 Class 文件經過反射存在內存中,因此咱們能夠經過 byte[]
寫入文件,咱們新建一個工具類來把內存中的 class 字節碼寫入文件,如:
public class ProxyUtil { public static void generateClassFile(Class aClass, String proxyName) { byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, new Class[]{aClass} ); String path = aClass.getResource(".").getPath(); System.out.println(path); FileOutputStream outputStream = null; try { outputStream = new FileOutputStream(path + proxyName + ".class"); outputStream.write(proxyClassFile); outputStream.flush(); } catch (IOException e) { e.printStackTrace(); } finally { try { outputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } }
經過輸出的 path
路徑,找到文件,如:
/Users/lishaoying/Documents/APP/Android/practice/annotation_reflect/anRePrDemo/proxyDemo/build/classes/java/main/net/lishaoy/proxydemo/service/impl/
文件代碼,以下:
// 繼承了 Proxy 實現了 ImplKoreaOrderService 接口 public final class $Proxy0 extends Proxy implements ImplKoreaOrderService { // 生成了各類方法 private static Method m1; private static Method m8; private static Method m3; private static Method m2; private static Method m5; private static Method m4; private static Method m7; private static Method m9; private static Method m0; private static Method m6; public $Proxy0(InvocationHandler var1) throws { super(var1); } ... // 生成了 真實角色的 saveOrder 方法 public final int saveOrder() throws { try { // h 是什?,點進去發現就是咱們 傳入的 InvocationHandler 接口 // m3 是什麼? 下面 static 代碼塊,就是咱們的 saveOrder 方法 return (Integer)super.h.invoke(this, m3, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } ... public final Class getClass() throws { try { return (Class)super.h.invoke(this, m7, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } ... static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object")); m8 = Class.forName("net.lishaoy.proxydemo.service.impl.ImplKoreaOrderService").getMethod("notify"); m3 = Class.forName("net.lishaoy.proxydemo.service.impl.ImplKoreaOrderService").getMethod("saveOrder"); m2 = Class.forName("java.lang.Object").getMethod("toString"); m5 = Class.forName("net.lishaoy.proxydemo.service.impl.ImplKoreaOrderService").getMethod("wait", Long.TYPE); m4 = Class.forName("net.lishaoy.proxydemo.service.impl.ImplKoreaOrderService").getMethod("wait", Long.TYPE, Integer.TYPE); m7 = Class.forName("net.lishaoy.proxydemo.service.impl.ImplKoreaOrderService").getMethod("getClass"); m9 = Class.forName("net.lishaoy.proxydemo.service.impl.ImplKoreaOrderService").getMethod("notifyAll"); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); m6 = Class.forName("net.lishaoy.proxydemo.service.impl.ImplKoreaOrderService").getMethod("wait"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } } }
因爲文章篇幅已經很長,且使用註解、反射、動態代理完成簡單的 Retrofit 的案例代碼過多,因此就再也不這裏展現,感興趣的小夥伴能夠去 GitHub 查看源碼。
最後附上博客和GitHub地址,以下:
博客地址:https://h.lishaoy.net
GitHub地址:https://github.com/persilee