Spring學(xué)習(xí)之動態(tài)代理(JDK動態(tài)代理和CGLIB動態(tài)代理)
前言
動態(tài)代理,是一種通過運(yùn)行時操作字節(jié)碼,以達(dá)到增強(qiáng)類的功能的技術(shù),也是Spring AOP操作的基礎(chǔ),關(guān)于AOP的內(nèi)容,將在后面的筆記中詳細(xì)講解,本小節(jié)主要是理清楚動態(tài)代理,畢竟,Spring的AOP是基于動態(tài)代理技術(shù),對動態(tài)代理技術(shù)有所了解,對于學(xué)習(xí)Spring AOP也會有幫助
動態(tài)代理技術(shù)詳解
動態(tài)代理,現(xiàn)在主要是用于增強(qiáng)類的功能,同時由于是具有動態(tài)性,所以避免了需要頻繁創(chuàng)建類的操作,同時,也使得原有的代碼在不需要改變的情況下,對類的功能進(jìn)行增強(qiáng),主要的動態(tài)代理技術(shù)有:通過實(shí)現(xiàn)目標(biāo)接口,重寫其方法,以增強(qiáng)其能力,典型的以JDK動態(tài)代理為代表;或者,通過繼承類,重寫其方法以增強(qiáng)其能力,典型的以CGLib為代表,這兩種技術(shù)分別從不同的方向來對類的能力進(jìn)行擴(kuò)充,接下來來具體看下這兩種技術(shù)的特點(diǎn)以及差異。
基于JDK動態(tài)代理
基于JDK的動態(tài)代理技術(shù),其主要特點(diǎn)就是目標(biāo)類,也就是需要被代理的類,必須有接口,并且代理類必須實(shí)現(xiàn)跟它一樣的接口,從而來起到代理其事務(wù)的功能,具體使用如下代碼所示,假設(shè)有一個UserService類,主要用于負(fù)責(zé)用戶的登錄和退出,同時,有個日志類,負(fù)責(zé)記錄用戶的操作信息,直接將信息日志寫在對應(yīng)的UserService實(shí)現(xiàn)類中,可以達(dá)到目的,但顯然這種方式不是很合理,特別是在UserService有很多個方法需要做日志記錄的時候,就會使得日志記錄代碼遍布整個UserService,不僅使得代碼的冗余很大,而且當(dāng)需要進(jìn)行修改的時候,也需要逐個修改,非常麻煩,這個時候,采用動態(tài)代理技術(shù)就是一種非常好的方法了。
/**
* UserService接口
*/
interface UserService{
void login();
void logout();
}
/**
* UseService實(shí)現(xiàn)類
*/
class UserServiceImpl implements UserService{
@Override
public void login() {
System.out.println("someone login....");
}
@Override
public void logout() {
System.out.println("someone logout....");
}
}
/**
* 實(shí)現(xiàn)InvocationHandle接口,用于織入所要增強(qiáng)的代碼
*/
class UserServiceHandle implements InvocationHandler{
private UserService userService;
public UserServiceHandle(UserService userService) {
this.userService = userService;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
LogService.info();
Object object = method.invoke(userService, args);
LogService.info();
return object;
}
}
/**
* 代理類工廠,用于產(chǎn)生UseService類的代理類
*/
class ProxyFactory{
public static UserService getProxyObject(UserService userService){
// 使用JDK動態(tài)代理技術(shù)來創(chuàng)建對應(yīng)的代理類
return (UserService) Proxy.newProxyInstance(
userService.getClass().getClassLoader(),
userService.getClass().getInterfaces(),
new UserServiceHandle(userService)
);
}
}
這樣,當(dāng)我們需要使用UseService類的時候,只需要從ProxyFactory中獲取即可,而且獲取的對象是UserService對象的代理類,也就是說,獲得的對象是UserService對象的增強(qiáng)版
基于CGLib的動態(tài)代理技術(shù)
從上面的ProxyFactory工廠中可以看到,在使用JDK進(jìn)行創(chuàng)建動態(tài)代理對象的時候,需要為其提供接口,或者說,如果所要增強(qiáng)的目標(biāo)類沒有實(shí)現(xiàn)任何接口,則JDK動態(tài)代理技術(shù)是無法為其創(chuàng)建對應(yīng)的代理對象的,這是JDK動態(tài)代理技術(shù)的一種缺點(diǎn),而CGLib動態(tài)代理技術(shù)則恰好彌補(bǔ)了這個缺點(diǎn),CGLib動態(tài)代理技術(shù)使用的是繼承該類的方式,從而避免了需要接口的缺陷,具體使用如下所示,注意,需要導(dǎo)入對應(yīng)的依賴文件
/**
* 基于CGLib的動態(tài)代理技術(shù)
* 注意這里需要實(shí)現(xiàn)MethodInterceptor接口
*/
class ProxyFactory implements MethodInterceptor{
// 提供對應(yīng)的增強(qiáng)操作類
private Enhancer enhancer = new Enhancer();
public UserService getProxyObject(Class clazz){
// 設(shè)置所要增強(qiáng)的類的父類
enhancer.setSuperclass(clazz);
// 設(shè)置回調(diào)對象
enhancer.setCallback(this);
// 創(chuàng)建對應(yīng)的對象
return (UserService) enhancer.create();
}
// 實(shí)現(xiàn)攔截方法,用于攔截對應(yīng)的方法,并且對對應(yīng)的方法進(jìn)行增強(qiáng)
// 參數(shù)含義:傳入的對象, Method對象,方法的參數(shù),進(jìn)行代理后的Method對象
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
LogService.info();
// 這里需要注意,由于methodProxy對象是增強(qiáng)后的Method對象,所以這里需要調(diào)用的
// 是methodProxy父類的方法,也就是所以增強(qiáng)的類的方法,以實(shí)現(xiàn)原來的功能
Object object = methodProxy.invokeSuper(o, objects);
LogService.info();
return object;
}
}
可以看到,使用CGLib動態(tài)代理技術(shù)可以在不需要實(shí)現(xiàn)接口的情況下東塔為對象創(chuàng)建代理對象,在很大程度上彌補(bǔ)了JDK動態(tài)代理技術(shù)的缺點(diǎn),不過由于CGLib動態(tài)代理技術(shù)是采用繼承目標(biāo)類的方式,所以也存在一些問題,比如說,對于final以及private修飾的方法是無法為其增強(qiáng)的,這里還需要注意一下。
總結(jié)
動態(tài)代理技術(shù)是實(shí)現(xiàn)AOP技術(shù)的基礎(chǔ),也是一種很方便地實(shí)現(xiàn)方式,常用的動態(tài)代理技術(shù)有基于JDK動態(tài)代理技術(shù)以及基于CGLib的動態(tài)代理技術(shù),兩種技術(shù)各有千秋,也都各有缺點(diǎn)基于JDK的動態(tài)代理技術(shù)需要為其提供接口,基于CGLib的動態(tài)代理技術(shù)不能為final以及private修飾的方法進(jìn)行增強(qiáng),在使用的時候需要根據(jù)具體進(jìn)行進(jìn)行合理選擇。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
java 重定義數(shù)組的實(shí)現(xiàn)方法(與VB的ReDim相像)
java 重定義數(shù)組的實(shí)現(xiàn)方法(與VB的ReDim相像),需要的朋友可以參考一下2013-04-04
Java中WeakHashMap和HashMap的區(qū)別詳解
這篇文章主要介紹了Java中WeakHashMap和HashMap的區(qū)別詳解,WeakHashMap和HashMap一樣,WeakHashMap也是一個散列表,它存儲的內(nèi)容也是鍵值對(key-value)映射,而且鍵和值都可以為null,需要的朋友可以參考下2023-09-09
java實(shí)現(xiàn)HttpClient異步請求資源的方法
這篇文章主要介紹了java實(shí)現(xiàn)HttpClient異步請求資源的方法,實(shí)例分析了java基于http協(xié)議實(shí)現(xiàn)異步請求的技巧,具有一定參考借鑒價值,需要的朋友可以參考下2015-07-07
關(guān)于SpringBoot使用@Async的總結(jié)
這篇文章主要介紹了關(guān)于SpringBoot使用@Async的總結(jié),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-12-12
SpringBoot整合SpringSecurityOauth2實(shí)現(xiàn)鑒權(quán)動態(tài)權(quán)限問題
這篇文章主要介紹了SpringBoot整合SpringSecurityOauth2實(shí)現(xiàn)鑒權(quán)-動態(tài)權(quán)限,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-06-06
java隨機(jī)生成字符串(字符隨機(jī)生成類 生成隨機(jī)字符組合)
java隨機(jī)生成字符串,字符組合多樣,可以大小字組合、大+小字符+數(shù)字等方式,大家參考使用吧2013-12-12
JAVA中使用openoffice將Excel轉(zhuǎn)PDF再轉(zhuǎn)圖片功能的實(shí)現(xiàn)代碼
這篇文章主要介紹了JAVA中使用openoffice將Excel轉(zhuǎn)PDF再轉(zhuǎn)圖片功能實(shí)現(xiàn),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-12-12
利用微信小程序+JAVA實(shí)現(xiàn)微信支付的全過程
微信支付是一種在線支付解決方案,允許用戶通過微信內(nèi)的支付功能進(jìn)行付款,下面這篇文章主要給大家介紹了關(guān)于利用微信小程序+JAVA實(shí)現(xiàn)微信支付的相關(guān)資料,需要的朋友可以參考下2024-08-08

