上一篇講解了反射的知識[],做爲反射的入門級,而後這一篇主要也是講解動態代理的實現機制。java
動態代理包括「jdk的動態代理」和「cglib 的動態代理」,二者實現相同的功能,可是實現方式倒是有明顯的區別。程序員
下面咱們就經過代碼的方式層層的深刻這兩種動態代理,瞭解他們的性能以、底層的實現原理及應用場景。web
在詳細介紹動態代理以前,先來講說Java中的代理模式。代理模式分爲兩種:編程
代理模式定義:個人我的理解就是給某一個對象提供一個代理對象,在代理對象中擁有被代理對象的引用,並在代理對象中調用被代理對象的方法以前和以後進行方法的加強。設計模式
我這裏畫了一張代理模式的類圖,設計模式中的代理模式比較簡單,代理類和委託類有公共的接口,最後由代理類去執行委託類的方法:編輯器
代理模式就好像生活中的中介,去幫你作事,而不用比本身去作事。舉個例子,好比你要買車,可是買車以前你要處處找車源,找到車源給錢了還要辦理一堆手續。ide
(1)下面咱們以買車這個案例進行代理模式的代碼編寫,首先要有一個公共的接口Person,Person接口裏面定義公共的方法:函數
public interface Person{
void buyCar();
}
複製代碼
(2)而後定義一個委託類,也就是我本人Myself,並實現Person接口,具體代碼以下:工具
性能
public class Myself implements Person {複製代碼@Override public void buyCar() { System.out.println("我要買車了"); } 複製代碼} 複製代碼@Override public void buyCar() { System.out.println("我要買車了"); } 複製代碼
(3)最後就是建立代理類CarProxy,一樣也是實現Person接口,具體實現代碼以下:
public class CarProxy implements Person{複製代碼private Myself myself ; public CarProxy(final Myself myself ) { this.myself = myself ; } @Override public void buyCar() { System.out.println("買車前去找車源"); myself .buyCar(); System.out.println("買車後辦理手續"); } 複製代碼} 複製代碼private Myself myself ; public CarProxy(final Myself myself ) { this.myself = myself ; } @Override public void buyCar() { System.out.println("買車前去找車源"); myself .buyCar(); System.out.println("買車後辦理手續"); } 複製代碼
這個代理的demo很簡單,如上面的類圖所示,代理類和委託類都實現公共的接口Person,在委託類中進行方法的具體業務邏輯的實現,而代理類中再次對這個方法進行加強。
「代理模式」的優勢就是「可以對目標對象進行功能的擴展」,缺點是每個業務類都要建立一個代理類,這樣會「使咱們系統內的類的規模變得很大,不利於維護」。
因而就出現了動態代理,仔細思考靜態代理的缺點,就是一個委託類就會對象一個代理類,那麼是否能夠將代理類作成一個通用的呢?
咱們仔細來看一下下面的這個圖:
咱們把靜態代理全部的執行過程均可以抽象成這張圖的執行過程,Proxy角色無非是在「調用委託類處理業務的方法以前或者以後作一些額外的操做」。
那麼爲了作一個通用性的處理,就把調用委託類的method的動做抽出來,當作一個通用性的處理類,因而就有了InvocationHandler
角色,抽象成一個處理類。
這樣在Proxy和委託類之間就多了一個InvocationHandler
處理類的角色,這個角色主要是「將以前代理類調用委託類的方法的動做進行統一的調用,都由InvocationHandler來處理」。
因而以前上面的類圖就有了這樣的改變,在Proxy和委託類之間加入了InvocationHandler,具體的實現圖以下:
看完上面的圖彷佛有那麼一點點的理解,下面咱們就來詳細的深刻動態代理。
上面講解到動態代理是在運行時環境動態加載class文件,並建立對應的class對象,那麼動態代理着靜態代理的執行時機是在哪裏呢?
我這邊又畫了一張原理圖,感受我爲畫圖操碎了心,每個點都會畫一個想截圖,是否是很暖。
這個是靜態代理的運行原理圖,靜態代理在程序運行時就已經建立好了class文件,在程序啓動後的某一個時機(用到class文件)就會加載class文件到內存中。
當在運行時期動態生成class文件並加載class文件的運行原理圖以下:
在JVM運行期時遵循JVM字節碼的結構和規範生成二進制文件,並加載到內存中生成對應的Class對象。這樣,就完成了動態建立class文件和Class對象的功能了。
在jdk的動態代理中的「Proxy類和委託類要求實現相同的功能」,這裏的相同是指「他們均可以調用統一的邏輯業務方法」。要實現這樣的設計有如下三種方法:
在jdk的動態代理中,是採用第一種方法進行實現,必須有公共的接口,下面咱們仍是經過靜態代理的案例使用動態代理來實現。
(1)首先建立一個公共接口Person:
public interface Person{
void buyCar();
}
複製代碼
(2)而後建立接口的實現類Myself:
public class Myself implements Person {複製代碼@Override public void buyCar() { System.out.println("我要買車了"); } 複製代碼} 複製代碼@Override public void buyCar() { System.out.println("我要買車了"); } 複製代碼
(3)這一步就是比較關鍵的,要建立一個類並實現InvocationHandler
:
public class InvocationHandlerImpl implements InvocationHandler {複製代碼private Person person; public InvocationHandlerImpl(Person person){ this.person=person; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("買車前開始找車源。。。。"); method.invoke(person, args); System.out.println("買車後辦理手續。。。。"); return null; } 複製代碼} 複製代碼private Person person; public InvocationHandlerImpl(Person person){ this.person=person; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("買車前開始找車源。。。。"); method.invoke(person, args); System.out.println("買車後辦理手續。。。。"); return null; } 複製代碼
(4)最後一步就是進行測試:
public class Test {複製代碼public static void main(String[] args) { Myself myself= new Myself(); // 建立代理對象,這裏有三個參數,第一個是類的ClassLoader,第二個是該類的接口集合,第三個就是InvocationHandler Object o = Proxy.newProxyInstance(myself.getClass().getClassLoader(), myself.getClass().getInterfaces(), new InvocationHandlerImpl(myself)); Person person= (Person) o; person.buyCar(); } 複製代碼} 複製代碼public static void main(String[] args) { Myself myself= new Myself(); // 建立代理對象,這裏有三個參數,第一個是類的ClassLoader,第二個是該類的接口集合,第三個就是InvocationHandler Object o = Proxy.newProxyInstance(myself.getClass().getClassLoader(), myself.getClass().getInterfaces(), new InvocationHandlerImpl(myself)); Person person= (Person) o; person.buyCar(); } 複製代碼
總體來講jdk動態代理的應用過程仍是比較簡單的,重要的實現理解他的底層實現過程,它的重要實現步驟就是InvocationHandler
中 的invoke
方法處理。
invoke方法纔是實現方法的調用者,根據上面的參數最後纔會建立代理對象newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
。
那麼在實現jdk動態代理的過程都作了哪些工做呢?具體有如下6個步驟:
咱們能夠經過反編譯工具來看看生成的代理類的源碼是怎麼樣的,我這裏使用的反編譯工具是jd-gui
,推薦給你們。
public final class MyselfProxy extends Proxy implements Person { private static Method m1; private static Method m3; private static Method m0; private static Method m2;public MyselfProxy(InvocationHandler paramInvocationHandler) throws {
super(paramInvocationHandler);
}public final boolean equals(Object paramObject) throws {
try { // InvocationHandler 實現equals的調用 return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
} catch (Error|RuntimeException localError) {
throw localError;
} catch (Throwable localThrowable) {
throw new UndeclaredThrowableException(localThrowable);
}
}public final void buyCar() throws {
try {
// InvocationHandler實現buyCar的調用 this.h.invoke(this, m3, null);
return;
} catch (Error|RuntimeException localError) {
throw localError;
} catch (Throwable localThrowable) {
throw new UndeclaredThrowableException(localThrowable);
}
}public final int hashCode() throws {
try {
// InvocationHandler實現hashCode方法的調用 return ((Integer)this.h.invoke(this, m0, null)).intValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}public final String toString() throws {
複製代碼static {
try {
// InvocationHandler實現toString的調用 return (String)this.h.invoke(this, m2, null);
} catch (Error|RuntimeException localError) {
throw localError;
} catch (Throwable localThrowable) {
throw new UndeclaredThrowableException(localThrowable);
}
}
try { //在靜態塊中經過反射初始化函數 m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m3 = Class.forName("com.ldc.org.Person").getMethod("buyCar", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
return;
} catch (NoSuchMethodException localNoSuchMethodException) {
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
} catch (ClassNotFoundException localClassNotFoundException) {
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
} 複製代碼
從上面反編譯的源碼中能夠能夠看出,在「靜態塊中直接經過反射的方式來生成Method對象」,對方法的調用則是經過InvocationHandler
對象來進行調用。
仔細的總結能夠看出上面反編譯出來的代理類有如下特徵:
到這裏我想你們應該對jdk的動態代理有一個清晰的認識了,包括他的底層實現的原理,下面咱們就來詳細的瞭解cglib動態代理的是實現方式。
在實現jdk的動態代理的實現會發現,「jdk動態代理必須實現一個接口」,而且代理類也「只能代理接口中實現的方法」,要是實現類中有本身私有的方法,而接口中沒有的話,該方法不能進行代理調用。
基於這種狀況cglib
便出現了,他也能夠在運行期擴展Java類和Java接口。
cglib
底層是採用「字節碼技術」,其原理是經過字節碼技術生成一個子類,並在子類中攔截父類的方法的調用,織入業務邏輯。
由於原理是採用繼承的方式,因此被代理的類不能被final修飾,在Spring Aop中底層的實現是以這兩種動態代理做爲基礎進行實現。
當使用cglib動態代理一個類demo時,JVM又作了哪些工做呢?
(1)那麼咱們經過代碼也來實現cglib動態代理,仍是建立Myself類,可是此時不須要實現接口:
public class Myself {複製代碼@Override public void buyCar() { System.out.println("I'm going to buy a house"); } 複製代碼} 複製代碼@Override public void buyCar() { System.out.println("I'm going to buy a house"); } 複製代碼
(2)而後是建立MyMethodInterceptor
類實現MethodInterceptor
接口,這個和動態代理實現InvocationHandler
方式同樣,實現統一方法的調用。
public class MyMethodInterceptor implements MethodInterceptor { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("買車前開始找車源。。。。"); proxy.invokeSuper(obj, args); System.out.println("買車後辦理手續。。。。"); return null; } 複製代碼} 複製代碼
(3)最後是進行測試
public class Test {
public static void main(String[] args) {
Myself myself= new Myself();
MyMethodInterceptor myMethodInterceptor = new MyMethodInterceptor ();
//cglib 中增強器,用來建立動態代理
Enhancer enhancer = new Enhancer();
//設置要建立的代理類
enhancer.setSuperclass(myself.getClass());
// 設置回調,這裏至關因而對於代理類上全部方法的調用
enhancer.setCallback(myMethodInterceptor );
// 建立代理類
Programmer proxy =(Myself)enhancer.create();
proxy.buyCar();
}
}
複製代碼
總結來講cglib是一個強大的、高性能的Code生產類庫,在Spring中就是經過cglib方式繼承要被代理的類,重寫父類的方法,實現Aop編程。
cglib建立動態代理對象的性能時機要比jdk動態代理的方式高不少,可是建立對象所花的時間卻要比jdk動態代理方式多不少。
在應用方面單例模式更適合用cglib,無需頻繁的建立對象,相反,則使用jdk動態代理的方式更加合適。