Spring學習之動態(tài)代理
前言
動態(tài)代理同蜻,是一種通過運行時操作字節(jié)碼闪湾,以達到增強類的功能的技術(shù)絮蒿,也是Spring AOP操作的基礎(chǔ)尊搬,關(guān)于AOP的內(nèi)容,將在后面的筆記中詳細講解土涝,本小節(jié)主要是理清楚動態(tài)代理佛寿,畢竟,Spring的AOP是基于動態(tài)代理技術(shù)但壮,對動態(tài)代理技術(shù)有所了解冀泻,對于學習Spring AOP也會有幫助
動態(tài)代理技術(shù)詳解
動態(tài)代理,現(xiàn)在主要是用于增強類的功能蜡饵,同時由于是具有動態(tài)性弹渔,所以避免了需要頻繁創(chuàng)建類的操作,同時溯祸,也使得原有的代碼在不需要改變的情況下肢专,對類的功能進行增強,主要的動態(tài)代理技術(shù)有:通過實現(xiàn)目標接口焦辅,重寫其方法博杖,以增強其能力,典型的以JDK動態(tài)代理為代表氨鹏;或者欧募,通過繼承類,重寫其方法以增強其能力仆抵,典型的以CGLib為代表跟继,這兩種技術(shù)分別從不同的方向來對類的能力進行擴充种冬,接下來來具體看下這兩種技術(shù)的特點以及差異。
基于JDK動態(tài)代理
基于JDK的動態(tài)代理技術(shù)舔糖,其主要特點就是目標類娱两,也就是需要被代理的類,必須有接口金吗,并且代理類必須實現(xiàn)跟它一樣的接口十兢,從而來起到代理其事務的功能,具體使用如下代碼所示摇庙,假設有一個UserService類旱物,主要用于負責用戶的登錄和退出,同時卫袒,有個日志類宵呛,負責記錄用戶的操作信息,直接將信息日志寫在對應的UserService實現(xiàn)類中夕凝,可以達到目的宝穗,但顯然這種方式不是很合理,特別是在UserService有很多個方法需要做日志記錄的時候码秉,就會使得日志記錄代碼遍布整個UserService逮矛,不僅使得代碼的冗余很大,而且當需要進行修改的時候转砖,也需要逐個修改须鼎,非常麻煩,這個時候堪藐,采用動態(tài)代理技術(shù)就是一種非常好的方法了莉兰。
/**
* UserService接口
*/
interface UserService{
void login();
void logout();
}
/**
* UseService實現(xiàn)類
*/
class UserServiceImpl implements UserService{
@Override
public void login() {
System.out.println("someone login....");
}
@Override
public void logout() {
System.out.println("someone logout....");
}
}
/**
* 實現(xiàn)InvocationHandle接口挑围,用于織入所要增強的代碼
*/
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)建對應的代理類
return (UserService) Proxy.newProxyInstance(
userService.getClass().getClassLoader(),
userService.getClass().getInterfaces(),
new UserServiceHandle(userService)
);
}
}
這樣,當我們需要使用UseService類的時候杉辙,只需要從ProxyFactory中獲取即可模捂,而且獲取的對象是UserService對象的代理類,也就是說蜘矢,獲得的對象是UserService對象的增強版
基于CGLib的動態(tài)代理技術(shù)
從上面的ProxyFactory工廠中可以看到狂男,在使用JDK進行創(chuàng)建動態(tài)代理對象的時候,需要為其提供接口品腹,或者說岖食,如果所要增強的目標類沒有實現(xiàn)任何接口,則JDK動態(tài)代理技術(shù)是無法為其創(chuàng)建對應的代理對象的舞吭,這是JDK動態(tài)代理技術(shù)的一種缺點泡垃,而CGLib動態(tài)代理技術(shù)則恰好彌補了這個缺點析珊,CGLib動態(tài)代理技術(shù)使用的是繼承該類的方式,從而避免了需要接口的缺陷蔑穴,具體使用如下所示忠寻,注意,需要導入對應的依賴文件
/**
* 基于CGLib的動態(tài)代理技術(shù)
* 注意這里需要實現(xiàn)MethodInterceptor接口
*/
class ProxyFactory implements MethodInterceptor{
// 提供對應的增強操作類
private Enhancer enhancer = new Enhancer();
public UserService getProxyObject(Class clazz){
// 設置所要增強的類的父類
enhancer.setSuperclass(clazz);
// 設置回調(diào)對象
enhancer.setCallback(this);
// 創(chuàng)建對應的對象
return (UserService) enhancer.create();
}
// 實現(xiàn)攔截方法存和,用于攔截對應的方法奕剃,并且對對應的方法進行增強
// 參數(shù)含義:傳入的對象, Method對象捐腿,方法的參數(shù)纵朋,進行代理后的Method對象
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
LogService.info();
// 這里需要注意,由于methodProxy對象是增強后的Method對象茄袖,所以這里需要調(diào)用的
// 是methodProxy父類的方法倡蝙,也就是所以增強的類的方法,以實現(xiàn)原來的功能
Object object = methodProxy.invokeSuper(o, objects);
LogService.info();
return object;
}
}
可以看到绞佩,使用CGLib動態(tài)代理技術(shù)可以在不需要實現(xiàn)接口的情況下東塔為對象創(chuàng)建代理對象寺鸥,在很大程度上彌補了JDK動態(tài)代理技術(shù)的缺點,不過由于CGLib動態(tài)代理技術(shù)是采用繼承目標類的方式品山,所以也存在一些問題胆建,比如說,對于final以及private修飾的方法是無法為其增強的肘交,這里還需要注意一下笆载。
總結(jié)
動態(tài)代理技術(shù)是實現(xiàn)AOP技術(shù)的基礎(chǔ),也是一種很方便地實現(xiàn)方式涯呻,常用的動態(tài)代理技術(shù)有基于JDK動態(tài)代理技術(shù)以及基于CGLib的動態(tài)代理技術(shù)凉驻,兩種技術(shù)各有千秋,也都各有缺點基于JDK的動態(tài)代理技術(shù)需要為其提供接口复罐,基于CGLib的動態(tài)代理技術(shù)不能為final以及private修飾的方法進行增強涝登,在使用的時候需要根據(jù)具體進行進行合理選擇。