JAVA設計模式——代理(動態代理)
傳送門:JAVA設計模式——代理(靜態代理)
序言:
在學習Spring的時候,我們知道Spring主要有兩大思想,一個是IoC,另一個就是AOP,對於IoC,依賴註入就不用多說了,而對於Spring的核心AOP來說,我們不但要知道怎麽通過AOP來滿足的我們的功能,我們更需要學習的是其底層是怎麽樣的一個原理,而AOP的原理就是java的動態代理機制,所以本篇隨筆就是對java的動態機制進行一個回顧。
動態代理模式主要由四個元素共同構成:
1. 接口,接口中的方法是要真正去實現的
2. 被代理類,實現上述接口,這是真正去執行接口中方法的類
3. InvocationHandler接口的實現,幫助被代理類去實現方法
4. 代理類(Proxy)
一、InvocationHandler
在java的動態代理機制中,有兩個重要的類或接口,一個是 InvocationHandler(Interface)、另一個則是 Proxy(Class),這一個類和接口是實現我們動態代理所必須用到的。
每一個動態代理類都必須要實現InvocationHandler這個接口,並且每個代理類的實例都關聯到了一個handler,當我們通過代理對象調用一個方法的時候,這個方法的調用就會被轉發為由InvocationHandler這個接口的 invoke 方法來進行調用。
我們來看看InvocationHandler這個接口的唯一一個方法 invoke
Object invoke(Object proxy, Method method, Object[] args) throws Throwable
我們看到這個方法一共接受三個參數,那麽這三個參數分別代表什麽呢?
- proxy: 指代我們所代理的那個真實對象
- method: 指代的是我們所要調用真實對象的某個方法的Method對象
- args: 指代的是調用真實對象某個方法時接受的參數
如果不是很明白,等下通過一個實例會對這幾個參數進行更深的講解。
二、Proxy類:
Proxy這個類的作用就是用來動態創建一個代理對象的類,它提供了許多的方法,但是我們用的最多的就是 newProxyInstance 這個方法:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
這個方法的作用就是得到一個動態的代理對象,其接收三個參數,我們來看看這三個參數所代表的含義:
- loader: 一個ClassLoader對象,定義了由哪個ClassLoader對象來對生成的代理對象進行加載
- interfaces: 一個Interface對象的數組,表示的是我將要給我需要代理的對象提供一組什麽接口,如果我提供了一組接口給它,那麽這個代理對象就宣稱實現了該接口(多態),這樣我就能調用這組接口中的方法了
- h: 一個InvocationHandler對象,表示的是當我這個動態代理對象在調用方法的時候,會關聯到哪一個InvocationHandler對象上
三、例子:
動態代理:就是實現階段不用關系代理是哪個,而在運行階段指定具體哪個代理。
抽象接口的類圖如下:
--圖來自設計模式之禪
所以動態代理模式要有一個InvocationHandler接口 和 InvocationHandler的實現類GamePlayerIH。其中 InvocationHandler是JD提供的動態代理接口,對被代理類的方法進行代理。
代碼實現如下
抽象主題類或者接口:
package com.yemaozi.proxy.dynamic; /* * 動態代理:就是實現階段不用關系代理是哪個,而在運行階段指定具體哪個代理。 */ public interface IGamePlayer { //登錄遊戲 public void login(String username, String password); //擊殺Boss public void killBoss(); //升級 public void upGrade(); }
被代理類:
package com.yemaozi.proxy.dynamic; public class GamePlayer implements IGamePlayer { private String name = ""; public GamePlayer(String name){ this.name = name; } public void login(String username, String password) { System.out.println("登錄名為 "+username+" 進入遊戲," + name + " 登錄 成功!"); } public void killBoss() { System.out.println(this.name + " 擊殺了Boss!"); } public void upGrade() { System.out.println(this.name + "升級了!"); } }
動態代理處理器類:
package com.yemaozi.proxy.dynamic; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class GamePlayerIHr implements InvocationHandler{ //被代理的對象 private Object obj; //將需要代理的實例通過處理器類的構造方法傳遞給代理。 public GamePlayerIHr (Object obj){ this.obj = obj; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = null; if("login".equalsIgnoreCase(method.getName())){ //這個方法不受任何影響的情況下,在方法前後添加新的功能,或者增強方法, //從側面切入從而達到擴展的效果的編程,就是面向切面編程(AOP Aspect Oriented Programming)。 //AOP並不是新技術,而是相對於面向對象編程的一種新的編程思想。在日誌,事務,權限等方面使用較多。 System.out.println("代理登錄遊戲!"); result = method.invoke(this.obj, args); return result; } result = method.invoke(this.obj, args); return result; } }
由於代理是動態產生的,所以不需要再聲明代理類。
動態代理場景類:
package com.yemaozi.proxy.dynamic; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; public class Client { public static void main(String[] args) { IGamePlayer gp = new GamePlayer("張三"); InvocationHandler gpHandler = new GamePlayerIH(gp); //獲取主題類的類加載器ClassLoader ClassLoader classLoader = gp.getClass().getClassLoader(); //一個Interface對象的數組 Class<?>[] cls = new Class[]{IGamePlayer.class}; //動態產生一個代理者。 IGamePlayer proxyGp =(IGamePlayer)Proxy.newProxyInstance(classLoader, cls, gpHandler); proxyGp.login("zhangsan", "123456"); proxyGp.killBoss(); proxyGp.upGrade(); } } 執行結果: 代理登錄遊戲! 登錄名為 zhangsan 進入遊戲,張三 登錄成功! 張三 擊殺了Boss! 張三升級了! //在此,我們沒有創建代理類,但是確實有代理類幫我們完成事情。
四、動態代理的應用———AOP
在此代理模式中,不僅代理是動態產生的(即在運行的時候生成),而且還在代理的時候,也增加了一些處理。在此處增加的處理,其實就是另一種編程思想-----面向切面編程思想(AOP Aspect Oriented Programming)。
帶有AOP的動態代理模式類圖:
--圖來自設計模式之禪
從上圖中,可以看出有兩個相對獨立的模塊(Subject和InvocationHandler)。動態代理實現代理的職責,業務邏輯Subject實現相關的邏輯功能,兩者之間沒有必然的相互耦合的關系。然而,通知Advice從另一個切面切入,最終在上層模塊就是Client耦合,完成邏輯的封裝。
代碼清單如下
抽象主題或者接口:
package com.yemaozi.proxy.dynamic_aop; public interface Subject { public void doSomething(String str); //...可以多個邏輯處理方法。。。 }
主題的實現類:
package com.yemaozi.proxy.dynamic_aop; public class RealSubject implements Subject{ public void doSomething(String str) { //do something... System.out.println("do something..." + str); } }
AOP的通知接口:
package com.yemaozi.proxy.dynamic_aop; //通知接口及定義、 public interface IAdvice { public void exec(); }
前置通知
package com.yemaozi.proxy.dynamic_aop; public class BeforeAdvice implements IAdvice { //在被代理的方法前來執行,從而達到擴展功能。 public void exec() { System.out.println("前置通知被執行!"); } }
後置通知:
package com.yemaozi.proxy.dynamic_aop; public class AfterAdvice implements IAdvice { //在被代理的方法後來執行,從而達到擴展功能。 public void exec() { System.out.println("後置通知被執行!"); } }
動態代理的處理器類:
所有的方法通過invoke方法類實現。
package com.yemaozi.proxy.dynamic_aop; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class MyInvocationHandler implements InvocationHandler { //被代理的對象 private Subject realSubject; //通過MyInvocationHandler的構造方法將被代理對象傳遞過來。 public MyInvocationHandler(Subject realSubject){ this.realSubject = realSubject; } //執行被代理類的方法。 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //在執行方法前,執行前置通知。 IAdvice beforeAdvice = new BeforeAdvice(); beforeAdvice.exec(); Object result = method.invoke(this.realSubject, args); //在執行方法後,執行後置通知。 IAdvice afterAdvice = new AfterAdvice(); afterAdvice.exec(); //前置通知,和後置通知,都是要看具體實際的業務需求來進行添加。 return result; } }
動態代理類(Proxy)
package com.yemaozi.proxy.dynamic_aop; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; public class DynamicProxy { /** * public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler handler) * loader: * 一個ClassLoader對象,定義了由哪個ClassLoader對象,來對生產的代理進行加載。 * interfaces: * 一個Interfaces數組,表示我將要給我所代理的對象提供一組什麽樣的接口, * 如果提供一組接口給它,那麽該代理對象就宣稱實現了該接口,從而可以調用接口中的方法。 * 即,查找出真是主題類的所實現的所有的接口。 * handler: * 一個InvocationHandler對象,表示當我這個動態代理對象在調用方法時,會關聯到該InvocationHandler對象。 * 該InvocationHandler與主題類有著關聯。 */ public static <T> T newProxyInstance(ClassLoader classLoader, Class<?>[] interfaces, InvocationHandler handler){ @SuppressWarnings("unchecked") T t = (T) Proxy.newProxyInstance(classLoader, interfaces, handler); return t; } }
動態代理場景類:
package com.yemaozi.proxy.dynamic_aop; import java.lang.reflect.InvocationHandler; public class AOPClient { public static void main(String[] args) { Subject realSubject = new RealSubject(); InvocationHandler handler = new MyInvocationHandler(realSubject); ClassLoader classLoader = realSubject.getClass().getClassLoader(); Class<?>[] interfaces = realSubject.getClass().getInterfaces(); Subject proxySubect = DynamicProxy.newProxyInstance(classLoader, interfaces, handler); proxySubect.doSomething("這是一個Dynamic AOP示例!!!"); } } 執行結果: 前置通知被執行! do something...這是一個Dynamic AOP示例!!! 後置通知被執行!
動態代理中invoke的動態調用:
動態代理類DynamicProxy是個純粹的動態創建代理類通用類。
所以在具體業務中,可以在進一步封裝具體的具有業務邏輯意義的DynamicProxy類。
代碼如下
具體業務的動態代理:
package com.yemaozi.proxy.dynamic_aop; import java.lang.reflect.InvocationHandler; //具體業務的動態代理。 public class SubjectDynamicProxy extends DynamicProxy { public static <T> T newProxyInstance(Subject subject){ ClassLoader classLoader = subject.getClass().getClassLoader(); Class<?>[] interfaces = subject.getClass().getInterfaces(); InvocationHandler handler = new MyInvocationHandler(subject); T t = newProxyInstance(classLoader, interfaces, handler); return t; } }
動態代理在現在用的是非常的多的,如像Spring AOP ,DBCP連接池,AspectJ等
JAVA設計模式——代理(動態代理)