反射的官方定義是這樣的:在運行狀態中,對於任意的一個類,都可以知道這個類的全部屬性和方法,對任意一個對象都可以經過反射機制調用一個類的任意方法,這種動態獲取類信息及動態調用類對象方法的功能稱爲java的反射機制。 java
講的通俗一點的話就是,對於jvm來講,.java文件必需要先編譯爲.class文件纔可以被jvm執行,因此在編譯爲.class文件的過程當中,對象的類型都會被指定好,好比說 User user。那麼若是說我想在代碼運行的過程當中獲取到對象的類型呢?或者說程序在運行過程當中如何載入一個特定的類呢?這就涉及到了java的反射機制了,反射提供了一套可以讓咱們在代碼運行時也能獲取到類型屬性的方法。api
jdk提供了三種方式獲取一個對象的Class,就User user來講安全
1.user.getClass(),這個是Object類裏面的方法app
2.User.Class屬性,任何的數據類型,基本數據類型或者抽象數據類型,均可以經過這種方式獲取類框架
3.Class.forName(""),Class類提供了這樣一個方法,讓咱們經過類名來獲取到對象類jvm
這三種方法用的最多的就是第三種,那麼獲取到類以後,Class類提供了不少獲取類屬性,方法,構造方法的api。函數
1.獲取全部的屬性spa
//獲取整個類 Class c = Class.forName("java.lang.Integer"); //獲取全部的屬性? Field[] fs = c.getDeclaredFields(); //定義可變長的字符串,用來存儲屬性 StringBuffer sb = new StringBuffer(); //經過追加的方法,將每一個屬性拼接到此字符串中 //最外邊的public定義 sb.append(Modifier.toString(c.getModifiers()) + " class " + c.getSimpleName() +"{\n"); //裏邊的每個屬性 for(Field field:fs){ sb.append("\t");//空格 sb.append(Modifier.toString(field.getModifiers())+" ");//得到屬性的修飾符,例如public,static等等 sb.append(field.getType().getSimpleName() + " ");//屬性的類型的名字 sb.append(field.getName()+";\n");//屬性的名字+回車 } sb.append("}"); System.out.println(sb);
2.獲取特定的屬性代理
//獲取類 Class c = Class.forName("User"); //獲取id屬性 Field idF = c.getDeclaredField("id"); //實例化這個類賦給o Object o = c.newInstance(); //打破封裝 idF.setAccessible(true); //使用反射機制能夠打破封裝性,致使了java對象的屬性不安全。 //給o對象的id屬性賦值"110" idF.set(o, "110"); //set //get System.out.println(idF.get(o));
3.獲取方法日誌
getDeclaredMethods() 獲取全部的方法 getReturnType() 得到方法的放回類型 getParameterTypes() 得到方法的傳入參數類型 getDeclaredMethod("方法名",參數類型.class,……) 得到特定的方法 getDeclaredConstructors() 獲取全部的構造方法 getDeclaredConstructor(參數類型.class,……) 獲取特定的構造方法 getSuperclass() 獲取某類的父類 getInterfaces() 獲取某類實現的接口
反射做用總結就是:1.動態地建立類的實例,將類綁定到現有的對象中,或從現有的對象中獲取類型。2.應用程序須要在運行時從某個特定的程序集中載入一個特定的類。
那麼什麼是動態代理呢?先給出百度的答案:動態代理,就是根據對象在內存中加載的Class類建立運行時類對象,從而調用代理類方法和屬性。
代理模式的定義:爲其餘對象提供一種代理以控制對這個對象的訪問。在某些狀況下,一個對象不適合或者不能直接引用另外一個對象,而代理對象能夠在客戶端和目標對象之間起到中介的做用。而代理模式又分爲靜態代理和動態代理,先說靜態代理。
靜態代理通俗點將就是本身手寫一個代理類,而動態代理則不用咱們手寫,而是依賴於java反射機制,下面以一個demo舉例。
demo結構如圖:
Subject是一個普通接口,裏面有個抽象的doSomething()的方法,而SubjectImpl.java是它的普通的實現類,以下所示。
而HandProxy.java就是SubjectImpl的靜態代理類,代替了SubjectImpl完成doSomething的工做,以下所示
這樣作的好處是對於doSomething方法來講,我用靜態代理類來實現,能夠任意的在其中插入我須要額外作的事情,好比說記錄日誌。
那麼AutoProxy.java就是動態代理類了,具體以下所示。
這裏面首先想要作到動態代理,必須先實現這個InvocationHandler接口,而後咱們主要看bind方法,參數tar是一個Object類型的對象,也就是須要被代理的對象SubjectImpl,
方法裏面有一個Proxy類,這個Proxy類提供了不少方法,這裏咱們用的是newProxyInstance方法,它有三個參數,第一個是被代理類的類構造器,第二個指的是被代理類的接口,也就是Subject接口,第三個是實現這個代理的類,這裏就是本類。具體的來講,這個方法執行了下面三步:
1.生成一個實現了參數interfaces裏全部接口且繼承了Proxy的代理類的字節碼,而後用參數裏的classLoader加載這個代理類。
2.使用代理類父類的構造函數 Proxy(InvocationHandler h)來創造一個代理類的實例,將咱們自定義的InvocationHandler的子類傳入。
3.返回這個代理類實例,由於咱們構造的代理類實現了interfaces(也就是咱們程序中傳入的subject.getClass().getInterfaces())裏的全部接口,所以返回的代理類能夠強轉成Subject類型來調用接口中定義的方法。
而在調用每一個代理類每一個方法的時候,都用反射去調h的invoke方法(也就是咱們自定義的InvocationHandler的子類中重寫的invoke方法),用參數傳遞了代理類實例、接口方法、調用參數列表,這樣咱們在重寫的invoke方法中就能夠實現對全部方法的統一包裝了。
總結一下,靜態代理的優勢是清晰易懂,可是若是說業務代碼不少,那麼在代理類裏面必須所有從新調用一遍,很麻煩。而動態代理,利用java反射機制,動態的生成了一個代理類,直接調用代理方法便可。
反射這一塊是博主最近在看框架源碼的時候老是遇到這類問題因此刻意去整理了一下,裏面有些東西其實我也研究的不是很深入,動態代理這邊的源碼還有待深刻挖掘,如有大神發現寫的不對的地方,還請多多指教,謝謝。