日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

代理模式(靜態代理和動態代理)

作者:龍兄你好呀 更新時間: 2022-07-09 編程語言

一、代理模式

代理模式是為某一個對象(委托類)提供一個代理(代理類),用來控制對這個對象的訪問。委托類和代理類有一個共同的父類或父接口,代理類會對請求做預處理、過濾,將請求分配給指定的對象。

生活中的常見代理有:租房中介、婚慶公司等。

代理類的兩個設計原則:

  • 代理類與委托類具有相似的行為
  • 代理類增強委托類的行為

常用的代理模式:

  • 靜態代理
  • 動態代理

二、靜態代理

1.代理三要素 (以結婚為例)

  • 有共同的行為(結婚)- 定義接口
  • 目標角色/真實角色(新人)- 實現接口
  • 代理角色(婚慶公司)- 實現接口 增強用戶行為

2.代碼

(1)有共同的行為(結婚)- 定義接口

public interface Marry {
    void toMarry();
}

(2)目標角色/真實角色(新人)- 實現接口

public class You implements Marry{
    @Override
    public void toMarry() {
        System.out.println("我結婚了");
    }
}

(3)代理角色(婚慶公司)- 實現接口 增強用戶行為

public class MarryProxy implements Marry{

    private Marry target;

    public MarryProxy(Marry target) {
        this.target = target;
    }

    @Override
    public void toMarry() {
        before();
        target.toMarry();
        after();
    }

    private void after() {
        System.out.println("百年好合");
    }

    private void before() {
        System.out.println("婚禮現場布置中");
    }
}

(4)測試

public static void main(String[] args) {
        You you = new You();
        MarryProxy marryProxy = new MarryProxy(you);
        marryProxy.toMarry();
    }

結果如下:
在這里插入圖片描述

3.靜態代理特點

  • 目標角色固定
  • 在應用程序之前就得知目標角色
  • 代理對象會增強目標對象的行為
  • 有可能存在多個代理,產生“類爆炸”

三、動態代理

可以根據需要,通過反射機制在程序運行期,動態的為目標對象創建代理對象。

動態代理的兩種實現方式:

  • JDK動態代理
  • CGLIB動態代理

動態代理的特點:

  • 目標對象不固定
  • 在程序運行時,動態創建目標對象
  • 代理對象會增強目標對象的行為

四、JDK動態代理

1.實現過程

public class JdkHandler implements InvocationHandler {

    private Object target;

    public JdkHandler(Object target) {
        this.target = target;
    }

    //得到代理對象
    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("方法執行前");
        Object object = method.invoke(target, args);
        System.out.println("方法執行后");
        return object;
    }
}

(1)定義一個動態代理類,該類去實現InvocationHandler這個接口。
(2)在該類中定義一個方法,使用Proxy.newProxyInstance得到代理對象,newProxyInstance有三個參數,如下:

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
  • ClassLoader loader:該動態代理類的類加載器
  • Class<?>[] interfaces:目標對象的接口數組
  • InvocationHandler h:傳入InvocationHandler 接口的實現類,在這里填入的是該動態代理類,因為該動態代理類去實現了InvocationHandler 這個接口。

(3)重寫InvocationHandler中的invoke方法,調用目標對象的方法并且增強目標對 象的行為。invoke方法同樣有三個參數,如下:

public Object invoke(Object proxy, Method method, Object[] args) 
  • Object proxy:調用該方法的代理實例
  • Method method:目標對象的方法
  • Object[] args:目標對象的方法所需要的參數

(4)測試:

public static void main(String[] args) {
        You you = new You();
        JdkHandler jdkHandler = new JdkHandler(you);
        Marry proxy = (Marry)jdkHandler.getProxy();
        proxy.toMarry();
    }

結果:
在這里插入圖片描述
以上結果說明,當執行到proxy.toMarry()這行代碼時,底層會自動去調用動態代理類JdkHandler 中的invoke方法。

2.執行流程

在這里插入圖片描述

五、CGLIB動態代理

JDK的動態代理機制只能代理實現了接口的類,而不能實現接口的類就不能使用JDK的動態代理。
cglib是針對類來實現代理的,它的原理是對指定的目標類生成一個子類,并覆蓋其中方法實現增強,但因為采用的是繼承,所以不能對final修飾的類進行代理。

cglib動態的原理如下圖:
在這里插入圖片描述

可以看到,每次調用代理類的方法都會被攔截器所攔截,在攔截器中才是調用目標類的該方法的邏輯。

1.引入依賴

<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>

2.實現過程

public class CglibProxy implements MethodInterceptor {

    private Object target;

    public CglibProxy(Object target) {
        this.target = target;
    }

    public Object getProxy(){
        Enhancer enhancer = new Enhancer();
        //設置目標類成為父類
        enhancer.setSuperclass(target.getClass());
        //設置回調 回調對象為本身
        enhancer.setCallback(this);
        //生成代理類對象并返回
        return enhancer.create();
    }
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {

        System.out.println("方法執行前");
        Object object = methodProxy.invoke(target, objects);
        System.out.println("方法執行后");
        return object;
    }
}

(1)定義一個類去實現MethodInterceptor 接口,并且重寫intercept攔截器方法。
(2)在類中定義一個方法getProxy,在方法中設置:讓目標類成為該動態代理類的父類,并且設置回調對象為本身。最后通過create方法返回生成的代理對象。
(3)在intercept攔截器方法調用目標類的方法,并且增強行為目標類的行為或寫上一些邏輯。
(4)測試:

public static void main(String[] args) {
        You you = new You();
        CglibProxy cglibProxy = new CglibProxy(you);
        You proxy = (You) cglibProxy.getProxy();
        proxy.toMarry();
    }

結果:
在這里插入圖片描述

以上結果說明,每次動態代理對象調用目標對象的方法時,都會被攔截器攔截,然后運行攔截器中的代碼。

六、JDK代理和CGLIB代理的區別

  • JDK動態代理實現接口,Cglib動態代理繼承思想
  • JDK動態代理(目標對象存在接口時)執行效率高于Cglib
  • 如果目標對象有接口實現,選擇JDK代理,如果沒有接口實現選擇Cglib代理

原文鏈接:https://blog.csdn.net/rqt1013_/article/details/125623094

欄目分類
最近更新