1. 程式人生 > >java反射與動態代理的理解

java反射與動態代理的理解

system 對象的訪問 讓我 integer 打破 類屬性 全部 列表 ces

一、什麽是反射機制?

  反射的官方定義是這樣的:在運行狀態中,對於任意的一個類,都能夠知道這個類的所有屬性和方法,對任意一個對象都能夠通過反射機制調用一個類的任意方法,這種動態獲取類信息及動態調用類對象方法的功能稱為java的反射機制。  

  講的通俗一點的話就是,對於jvm來說,.java文件必須要先編譯為.class文件才能夠被jvm執行,所以在編譯為.class文件的過程中,對象的類型都會被指定好,比如說 User user。那麽如果說我想在代碼運行的過程中獲取到對象的類型呢?或者說程序在運行過程中如何載入一個特定的類呢?這就涉及到了java的反射機制了,反射提供了一套能夠讓我們在代碼運行時也能獲取到類型屬性的方法。

二、反射的使用

  jdk提供了三種方式獲取一個對象的Class,就User user來說

  1.user.getClass(),這個是Object類裏面的方法

  2.User.Class屬性,任何的數據類型,基本數據類型或者抽象數據類型,都可以通過這種方式獲取類

  3.Class.forName(""),Class類提供了這樣一個方法,讓我們通過類名來獲取到對象類

  這三種方法用的最多的就是第三種,那麽獲取到類之後,Class類提供了很多獲取類屬性,方法,構造方法的api。

  1.獲取所有的屬性

//獲取整個類  
            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反射機制,動態的生成了一個代理類,直接調用代理方法即可。

四、總結

  反射這一塊是博主最近在看框架源碼的時候總是遇到這類問題所以刻意去整理了一下,裏面有些東西其實我也研究的不是很深刻,動態代理這邊的源碼還有待深入挖掘,若有大神發現寫的不對的地方,還請多多指教,謝謝。

java反射與動態代理的理解