共計 4999 個字符,預(yù)計需要花費 13 分鐘才能閱讀完成。
如何從動態(tài)代理實現(xiàn)到 Spring AOP,針對這個問題,這篇文章詳細介紹了相對應(yīng)的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。
丸趣 TV 小編主要講了 Spring Aop 動態(tài)代理實現(xiàn)的兩種方式。
1. Spring AOP
Spring 是一個輕型容器,Spring 整個系列的最最核心的概念當屬 IoC、AOP。可見 AOP 是 Spring 框架中的核心之一,在應(yīng)用中具有非常重要的作用,也是 Spring 其他組件的基礎(chǔ)。AOP(Aspect Oriented Programming),即面向切面編程,可以說是 OOP(Object Oriented Programming,面向?qū)ο缶幊蹋┑难a充和完善。OOP 引入封裝、繼承、多態(tài)等概念來建立一種對象層次結(jié)構(gòu),用于模擬公共行為的一個集合。不過 OOP 允許開發(fā)者定義縱向的關(guān)系,但并不適合定義橫向的關(guān)系,例如日志功能。
關(guān)于 AOP 的基礎(chǔ)知識,并不是本文的重點,我們主要來看下 AOP 的核心功能的底層實現(xiàn)機制:動態(tài)代理的實現(xiàn)原理。AOP 的攔截功能是由 java 中的動態(tài)代理來實現(xiàn)的。在目標類的基礎(chǔ)上增加切面邏輯,生成增強的目標類(該切面邏輯或者在目標類函數(shù)執(zhí)行之前,或者目標類函數(shù)執(zhí)行之后,或者在目標類函數(shù)拋出異常時候執(zhí)行。不同的切入時機對應(yīng)不同的 Interceptor 的種類,如 BeforeAdviseInterceptor,AfterAdviseInterceptor 以及 ThrowsAdviseInterceptor 等)。
那么動態(tài)代理是如何實現(xiàn)將切面邏輯(advise)織入到目標類方法中去的呢?下面我們就來詳細介紹并實現(xiàn) AOP 中用到的兩種動態(tài)代理。
AOP 的源碼中用到了兩種動態(tài)代理來實現(xiàn)攔截切入功能:jdk 動態(tài)代理和 cglib 動態(tài)代理。兩種方法同時存在,各有優(yōu)劣。jdk 動態(tài)代理是由 java 內(nèi)部的反射機制來實現(xiàn)的,cglib 動態(tài)代理底層則是借助 asm 來實現(xiàn)的。總的來說,反射機制在生成類的過程中比較高效,而 asm 在生成類之后的相關(guān)執(zhí)行過程中比較高效(可以通過將 asm 生成的類進行緩存,這樣解決 asm 生成類過程低效問題)。
下面我們分別來示例實現(xiàn)這兩種方法。
2. JDK 動態(tài)代理 2.1 定義接口與實現(xiàn)類
public interface OrderService { public void save(UUID orderId, String name);
public void update(UUID orderId, String name);
public String getByName(String name);
}
上面代碼定義了一個被攔截對象接口,即橫切關(guān)注點。下面代碼實現(xiàn)被攔截對象接口。
public class OrderServiceImpl implements OrderService {
private String user = null;
public OrderServiceImpl() { }
public OrderServiceImpl(String user) { this.setUser(user);
}
//...
@Override
public void save(UUID orderId, String name) { System.out.println( call save() 方法,save: + name);
}
@Override
public void update(UUID orderId, String name) { System.out.println( call update() 方法
}
@Override
public String getByName(String name) { System.out.println( call getByName() 方法
return aoho
}
}
2.2 JDK 動態(tài)代理類
public class JDKProxy implements InvocationHandler {
// 需要代理的目標對象
private Object targetObject;
public Object createProxyInstance(Object targetObject) {
this.targetObject = targetObject;
return Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader(),
this.targetObject.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 被代理對象
OrderServiceImpl bean = (OrderServiceImpl) this.targetObject;
Object result = null;
// 切面邏輯(advise),此處是在目標類代碼執(zhí)行之前
System.out.println( ---before invoke----
if (bean.getUser() != null) { result = method.invoke(targetObject, args);
}
System.out.println( ---after invoke----
return result;
}
//...
}
上述代碼實現(xiàn)了動態(tài)代理類 JDKProxy,實現(xiàn) InvocationHandler 接口,并且實現(xiàn)接口中的 invoke 方法。當客戶端調(diào)用代理對象的業(yè)務(wù)方法時,代理對象執(zhí)行 invoke 方法,invoke 方法把調(diào)用委派給 targetObject,相當于調(diào)用目標對象的方法,在 invoke 方法委派前判斷權(quán)限,實現(xiàn)方法的攔截。
2.3 測試
public class AOPTest { public static void main(String[] args) { JDKProxy factory = new JDKProxy();
//Proxy 為 InvocationHandler 實現(xiàn)類動態(tài)創(chuàng)建一個符合某一接口的代理實例
OrderService orderService = (OrderService) factory.createProxyInstance(new OrderServiceImpl( aoho));
// 由動態(tài)生成的代理對象來 orderService 代理執(zhí)行程序
orderService.save(UUID.randomUUID(), aoho
}
}
結(jié)果如下:
---before invoke----
call save() 方法,save:aoho
---after invoke----
3. CGLIB 字節(jié)碼生成 3.1 要代理的類
CGLIB 既可以對接口的類生成代理,也可以針對類生成代理。示例中,實現(xiàn)對類的代理。
public class OrderManager {
private String user = null;
public OrderManager() { }
public OrderManager(String user) { this.setUser(user);
}
//...
public void save(UUID orderId, String name) { System.out.println( call save() 方法,save: + name);
}
public void update(UUID orderId, String name) { System.out.println( call update() 方法
}
public String getByName(String name) { System.out.println( call getByName() 方法
return aoho
}
}
該類的實現(xiàn)和上面的接口實現(xiàn)一樣,為了保持統(tǒng)一。
3.2 CGLIB 動態(tài)代理類
public class CGLibProxy implements MethodInterceptor {
// CGLib 需要代理的目標對象
private Object targetObject;
public Object createProxyObject(Object obj) {
this.targetObject = obj;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(obj.getClass());
// 回調(diào)方法的參數(shù)為代理類對象 CglibProxy,最后增強目標類調(diào)用的是代理類對象 CglibProxy 中的 intercept 方法
enhancer.setCallback(this);
// 增強后的目標類
Object proxyObj = enhancer.create();
// 返回代理對象
return proxyObj;
}
@Override
public Object intercept(Object proxy, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {
Object obj = null;
// 切面邏輯(advise),此處是在目標類代碼執(zhí)行之前
System.out.println( ---before intercept----
obj = method.invoke(targetObject, args);
System.out.println( ---after intercept----
return obj;
}
}
上述實現(xiàn)了創(chuàng)建子類的方法與代理的方法。getProxy(SuperClass.class) 方法通過入?yún)⒓锤割惖淖止?jié)碼,擴展父類的 class 來創(chuàng)建代理對象。intercept() 方法攔截所有目標類方法的調(diào)用,obj 表示目標類的實例,method 為目標類方法的反射對象,args 為方法的動態(tài)入?yún)ⅲ琺ethodProxy 為代理類實例。method.invoke(targetObject, args) 通過代理類調(diào)用父類中的方法。
3.3 測試
public class AOPTest { public static void main(String[] args) { OrderManager order = (OrderManager) new CGLibProxy().createProxyObject(new OrderManager( aoho));
order.save(UUID.randomUUID(), aoho
}
結(jié)果如下:
---before intercept----
call save() 方法,save:aoho
---after intercept----
4. 總結(jié)
主要講了 Spring Aop 動態(tài)代理實現(xiàn)的兩種方式,并分別介紹了其優(yōu)缺點。jdk 動態(tài)代理的應(yīng)用前提是目標類基于統(tǒng)一的接口。如果沒有該前提,jdk 動態(tài)代理不能應(yīng)用。由此可以看出,jdk 動態(tài)代理有一定的局限性,cglib 這種第三方類庫實現(xiàn)的動態(tài)代理應(yīng)用更加廣泛,且在效率上更有優(yōu)勢。
JDK 動態(tài)代理機制是委托機制,不需要以來第三方的庫,只要要 JDK 環(huán)境就可以進行代理,動態(tài)實現(xiàn)接口類,在動態(tài)生成的實現(xiàn)類里面委托為 hanlder 去調(diào)用原始實現(xiàn)類方法;CGLib 必須依賴于 CGLib 的類庫,使用的是繼承機制,是被代理類和代理類繼承的關(guān)系,所以代理類是可以賦值給被代理類的,如果被代理類有接口,那么代理類也可以賦值給接口。
關(guān)于如何從動態(tài)代理實現(xiàn)到 Spring AOP 問題的解答就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關(guān)注丸趣 TV 行業(yè)資訊頻道了解更多相關(guān)知識。