Spring AOP 的代理模式
1. 前言
大家好,我們學(xué)習(xí)了 AOP 的概念,理解了 AOP 的作用,明白了什么是 AOP 面向切面編程,目的是進(jìn)行解耦,可以自由地對(duì)某個(gè)功能方法做增強(qiáng),而它的設(shè)計(jì)模式就是代理模式。
那么本小節(jié)我們就學(xué)習(xí) AOP 設(shè)計(jì)模式之代理模式,明白代理模式的含義,掌握代理模式的語(yǔ)法,同時(shí)明白它的應(yīng)用場(chǎng)景。
2. 代理模式介紹
3.1 概念解釋
代理名詞解釋為:以被代理人名義,在授權(quán)范圍內(nèi)與第三方實(shí)施行為。而在軟件行業(yè)中代理模式是一種非常常用的設(shè)計(jì)模式,跟現(xiàn)實(shí)生活中的邏輯一致。
在開(kāi)發(fā)中代理模式的表現(xiàn)為:我們創(chuàng)建帶有現(xiàn)有對(duì)象的代理對(duì)象,以便向外界提供功能接口。代理對(duì)象可以在客戶(hù)端和目標(biāo)對(duì)象之間起到中介的作用。
中介什么作用? 可以為被代理對(duì)象執(zhí)行一些附帶的,增加的額外功能。
代理模式圖例:
圖例解釋
- 數(shù)字 1 處表示發(fā)起請(qǐng)求的客戶(hù)端;
- 數(shù)字 2 處表示需要調(diào)用的接口;
- 數(shù)字 3 處表示真實(shí)接口的實(shí)現(xiàn)類(lèi),執(zhí)行的代碼邏輯在此;
- 數(shù)字 4 處表示代理對(duì)象,同樣實(shí)現(xiàn)了接口,覆寫(xiě)了需要執(zhí)行的代碼 Request 方法;
- 數(shù)字 5 處是代理類(lèi)中多余的代碼邏輯,對(duì)原有方法的增強(qiáng);
- 數(shù)字 6 處是代理類(lèi)中的執(zhí)行的被代理類(lèi)的原始邏輯。
3.2 代理模式分類(lèi)
上面已經(jīng)解釋了代理模式的概念和原理,在開(kāi)發(fā)中,實(shí)現(xiàn)代理模式可以分為兩種。
- 靜態(tài)代理:若代理類(lèi)在程序運(yùn)行前就已經(jīng)存在,那么這種代理方式被成為靜態(tài)代理 ,這種情況下的代理類(lèi)通常都是我們?cè)?Java 代碼中定義的, 靜態(tài)代理中的代理類(lèi)和委托類(lèi)會(huì)實(shí)現(xiàn)同一接口;
- 動(dòng)態(tài)代理:代理類(lèi)在程序運(yùn)行時(shí)創(chuàng)建的代理方式被稱(chēng)為動(dòng)態(tài)代理。 也就是說(shuō),這種情況下,代理類(lèi)并不是在 Java 代碼中定義的,而是在運(yùn)行時(shí)根據(jù)我們?cè)?Java 代碼中的 “指示” 動(dòng)態(tài)生成的。
下面,我們就針對(duì)這兩種模式演示效果… 各位看官隨我來(lái)。
3. 代碼模式測(cè)試
3.1 靜態(tài)代理實(shí)現(xiàn)
1.userService 接口代碼
public interface UserService {
public void saveUser();
}
2.userServiceImpl 實(shí)現(xiàn)類(lèi)代碼
@Service
public class UserServiceImpl implements UserService {
public void saveUser() {
System.out.println("執(zhí)行service中的保存邏輯");
}
}
3.userServiceProxy 代理類(lèi)代碼
public class UserServiceProxy implements UserService {
//被代理類(lèi)實(shí)現(xiàn)接口
private UserService userService;
public UserServiceProxy(UserService userService) {
this.userService = userService;
}
//覆寫(xiě)的方法
@Override
public void saveUser() {
System.out.println("原始功能執(zhí)行之前的邏輯代碼");
userService.saveUser();;
System.out.println("原始功能執(zhí)行之后的邏輯代碼");
}
}
代碼解釋:
userService
接口和 userServiceImpl
實(shí)現(xiàn)類(lèi)代碼不做贅述,已經(jīng)用過(guò)多次。
重點(diǎn)關(guān)注于在 userServiceProxy
代理類(lèi)代碼,其中屬性為被代理類(lèi)的接口,目的是傳入進(jìn)來(lái)被代理類(lèi)實(shí)例,對(duì)它做功能增強(qiáng)。
下面的 saveUser
方法是代理類(lèi)執(zhí)行的邏輯,在方法內(nèi)部有增強(qiáng)的代碼邏輯,也保留了原始實(shí)例的代碼功能。
4. 測(cè)試代碼
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(SpringConfig.class);
context.refresh();
//獲取接口實(shí)例
UserService service = context.getBean(UserService.class);
//創(chuàng)建實(shí)例的代理
UserServiceProxy proxy = new UserServiceProxy(service);
//執(zhí)行方法
proxy.saveUser();
}
5. 測(cè)試結(jié)果
可以看到,執(zhí)行結(jié)果中即包含了被代理對(duì)象的原始保存方法的邏輯,也有代理類(lèi)中對(duì)原始方法的兩個(gè)增強(qiáng)代碼。
3.2 動(dòng)態(tài)代理實(shí)現(xiàn)
1. 創(chuàng)建動(dòng)態(tài)處理器
public class DynamicProxy implements InvocationHandler {
private Object object;
public DynamicProxy(final Object object) {
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("執(zhí)行前邏輯");
Object result = method.invoke(object, args);
System.out.println("執(zhí)行后邏輯");
return result;
}
}
2. 測(cè)試類(lèi)代碼
public class SpringTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(SpringConfig.class);
context.refresh();
//獲取接口實(shí)例
UserService service = context.getBean(UserService.class);
//動(dòng)態(tài)創(chuàng)建實(shí)例的代理
UserService proxy = (UserService)Proxy.newProxyInstance(UserService.class.getClassLoader(),
new Class[]{UserService.class}, new DynamicProxy(service));
//proxy執(zhí)行方法
proxy.saveUser();
}
}
代碼解釋
Proxy.newProxyInstance
是 JDK 提供的一個(gè)用于動(dòng)態(tài)創(chuàng)建代理實(shí)例的方法,JDK 1.7 的 API 有如下說(shuō)明:
紅線框的內(nèi)部是簡(jiǎn)易方式創(chuàng)建代理對(duì)象的方式,也就是我們示例中代碼的實(shí)現(xiàn)方式,參數(shù)解釋如下:
- ClassLoader loader: 指定當(dāng)前目標(biāo)對(duì)象使用的類(lèi)加載器,獲取加載器的方法是固定的
- Class<?>[] interfaces: 指定目標(biāo)對(duì)象實(shí)現(xiàn)的接口的類(lèi)型,使用泛型方式確認(rèn)類(lèi)型
- InvocationHandler: 指定動(dòng)態(tài)處理器,執(zhí)行目標(biāo)對(duì)象的方法時(shí),會(huì)觸發(fā)事件處理器的方法
3. 測(cè)試結(jié)果
4. 小結(jié)
本小節(jié)學(xué)習(xí)了代理模式以及它的的實(shí)現(xiàn),那么要求大家掌握的內(nèi)容如下:
1. 實(shí)現(xiàn)要素
被代理類(lèi)最少實(shí)現(xiàn)一個(gè)接口。
2. 實(shí)現(xiàn)步驟
newProxyInstance
方法獲取代理類(lèi)實(shí)例;- 編寫(xiě)代理實(shí)例方法;
- 添加代理實(shí)例方法內(nèi)邏輯;
- invoke 方法中調(diào)用委托類(lèi),也就是被代理類(lèi)的方法。