spring容器經過動態代理再結合java反射思想能夠使得方法調用更加簡潔java
與靜態代理對照(關於靜態代理的介紹 能夠閱讀上一篇:JAVA設計模式之 代理模式【Proxy Pattern】(博主),spring
動態代理類的字節碼是在程序運行時由Java反射機制動態生成。設計模式
注意:
一、AspectJ是採用編譯時生成AOP代理類,具備更好的性能,可是須要使用特定的編譯器進行處理
框架
二、Spring AOP採用運行時生成AOP代理類,無需使用特定編譯器進行處理,可是性能相對於AspectJ較差eclipse
一、JDK動態代理中 須要瞭解的兩個重要的類或接口 [InvocationHandler 和 Proxy]
ide
① InvocationHandler接口函數
public interface InvocationHandler { public Object invoke(Object proxy,Method method,Object[] args) throws Throwable; }
參數說明:
Object proxy:指被代理的對象
Method method:咱們所要調用被代理對象的某個方法的Method對象
Object[] args:被代理對象某個方法調用時所須要的參數
能夠將InvocationHandler接口的子類想象成一個代理的最終操做類。性能
說明:每個動態代理類都必需要實現InvocationHandler這個接口,而且每一個代理類(Proxy)的實例都關聯到了一個handler,當咱們經過代理對象調用一個方法的時候,這個方法的調用就會被轉發爲由InvocationHandler這個接口的 invoke 方法來進行調用。同時在invoke的方法裏 咱們能夠對被代理對象的方法調用作加強處理(如添加事務、日誌、權限驗證等操做)。測試
② Proxy類this
Proxy類是專門完成代理的操做類,能夠經過此類爲一個或多個接口動態地生成實現類,該類經常使用的調用方法以下:
newProxyInstance方法參數說明以下:
ClassLoader loader:類加載器,定義了由哪一個ClassLoader對象來對生成的代理對象進行加載
Class<?>[] interfaces:獲得被代理類所有的接口,若是我提供了一組接口給它,那麼這個代理對象就宣稱實現了該接口,這樣我就能調用這組接口中的方法了
InvocationHandler h:獲得InvocationHandler接口的子類實例
二、JDK動態代理代碼示例:
首先咱們定義了一個Subject類型的接口:Subject.java
public interface Subject { public void visit(); }
接着定義一個接口的實現類,這個類就是咱們示例中的被代理對象:RealSubject.java
/** * 被代理類 * @author lvzb.software@qq.com * */ public class RealSubject implements Subject { @Override public void visit() { System.out.println("I am 'RealSubject',I am the execution method"); } }
第三步 定義一個動態代理類(必需要實現 InvocationHandler 這個接口):DynamicProxy.java
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; /** * JDK動態代理類 * @author lvzb.software@qq.com * */ public class DynamicProxy implements InvocationHandler { // 咱們要代理的真實對象(委託對象) private Object subject; // 構造方法,給咱們要代理的真實對象賦初值 public DynamicProxy(Object obj){ this.subject = obj; } @Override public Object invoke(Object object, Method method, Object[] args) throws Throwable { // 在代理真實對象操做前 咱們能夠添加一些本身的操做 System.out.println("before proxy invoke"); // 當代理對象調用真實對象的方法時,其會自動的跳轉到代理對象關聯的handler對象的invoke方法來進行調用 method.invoke(subject, args); // 在代理真實對象操做後 咱們也能夠添加一些本身的操做 System.out.println("after proxy invoke"); return null; } }
最後代理測試類:Client.java
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; public class Client { public static void main(String[] args) { // 咱們要代理的真實對象 Subject realSubject = new RealSubject(); // 咱們要代理哪一個真實對象,就將該對象傳進去,最後是經過該真實對象調用方法的 InvocationHandler handler = new DynamicProxy(realSubject); /* * 經過Proxy的newProxyInstance方法來動態建立咱們的代理對象,咱們來看看其三個參數< * 參數一:咱們這裏使用handler這個類的ClassLoader對象來加載咱們的代理對象 * 參數二:咱們這裏爲代理對象提供的接口是真實對象所實行的接口,表示我要代理的是該真實對象,這樣我就能調用這組接口中的方法了 * 參數三:咱們這裏將這個代理對象關聯到了上方的 InvocationHandler 這個對象上 */ Subject proxyInstance = (Subject)Proxy.newProxyInstance(handler.getClass().getClassLoader(), RealSubject.class.getInterfaces(), handler); System.out.println(proxyInstance.getClass().getName()); proxyInstance.visit(); } }
運行->控制檯輸出結果以下:
com.sun.proxy.$Proxy0 before proxy invoke I am 'RealSubject',I am the execution method after proxy invoke
3、Cglib(Code Generation Library)動態代理 [對沒有實現接口的普通類作代理]
一、概述:
Cglib是一個優秀的動態代理框架,它的底層使用ASM(JAVA字節碼處理框架)在內存中動態的生成被代理類的子類。使用CGLIB即便被代理類沒有實現任何接 也能夠實現動態代理功能。可是不能對final修飾的類進行代理。
二、原理:
經過字節碼技術爲一個類建立子類,並在子類中採用方法攔截的技術攔截全部父類方法的調用。
<JDK動態代理與CGLib動態代理均是實現Spring AOP的基礎>
三、使用:
使用Cglib前須要導入如下兩個jar文件:
asm.jar – Cglib的底層實現。
【cglib包的底層是使用字節碼處理框架ASM來轉換字節碼並生成新的類,因此cglib包要依賴於asm包】
cglib.jar - Cglib的核心jar包。
四、Cglib動態代理代碼示例:
首先定義一個沒有實現接口的代理委託類:CglibRealSubject.java
/** * 沒有實現接口的代理委託類 * @author lvzb.software@qq.com * */ public class CglibRealSubject{ public void visit() { System.out.println("I am 'RealSubject',I am the execution method"); } }
接着定義一個Cglib動態代理類: CglibDynamicProxy.java
import java.lang.reflect.Method; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; /** * 使用cglib動態代理 * @author lvzb.software@qq.com * */ public class CglibDynamicProxy implements MethodInterceptor { private Object target; /** * 建立代理對象 * @param target 被代理的對象 * @return */ public Object getProxyInstance(Object target){ this.target = target; // 聲明加強類實例 Enhancer enhancer = new Enhancer(); // 設置被代理類字節碼,CGLIB根據字節碼生成被代理類的子類 enhancer.setSuperclass(this.target.getClass()); // 設置要代理的攔截器,回調函數,即一個方法攔截 new MethodInterceptor() enhancer.setCallback(this); // 建立代理對象 實例 return enhancer.create(); } @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; } }
最後測試客戶端類:CglibClient.java
public class CglibClient { public static void main(String[] args) { CglibDynamicProxy cglib = new CglibDynamicProxy(); CglibRealSubject realSubject = (CglibRealSubject) cglib.getProxyInstance(new CglibRealSubject()); realSubject.visit(); } }
運行->控制檯輸出結果以下:
前置代理,加強處理 I am 'RealSubject',I am the execution method 後置代理,加強處理