代理模式是常用的java設計模式,他的特征是代理類與委托類有同樣的接口,代理類主要負責為委托類預處理消息、過濾消息、把消息轉發給委托類,以及事后處理消息等。代理類與委托類之間通常會存在關聯關系,一個代理類的對象與一個委托類的對象關聯,代理類的對象本身並不真正實現服務,而是通過調用委托類的對象的相關方法,來提供特定的服務。
按照代理的創建時期,代理類可以分為兩種。
靜態代理:由程序員創建或特定工具自動生成源代碼,再對其編譯。在程序運行前,代理類的.class文件就已經存在了。
動態代理:在程序運行時,運用反射機制動態創建而成。
動態代理:在程序運行時,運用反射機制動態創建而成。
靜態代理的每一個代理類只能為一個接口服務,這樣一來程序開發中必然會產生過多的代理,而且,所有的代理操作除了調用的方法不一樣之外,其他的操作都一樣,則此時肯定是重復代碼。解決這一問題最好的做法是可以通過一個代理類完成全部的代理功能,那么此時就必須使用動態代理完成。
動態代理
與靜態代理類對照的是動態代理類,動態代理類的字節碼在程序運行時由Java反射機制動態生成,無需程序員手工編寫它的源代碼。動態代理類不僅簡化了編程工作,而且提高了軟件系統的可擴展性,因為Java 反射機制可以生成任意類型的動態代理類。java.lang.reflect 包中的Proxy類和InvocationHandler 接口提供了生成動態代理類的能力。
與靜態代理類對照的是動態代理類,動態代理類的字節碼在程序運行時由Java反射機制動態生成,無需程序員手工編寫它的源代碼。動態代理類不僅簡化了編程工作,而且提高了軟件系統的可擴展性,因為Java 反射機制可以生成任意類型的動態代理類。java.lang.reflect 包中的Proxy類和InvocationHandler 接口提供了生成動態代理類的能力。
Spring AOP使用動態代理技術在運行期間織入增強的代碼,主要有兩種代理機制:基於JDK的動態代理;基於CGLib的動態代理。JDK本身只提供接口的代理,而不支持類的代理。
一.JDK動態代理
JDK動態代理主要涉及java.lang.reflect包下的兩個類:Proxy類和InvocationHandler接口。
InvocationHandler接口:
參數說明:
Object proxy:指被代理的對象。
Method method:要調用的方法
Object[] args:方法調用時所需要的參數
InvocationHandler接口:
public interface InvocationHandler { public Object invoke(Object proxy,Method method,Object[] args) throws Throwable; }
Object proxy:指被代理的對象。
Method method:要調用的方法
Object[] args:方法調用時所需要的參數
InvocationHandler接口的子類可以看成代理的最終操作類。
Proxy類:
Proxy類是專門完成代理的操作類,可以通過此類為一個或多個接口動態地生成實現類,此類提供了如下的操作方法:
參數說明:
ClassLoader loader:類加載器
Class<?>[] interfaces:得到全部的接口
InvocationHandler h:得到InvocationHandler接口的子類實例
Proxy類是專門完成代理的操作類,可以通過此類為一個或多個接口動態地生成實現類,此類提供了如下的操作方法:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
ClassLoader loader:類加載器
Class<?>[] interfaces:得到全部的接口
InvocationHandler h:得到InvocationHandler接口的子類實例
JDK動態代理主要涉及java.lang.reflect包下的兩個類:Proxy和InvocationHandler。其中,InvocationHandler是一個接口,可以通過實現該接口定義橫切邏輯,並通過反射機制調用目標類的代碼,動態將橫切邏輯和業務邏輯編織在一起。Proxy利用InvocationHandler動態創建一個符合某一接口的實例,生成目標類的代理對象。
代碼:移除性能監視橫切代碼
public class ForumServiceImpl implements ForumService{ public void removeTopic(int topicId){ //①在此位置原有的橫切代碼被移除(抽取切面中) System.out.println("模擬刪除Topic記錄:"+topicId); try{ Thread.currentThread().sleep(20); }catch(Exception e){ throw new RuntimeException(e); } //①在此位置原有的橫切代碼被移除(抽取切面中) } public void removeForum(int forumId){ //②在此位置原有的橫切代碼被移除(抽取切面中) System.out.println("模擬刪除Forum記錄:"+forumId); try{ Thread.currentThread().sleep(40); }catch(Exception e){ throw new RuntimeException(e); } //②在此位置原有的橫切代碼被移除(抽取切面中) } }
把抽取出來的代碼性能監視代碼放置到PerformanceHandler中:
public class PerformanceHandler implements InvocationHandler{ private Object target; //target為目標業務類 public PerformanceHandler(Object target){ this.target=target; } public Object invoke(Object proxy,Method method,Object[] args) throws Throwable{ PerformanceMonitor.begin(target.getClass().getName()+"."+method.getName()); //③ Object obj=method.invoke(target, args); //⑤通過反射調用業務類的目標方法 PerformanceMonitor.end(); //④ return obj; } }
invoke()方法中③④處的代碼為性能監視的橫切代碼,只出現一次。⑤處的method.invoke()語句通過java反射機制間接調用目標對象的方法,這樣InvocationHandler的invoke()方法就將橫切邏輯代碼和業務類方法的業務邏輯代碼編織到一起,我們可以把InvocationHandler看成一個編織器。
我們通過Proxy和PerformanceHandler創建ForumService接口的代理實例:
import java.lang.reflect.Proxy; public class TestForumService { public static void main(String[] args){ ForumService target=new ForumServiceImpl(); //被代理對象 PerformanceHandler handler=new PerformanceHandler(target); //將目標業務類和橫切代碼編織到一起 ForumService proxy=(ForumService)Proxy.newProxyInstance( //創建代理實例 target.getClass().getClassLoader, target.getClass().getInstances(), handler); proxy.removeForum(10); proxy.removeTopic(1012); } }
但是,JDK的動態代理依靠接口實現,如果有些類並沒有實現接口,則不能使用JDK代理,這就要使用cglib動態代理了。
二.CGLib動態代理
CGLib采用非常底層的字節碼技術,可以為一個類創建子類,並在子類中采用方法攔截的技術攔截所有的父類方法的調用,並順勢織入橫切邏輯。
下面采用CGLib技術寫一個可以為任何類創建織入性能監視橫切邏輯代理對象的代理創建器:
import class CglibProxy implements MethodInterceptor{ private Enhancer enhancer = new Enhancer(); public Object getProxy(Class clazz){ enhancer.setSuperclass(clazz); //設置需要創建子類的類 enhancer.setCallback(this); return enhancer.create(); //通過字節碼技術動態創建子類實例 } public Object intercept(Object obj,Method method,Object[] args,MethodProxy proxy) throws Throwable{//攔截父類所有方法的調用 PerformanceMonitor.begin(obj.getClass().getName()+"."+method.getName()); //① Object result = proxy.invokeSuper(obj,args); //通過代理類調用父類中的方法 PerformanceMonitor.end(); //② return result; } }
getProxy(Class clazz):為一個類創建動態代理對象,該代理對象通過擴展clazz創建代理對象。在這個代理對象中,我們織入性能監視的橫切邏輯①②。
intercept(Object obj,Method method,Object[] args,MethodProxy proxy):是CGLib定義的Intercept接口的方法,它攔截所有目標類方法的調用,obj:目標類的實例;method:目標類方法的反射對象;args:方法的動態入參;proxy:代理類實例。
下面通過CglibProxy為ForumServiceImpl類創建代理對象,並測試代理對象的方法:
import java.lang.reflect.Proxy; public class TestForumService{ public static void main(String[] args){ CglibProxy proxy = new CglibProxy(); ForumServiceImpl forumService = (ForumServiceImpl)proxy.getProxy(ForumServiceImpl.class); //動態生成子類的方式創建代理類 forumService.removeForum(10); forumService.removeTopic(1023); } }