天天看點

java中動态代理實作機制

  代理模式是常用的java設計模式,它的特征是代理類與委托類有同樣的接口,代理類主要負責為委托類預處理消息、過濾消息、把消息轉發給委托類,以及事後處理消息等。代理類與委托類之間通常會存在關聯關系,一個代理類的對象與一個委托類的對象關聯,代理類的對象本身并不真正實作服務,而是通過調用委托類的對象的相關方法,來提供特定的服務。 

java中動态代理實作機制

  Java在JDK1.3後引入的動态代理機制,使我們可以在運作期動态的建立代理類。使用動态代理實作AOP需要有四個角色:被代理的類,被代理類的接口,織入器,和InvocationHandler,而織入器使用接口反射機制生成一個代理類,然後在這個代理類中織入代碼。被代理的類是AOP裡所說的目标,InvocationHandler是切面,它包含了Advice和Pointcut。 

java中動态代理實作機制
java中動态代理實作機制

  Enhancer和MethodInterceptor。Enhancer可以用來動态的生成一個類,這個類可以繼承指定的一個類,實作指定的一些接口。同時,Enhancer在生成一個類之前需要指定一個Callback,當類方法調用時,方法的執行被配置設定給這個Callback,MethodInterceptor是一個使用比較多的繼承自Callback的接口,它隻有一個方法聲明。

    從參數構成上,methodInterceptor的輸入參數比Invocationhandler多1個,其實前3個參數對象的含義與Invocationhandler的含義是相同的。

  第一個參數表示調用方法來自哪個對象;

  第二個參數表示調用方法的Method對象;

  第三個參數表示此次調用的輸入參數清單;

  多出來的參數是MethodProxy 類型的,它應該是cglib生成用來代替Method對象的一個對象,使用MethodProxy比調用JDK自身的Method直接執行方法效率會有提升。

  MethodInterceptor接口的實作

  建立代理類并調用代理類

   注意了,MethodProxy在對執行函數的時候,提供了2個方法

  其中,javadoc上說這個invoke()方法可以用于相同類中的其他對象的方法執行,也就是說這個方法中的obj需要傳入相同一個類的另一個對象(上述方法中就是傳入了Arithmetic類的不同對象),否則會進入無限遞歸循環(測試之後還真是出現了StackOverflowError)。仔細的想一想就會發現,public Object intercept(Object target, Method method, Object[] args, MethodProxy proxy)中的target是實作的代理類對象,通過target調用add()方法時會觸發intercept()方法被調用,如果在intercept()方法中再調用method.invoke(target, args),就相當于add()方法中又調用add()方法,導緻無限的遞歸循環。但是如果執行method.invoke(real, args)則不會,因為real和target是同一個類不同對象,real是真實邏輯主題,target是真實主題real的代理。

  下面一個例子來模拟一下:

  

  其實Method的invoke()方法會根據obj的類型去調用對應的solve()方法,也就是多态性。

  注意了,實作2中Enhancer 沒有設定接口,因為設定了Superclass了(也就是代理類的父類是Arithmetic),我們的代理類會繼承它,而Arithmetic已經實作了我們的接口。為了證明這一點,可以在MethodInterceptor的 intercept方法中列印 target.getClass()的類資訊,你會發現cglib的兩種方式代理類的父類是不同的。如下:

  實作1:

  實作2:

  Javassist是一個編輯位元組碼的架構,可以讓你很簡單地操作位元組碼。它可以在運作期定義或修改Class。使用Javassist實作AOP的原理是在位元組碼加載前直接修改需要切入的方法。這比使用Cglib實作AOP更加高效,并且沒太多限制,實作原理如下圖: 

java中動态代理實作機制
java中動态代理實作機制

接口的實作

建立代理類并調用代理類

  注意:MethodHandler接口中invoke方法的定義,如下:

  method代表調用方法的Method對象,proxy是代理類産生并代替method的對象,否則用method.invoke(target, args)會産生無限循環調用。

  (來自:http://cuishen.iteye.com/blog/421464),代碼注釋很詳細,仔細研究一下就會懂了!

  javassist使用動态java代碼常見代理過程和前文的方法略有不同。javassist内部可以通過動态java代碼,生成位元組碼。這種方式建立的動态代理可以非常靈活,甚至可以在運作時産生業務邏輯。

  列印一下動态實作接口的代碼如下: