在Java中有多種動態代理技術,如JDK、CGLIB、Javassist、ASM,其中最經常使用的動態代理技術是JDK和CGLIB。java
JDK動態代理是java.lang.reflect.*包提供的方法,必需要藉助一個接口才能產生代理對象,對於使用業務接口的類,Spring默認使用JDK動態代理實現AOP。
代碼示例以下:
建立dao包,並建立StuDao接口和StuDaoImpl實現類,
StuDao接口spring
public interface StuDao { public void add(); public void find(); public void update(); public void delete(); }
StuDaoImpl實現類數組
public class StuDaoImpl implements StuDao { @Override public void add() { System.out.println("添加學生"); } @Override public void find() { System.out.println("查詢學生"); } @Override public void update() { System.out.println("修改學生"); } @Override public void delete() { System.out.println("刪除學生"); } }
建立aspect包,並建立切面類MyAspect,該類中能夠定義多個通知,即加強處理的方法,示例代碼以下:ide
public class MyAspect { public void check(){ System.out.println("模擬權限控制"); } public void except(){ System.out.println("模擬異常處理"); } public void log(){ System.out.println("模擬日誌記錄"); } public void monitor(){ System.out.println("模擬性能檢測"); } }
建立proxy包,並建立代理類MyJdkProxy,在JDK動態代理中代理類必須實現java.lang.reflect.InvocationHandler接口,並編寫代理方法,在代理方法中須要經過Proxy實現動態代理。示例代碼以下:性能
package com.aop.proxy; import com.aop.aspect.MyAspect; import com.aop.dao.StuDao; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class MyJdkProxy implements InvocationHandler { //聲明目標類接口對象(真實對象) private StuDao stuDao; public MyJdkProxy(StuDao stuDao){ this.stuDao = stuDao; } //建立代理的方法,創建代理對象和真實對象的代理關係,返回代理對象 public Object createProxy(){ //1.類加載器 ClassLoader cld = MyJdkProxy.class.getClassLoader(); //2.被代理對象實現的全部接口 Class[] clazz = stuDao.getClass().getInterfaces(); return Proxy.newProxyInstance(cld,clazz,this); } /** * 代理的邏輯方法,全部動態代理類的方法調用都交給該方法處理 * @param proxy 被代理對象 * @param method 要執行的方法 * @param args 執行方法時須要的參數 * @return 返回代理結果 * @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //建立一個切面 MyAspect myAspect = new MyAspect(); //前加強 myAspect.check(); myAspect.except(); //在目標類上調用方法並傳入參數,至關於調用stuDao中的方法 Object obj = method.invoke(stuDao,args); //後加強 myAspect.log(); myAspect.monitor(); return obj; } }
建立測試類測試
@Test public void testStu(){ //建立目標對象 StuDao stuDao = new StuDaoImpl(); //建立代理對象 MyJdkProxy myJdkProxy = new MyJdkProxy(stuDao); //從代理對象中獲取加強後的目標對象 //該對象是一個被代理的對象,它會進入代理的邏輯方法invoke中 StuDao stuDaoProxy = (StuDao) myJdkProxy.createProxy(); //執行方法 stuDaoProxy.add(); System.out.println("=================="); stuDaoProxy.update(); System.out.println("=================="); stuDaoProxy.delete(); }
運行結果this
JDK動態代理必須提供接口才能使用,對於沒有提供接口的類,只能採用CGLIB動態代理。CGLIB採用很是底層的字節碼技術,對指定的目標類生產一個子類,並對子類進行加強。在Spring Core 包中已經集成了CGLIB所須要的jar包,無需另外引入jar包。
示例代碼以下:
建立目標類TestDaospa
public class TestDao { public void save(){ System.out.println("保存方法"); } public void modify(){ System.out.println("修改方法"); } public void delete(){ System.out.println("刪除方法"); } }
建立切面類MyAspect,並在該類中定義多個通知代理
public class MyAspect { public void check(){ System.out.println("模擬權限控制"); } public void except(){ System.out.println("模擬異常處理"); } public void log(){ System.out.println("模擬日誌記錄"); } public void monitor(){ System.out.println("模擬性能檢測"); } }
建立代理類MyCglibProxy,並實現MethodInterceptor接口日誌
package com.aop.proxy; import com.aop.aspect.MyAspect; import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class MyCglibProxy implements MethodInterceptor { /** * 建立代理的方法,生成CGLIB代理對象 * @param target 目標對象,須要加強的對象 * @return 返回目標對象的CGLIB代理對象 */ public Object createProxy(Object target){ //建立一個動態類對象,即加強類對象 Enhancer enhancer = new Enhancer(); //設置其父類 enhancer.setSuperclass(target.getClass()); //肯定代理邏輯對象爲當前對象 enhancer.setCallback(this); return enhancer.create(); } /** * 該方法會在程序執行目標方法時調用 * @param proxy 是CGLIB根據指定父類生成的代理對象 * @param method 是攔截方法 * @param args 攔截方法的參數數組 * @param methodProxy 方法的代理對象,用於執行父類的方法 * @return 返回代理結果 * @throws Throwable */ @Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { //建立一個切面 MyAspect myAspect = new MyAspect(); //前置加強 myAspect.check(); //目標方法執行,返回執行結果 Object obj = methodProxy.invokeSuper(proxy,args); //後置加強 myAspect.log(); return obj; } }
建立測試類
@Test public void test(){ //建立目標對象 TestDao testDao = new TestDao(); //建立代理對象 MyCglibProxy myCglibProxy = new MyCglibProxy(); //獲取加強後的目標對象 TestDao testDaoAdvice = (TestDao) myCglibProxy.createProxy(testDao); //執行方法 testDaoAdvice.save(); System.out.println("=================="); testDaoAdvice.modify(); System.out.println("=================="); testDaoAdvice.delete(); }
運行結果
(1)程序中應優先對接口建立代理,便於程序解耦維護;
(2)使用final關鍵字修飾的方法不能被代理,由於沒法覆蓋
(3)Spring只支持方法鏈接點,不提供屬性鏈接點