前言
最近在學(xué)習(xí)設(shè)計(jì)模式,個(gè)人覺得代理模式比較重要,就在網(wǎng)上找了很多代理模式相關(guān)的知識(shí)點(diǎn),總結(jié)如下,希望可以幫到有需要的小伙伴 :)
一、代理模式
- 定義:代理模式,顧名思義就是提供一個(gè)代理類,可以訪問原對(duì)象并且替原對(duì)象進(jìn)行一些操作。
- 優(yōu)點(diǎn):使用代理模式可以在保證不修改原有類的同時(shí)(即滿足對(duì)擴(kuò)展開放,對(duì)修改關(guān)閉的原則),對(duì)原有類增加一些功能實(shí)現(xiàn)。
二、靜態(tài)代理
定義:靜態(tài)代理就是在編譯時(shí)就確定了代理類與被代理類的關(guān)系。
實(shí)現(xiàn)思路:定義一個(gè)接口
Subject,定義一個(gè)目標(biāo)類RealSubject和一個(gè)代理類ProxySubject同時(shí)實(shí)現(xiàn)Subject這個(gè)接口,在代理類ProxySubject中持有目標(biāo)類RealSubject對(duì)象,并對(duì)目標(biāo)類RealSubject對(duì)象的方法進(jìn)行一些增強(qiáng)實(shí)現(xiàn)。代碼如下:Subject接口
public interface Subject {
public void doSomething();
}
- 目標(biāo)類
RealSubject
/**
* 目標(biāo)類
*
* @Author Marshal
* @Date 2019-05-20 23:09
*/
public class RealSubject implements Subject {
@Override
public void doSomething() {
System.out.println("This is RealSubject~s method!");
}
}
- 代理類
ProxySubject
/**
* 代理類
*
* @Author Marshal
* @Date 2019-05-20 23:09
*/
public class ProxySubject implements Subject {
// 持有目標(biāo)對(duì)象
private RealSubject realSubject;
public ProxySubject(RealSubject realSubject) {
this.realSubject = realSubject;
}
@Override
public void doSomething() {
// 在被代理對(duì)象的方法前后增加業(yè)務(wù)
before();
realSubject.doSomething();
after();
}
private void after() {
System.out.println("after proxy");
}
private void before() {
System.out.println("before proxy");
}
}
- 測(cè)試類
public class ProxyTest {
public static void main(String[] args) {
Subject subject = new ProxySubject(new RealSubject());
subject.doSomething();
}
}
- 輸出結(jié)果如下:
before proxy
This is RealSubject~s method!
after proxy
總結(jié)
- 優(yōu)點(diǎn):
- 代理模式在客戶端與目標(biāo)對(duì)象之間起到一個(gè)中介作用和保護(hù)目標(biāo)對(duì)象的作用
- 代理對(duì)象可以擴(kuò)展目標(biāo)對(duì)象的功能
- 代理模式能將客戶端與目標(biāo)對(duì)象分離,在一定程度上降低了系統(tǒng)的耦合度
- 缺點(diǎn):
- 因?yàn)榇韺?duì)象需要與目標(biāo)對(duì)象實(shí)現(xiàn)一樣的接口,所以會(huì)有很多代理類,類太多.同時(shí),一旦接口增加方法,目標(biāo)對(duì)象與代理對(duì)象都要維護(hù)
PS: 靜態(tài)代理的缺點(diǎn),可以通過動(dòng)態(tài)代理來解決。
三、動(dòng)態(tài)代理
- 定義 :代理類在程序運(yùn)行時(shí)創(chuàng)建的代理方式被稱為動(dòng)態(tài)代理。也就是說,代理類并不需要在Java代碼中定義,而是在運(yùn)行時(shí)動(dòng)態(tài)生成的。相比于靜態(tài)代理,動(dòng)態(tài)代理的優(yōu)勢(shì)在于可以很方便的對(duì)代理類的函數(shù)進(jìn)行統(tǒng)一的處理,而不用修改每個(gè)代理類的函數(shù)。
- Java的動(dòng)態(tài)代理主要有兩種,即JDK代理和Cglib代理
3.1 JDK代理
使用JDK動(dòng)態(tài)代理有一個(gè)很大的限制,就是它要求目標(biāo)類必須實(shí)現(xiàn)了對(duì)應(yīng)方法的接口,它只能為接口創(chuàng)建代理實(shí)例。
我們繼續(xù)接著使用上面的類,JDK代理類需要實(shí)現(xiàn)InvocationHandler接口,通過實(shí)現(xiàn)該接口定義橫切邏輯,并通過反射機(jī)制調(diào)用目標(biāo)類的代碼,動(dòng)態(tài)的將橫切邏輯和業(yè)務(wù)邏輯編織在一起。代碼如下:
-
JDKProxyjdk代理類:
/**
* 動(dòng)態(tài)代理之JDK代理
*
* @Author Marshal
* @Date 2019-05-20 23:15
*/
public class JDKProxy implements InvocationHandler {
// 目標(biāo)對(duì)象
private Object object;
public JDKProxy(Object object) {
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("-----------JDKProxy before------------");
Object invoke = method.invoke(object, args);
System.out.println("-----------JDKProxy after------------");
return invoke;
}
}
- 測(cè)試類:
/**
* @Author Marshal
* @Date 2019-05-21 21:39
*/
public class JDKProxyTest {
public static void main(String[] args) {
// 被代理的類
RealSubject realSubject = new RealSubject();
// 代理類
JDKProxy jdkProxy = new JDKProxy(realSubject);
// 生成代理對(duì)象
Subject subject = (Subject) Proxy.newProxyInstance(RealSubject.class.getClassLoader(), new Class[]{Subject.class}, jdkProxy);
subject.doSomething();
}
}
- 測(cè)試
-----------JDKProxy before------------
This is RealSubject~s method!
-----------JDKProxy after------------
3.2 cglib代理
使用cglib實(shí)現(xiàn)動(dòng)態(tài)代理,并不要求委托類必須實(shí)現(xiàn)接口,底層采用asm字節(jié)碼生成框架生成代理類的字節(jié)碼,我們需要導(dǎo)入 cglib-nodep-3.2.6.jar 包。實(shí)現(xiàn)步驟如下:
- 新建一個(gè)目標(biāo)類
CglibRealSubject
public class CglibRealSubject {
public void doSomething() {
System.out.println("This is CglibRealSubject~s method!");
}
}
- 代理類
CglibProxy實(shí)現(xiàn)MethodInterceptor接口并重寫intercept方法
/**
* Cglib代理
* cglib-nodep-3.2.6.jar : https://github.com/cglib/cglib/releases/tag/RELEASE_3_2_6
*
* @Author Marshal
* @Date 2019-05-22 21:23
*/
public class CglibProxy implements MethodInterceptor {
public Object getProxyInstance(Class cla) {
// 1. 工具類
Enhancer en = new Enhancer();
// 2. 設(shè)置父類
en.setSuperclass(cla);
// 3. 設(shè)置回調(diào)函數(shù)
en.setCallback(this);
// 4. 創(chuàng)建子類(代理對(duì)象)
return en.create();
}
@Override
public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy)
throws Throwable {
System.out.println("-------------Before CglibProxy-------------");
// 目標(biāo)方法調(diào)用
Object invoke = methodProxy.invokeSuper(object, args);
System.out.println("-------------After CglibProxy-------------");
return invoke;
}
}
- 測(cè)試
public class CglibProxyTest {
public static void main(String[] args) {
CglibProxy proxy = new CglibProxy();
CglibRealSubject realSubject = (CglibRealSubject) proxy.getProxyInstance(CglibRealSubject.class);
realSubject.doSomething();
}
}
- 輸出結(jié)果
-------------Before CglibProxy-------------
This is CglibRealSubject~s method!
-------------After CglibProxy-------------
3.3 動(dòng)態(tài)代理的應(yīng)用
動(dòng)態(tài)代理主要運(yùn)用于框架中,例如在Spring的AOP中:
- 如果加入容器的目標(biāo)對(duì)象有實(shí)現(xiàn)接口,使用JDK代理
- 如果目標(biāo)對(duì)象沒有實(shí)現(xiàn)接口,則使用Cglib代理