代理模式


typora-copy-images-to: img

代理模式

代理模式(Proxy Pattern)的定義很簡單,是指為其他對象提供一種代理,以控制對這個對象的訪問。

代理對象在客服端和目標對象之間起到中介作用,代理模式屬于結構型設計模式。

使用代理模式主要有兩個目的:

  • 保護目標對象
  • 增強目標對象

靜態(tài)代理

顯式聲明被代理對象

靜態(tài)代理其實在平時的開發(fā)當中應用非常廣泛,最常見的就是三層架構

  • UML類圖
靜態(tài)代理.png
  • 代碼:

頂層接口 Person

package com.pmz.proxy;

public interface Person {
    
    public void getGril();
}

被代理對象 User 實現(xiàn)Person接口

package com.pmz.proxy.staticproxy;

import com.pmz.proxy.Person;

public class User implements Person {

    @Override
    public void getGril() {
        System.out.println("我想找個女朋友");
    }
}

代理類 UserStaticProxy,持有被代理對象的引用,以及在調用被代理對象前后進行代碼增強

package com.pmz.proxy.staticproxy;

public class UserStaticProxy {
    //顯示聲明被代理對象 user
    private User user ;

    public UserStaticProxy (User user){
        this.user = user;
    }

    public void getGirl(){
        System.out.println("找對象的前提要有房有車");
        user.getGril();
        System.out.println("一見鐘情,準備結婚");
    }
}

調用端 UserAction 只需要把被代理對象傳給代理類,就可以直接調用執(zhí)行。

package com.pmz.proxy.staticproxy;

public class UserAction {

    public static void main(String[] args) {

        UserStaticProxy usp = new UserStaticProxy(new User());
        usp.getGirl();
    }
}

Person是頂層接口,User是真實對象(被代理對象),UserStaticProxy是代理對象,代

理對象持有被代理對象的引用,客戶端調用代理對象方法,同時也調用被代理對象的方

法,但是在代理對象前后增加一些處理。

在代碼中,我們想到代理,就會理解為是代碼 增強,其實就是在原本邏輯前后增加一些邏輯,而調用者無感知。

缺點:如果再來一個Cat也要找對象,那就沒辦法調用這個代理,就是說不具備擴展性,這個UserStaticProxy只為User類代理。


動態(tài)代理

動態(tài)代理和靜態(tài)對比基本思路是一致的,只不過動態(tài)代理功能更加強大,隨著業(yè)務的擴 展適應性更強。

jdk動態(tài)代理

實現(xiàn)動態(tài)代理的前提是被代理對象必須要實現(xiàn)接口

  • UML類圖
jdk動態(tài)代理.png
  • 代碼:

Person和User跟上面的靜態(tài)代理一樣,保持不變

UserDynamicProxy

package com.pmz.proxy.dynamic.jdkd;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class UserDynamicProxy implements InvocationHandler {
    //通用類引用
    private Object object ;

    public Object getInstance(Object object){
        this.object = object;
        //通過反射獲取被代理類的所有接口實現(xiàn)方法
        Class<?> clazz = object.getClass();
        //返回對象是新生成的一個以$Proxy0開頭的對象,繼承UserDynamicProxy代理類
        //  并實現(xiàn)頂層接口Person
        return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        Object obj = method.invoke(this.object,args);
        after();
        return obj;
    }

    private void before() {
        System.out.println("找對象的前提要有房有車");
    }

    private void after() {
        System.out.println("一見鐘情,準備結婚");
    }
}

UserAction

package com.pmz.proxy.dynamic.jdkd;

import java.lang.reflect.Method;

public class UserAction {

    public static void main(String[] args) {

        try {
            Object obj = new UserDynamicProxy().getInstance(new User());
            //通過jdk靜態(tài)代理拿到的對象已經不是User了
            //而是新生成的一個以$Proxy0開頭的對象,繼承UserDynamicProxy代理類
            System.out.println(obj);
            //通過反射獲取被代理類的指定實現(xiàn)方法
            Method method = obj.getClass().getMethod("getGril",null);
            method.invoke(obj);

        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}
  • 輸出

JDK動態(tài)代理的新生成實現(xiàn)類都是以$Proxy開頭,后面的數(shù)字是以第幾個實現(xiàn)類為基礎計算的。

該實現(xiàn)類 繼承代理對象UserDynamicProxy,并且實現(xiàn)頂層接口Person

class com.sun.proxy.$Proxy0  
找對象的前提要有房有車
我想找個女朋友
一見鐘情,準備結婚

JDK Proxy 生成對象的步驟如下:

1、拿到被代理對象的引用,并且獲取到它的所有的接口,反射獲取。

2、JDK Proxy 類重新生成一個新的類、同時新的類要實現(xiàn)被代理類的所有接口。

3、動態(tài)生成 Java 代碼,把新加的業(yè)務邏輯方法由一定的邏輯代碼去調用(在代碼中體現(xiàn))。

4、編譯新生成的 Java 代碼.class。

5、再重新加載到 JVM 中運行。

cglib動態(tài)代理

行業(yè)內較為流行的動態(tài)代理實現(xiàn)方案,采用繼承被代理類的方式。cglib包的底層是通過使用一個小而快的字節(jié)碼處理框架ASM,來轉換字節(jié)碼并生成新的類

注意:被final修飾的方法不能使用cglib動態(tài)代理調用,因為final修飾的方法不能被繼承

  • UML類圖
cglib代理.png

老規(guī)矩看代碼

User不需要實現(xiàn)接口,就是一個干凈的類

import com.pmz.proxy.Person;

public class User{

    public void getGril() {
        System.out.println("我想找個女朋友");
    }
}

UserDynamicProxy必須實現(xiàn)MethodInterceptor(方法攔截器)接口

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class UserDynamicProxy implements MethodInterceptor {
    private Object object ;
    
    public Object getInstance(Class<?> clazz){
        //Enhancer為被代理類創(chuàng)建一個子類,
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
        return enhancer.create();
    }

    private void before() {
        System.out.println("找對象的前提要有房有車");
    }


    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        before();
        Object obj = methodProxy.invokeSuper(o,objects);
        after();
        return obj;
    }
    private void after() {
        System.out.println("一見鐘情,準備結婚");
    }
}

UserAction

import net.sf.cglib.core.DebuggingClassWriter;

public class UserAction {

    public static void main(String[] args) {

        try {
            //會把生成的3個class文件,存到E://cglib_proxy_classes目錄下面去,便于查看源碼
            System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,
                               "E://cglib_proxy_classes");
            User obj = (User) new UserDynamicProxy().getInstance(User.class);
            System.out.println(obj.getClass());
            obj.getGril();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容