設(shè)計模式 - 代理模式

前言:

在開發(fā)中一定被老大這樣說過不要隨意去修改別人已經(jīng)寫好的代碼或者方法。但我們又希望原來的對象可以得到擴(kuò)展,又能保護(hù)原對象,這時代理模式就出來了。
在百科中這樣描述(定義):為其他對象提供一種代理以控制對這個對象的訪問。在某些情況下,一個對象不適合或者不能直接引用另一個對象,而代理對象可以在客戶端和目標(biāo)對象之間起到中介的作用。aop的實現(xiàn)對方法前后進(jìn)行攔截,改變原有對象或方法都是基于代理模式。代理模式分為靜態(tài)代理和jdk動態(tài)代理。

靜態(tài)代理

需求:張三看上了妹子,怕被拒絕而苦惱,于是李四說我替你把她約出來。

  1. 定義代理和被代理的公共的接口
//張三和李四的公共行為
public interface Person {
    void meetGirl();
}

  1. 實現(xiàn)person接口,即具體動作的實現(xiàn)
//動作的具體實現(xiàn)
public class Man implements Person{

    private String name;

    public Man(String name){
        this.name = name;
    }

    @Override
    public void meetGirl() {
        System.out.println(name+"正在和女孩吃飯,感謝你李四");
    }
}
  1. 持有一個被代理對象的代理類,同樣要實現(xiàn)person接口
//man代理類
public class ManProxy implements Person {

    //被代理的對象
    private Man man;

    //代理
    public ManProxy(Person man){
          this.man = (Man) man;
    }
    //被代理的執(zhí)行接口方法
    @Override
    public void meetGirl() {
        man.meetGirl();
    }
}
  1. client 測試,李四替張三約妹子
//代理的測試類
public class ProxyClient {

    @Test
    public void test(){
        //被代理的對象
        Person zhangsan = new Man("張三");
        //生成代理對象李四 幫張三去約姑娘,把張三傳給代理對象
        Person lisi = new ManProxy(zhangsan);
        lisi.meetGirl();
    }
}
image.png

動態(tài)代理參考

代理類在程序運(yùn)行時創(chuàng)建的代理方式被成為動態(tài)代理(生成相應(yīng)的class文件在加入jvm)。 我們上面靜態(tài)代理的例子中,代理類(ManProxy實現(xiàn)了接口和業(yè)務(wù)調(diào)用)是自己定義好的,在程序運(yùn)行之前就已經(jīng)編譯完成。然而動態(tài)代理,代理類并不是在代碼中定義的,而是在運(yùn)行時根據(jù)我們在代碼中的“指示”動態(tài)生成的, 動態(tài)代理的優(yōu)勢在于可以很方便的對代理類的函數(shù)進(jìn)行統(tǒng)一的處理,而不用修改每個代理類中的方法。動態(tài)代理分為兩種,一種分為基于接口的jdk代理;另一種則是基于類的代理如cglib(spring aop 實現(xiàn)),javassist等。

jdk 動態(tài)代理

JVM根據(jù)傳進(jìn)來的 業(yè)務(wù)實現(xiàn)類對象 以及 方法名 ,動態(tài)地創(chuàng)建了一個代理類的class文件并被字節(jié)碼引擎執(zhí)行,然后通過該代理類對象進(jìn)行方法調(diào)用。我們需要做的,只需指定代理類的預(yù)處理、調(diào)用后操作;在java的java.lang.reflect包下提供了一個Proxy類和一個InvocationHandler接口,實現(xiàn)InvocationHandler接口就可以生成被代理的對象。

  1. 假如如以上person 接口不變
  2. 業(yè)務(wù)邏輯的具體實現(xiàn)
public class RealPerson implements Person {
    @Override
    public void meetGirl() {
        System.out.println("他們說你最美,來自jdk動態(tài)代理");
    }
}

3 . 實現(xiàn)InvocationHandler接口定義代理類

//實現(xiàn)invocationHandler 接口的jdk動態(tài)代理類
public class SubjectHandler implements InvocationHandler {

    //被代理的對象,包含相應(yīng)的業(yè)務(wù)方法
    private Object target;
    //綁定被代理的對象
    public SubjectHandler(Object target){
        this.target = target;
    }
//    //或者如下調(diào)用是會更加簡單 直接new SubjectHandler.bind(targect)
//    public Object bind(Object target) {
//        this.target = target;  //接收業(yè)務(wù)實現(xiàn)類對象
//
//        //通過反射機(jī)制,創(chuàng)建一個代理類對象實例并返回。用戶進(jìn)行方法調(diào)用時使用
//        //創(chuàng)建代理對象時,需要傳遞該業(yè)務(wù)類的類加載器、接口、handler實現(xiàn)類
//        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
//                target.getClass().getInterfaces(), this);
//    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //具體要執(zhí)行的邏輯
        Object result = null;
        System.out.println("代理前的操作-----");
        //合并邏輯,并通過return決定是否執(zhí)行
        result = method.invoke(target,args);
        System.out.println("方法處理后的操作-----");
        //執(zhí)行后續(xù)操作
        return result;
    }
}
  1. 測試
public class JdkProxyClient {

    @Test
    public void test(){
        //需要代理的目標(biāo)對象
        RealPerson realPerson = new RealPerson();
        //攔截器
        SubjectHandler interceptor = new SubjectHandler(realPerson);
        //生成代理類對象,執(zhí)行代理類的業(yè)務(wù)方法
        //newProxyInstance 參數(shù):1.目標(biāo)類的加載器 2.目標(biāo)類接口 3.攔截器(處理的類)
        Person person = (Person) Proxy.newProxyInstance(realPerson.getClass().getClassLoader(),realPerson.getClass().getInterfaces(),interceptor);
        //執(zhí)行代理的業(yè)務(wù)方法
        person.meetGirl();
    }
}
spring 中的cglib 動態(tài)代理

JDK動態(tài)代理的代理對象在創(chuàng)建時,需要根據(jù)接口內(nèi)的方法名進(jìn)行調(diào)用,如果業(yè)務(wù)實現(xiàn)類是沒有實現(xiàn)接口或者業(yè)務(wù)實現(xiàn)類中新增了接口中沒有的方法,就無法使用JDK動態(tài)代理了(因為無法被調(diào)用),spirng 的cglib是針對實現(xiàn)代理的需要實現(xiàn)MethodInterceptor 接口

  1. 假設(shè)以上實現(xiàn)類不變,接口可有可無,則定義代理類如下
//基于spring 的cglib的動態(tài)代理
public class CglibInterceptor implements MethodInterceptor {
    //代理對象
    private Object target;
    //綁定被代理的對象
    public Object createProxyObject(Object obj) {
        this.target = obj;
        //創(chuàng)建代理類
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(obj.getClass());
        enhancer.setCallback(this);
        Object proxyObj = enhancer.create();
        return proxyObj;// 返回代理對象
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        Object result = null;
        //執(zhí)行業(yè)務(wù)邏輯
        System.out.println("我是cglib代理");
        //合并邏輯
        result = method.invoke(target,objects);
        return result;  //返回結(jié)果
    }
}
  1. 測試
public class CglibProxyClient {
    @Test
    public void test(){
        //目標(biāo)對象
        RealPerson realPerson = new RealPerson();
        //攔截器
        CglibInterceptor cglibInterceptor = new CglibInterceptor();
        //獲得代理對象,區(qū)別jdk動態(tài),不需要接口
        RealPerson cglib = (RealPerson) cglibInterceptor.createProxyObject(realPerson);
        cglib.meetGirl();

    }
}
image.png

總結(jié)

1.JDK動態(tài)代理只能對實現(xiàn)了接口的類生成代理,通過Proxy.newProxyInstance產(chǎn)生代理對象而不能針對類。
2.CGLIB是針對類實現(xiàn)代理,主要是對指定的類生成一個子類,覆蓋其中的方法

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容