天天看點

Spring的靜态代理和動态代理

Spring的靜态代理和動态代理

文章目錄

一、前言

二、分類

2.1、靜态代理

2.2、動态代理

2.2.1、分類

2.2.2、對比

三、實作

3.1 靜态代理

3.1 動态代理

3.1.1基于jdk的動态代理

3.1.2基于cglib的動态代理

四、結語

        開始接觸代理是在設計模式動态代理中了解的,大概是這樣的:張三喜歡一個女孩,但是她不敢表白怎麼辦,很簡單,他找李四幫他去表白。這裡李四就是代理對象,代替張三幹活,可以看下面的畫面:

        慢慢積累之後,發現很多經典的架構背後都使用了代理的模式,例如Spring的AOP實作原理便是動态代理;常用的ORM模型中mybatis架構實作原理也是動态代理;事務的實作原理反射+動态代理;dubbo的實作中也有動态代理…很多很多,我們可以得出的結論是熟悉代理以及熟悉動态代理的重要性。代理模式是:通過代理控制對目标對象的通路,下面是代理類圖,我們先對代理有個整體認識。

       代理分為靜态代理和動态代理,他們的差別是靜态代理有真實的代理類存在,就是我們會代碼中建立一個代理類,并在代理類的方法中調用目标對象的方法,以此來完成代理的工作。動态代理的代理類沒有在代碼中建立一個代理類,而是在運作時在JVM裡面建立代理對象。

       我們知道靜态代理是有實實在在的代理類存在,并且和目标類實作相同的接口。他的特點是效率高,因為所有的類都是已經編寫完成的,用戶端隻需要取得代理對象并且執行即可,同時他可以實作對目标對象中指定的方法進行增強。但是他也有如下缺點:

與目标類實作相同的接口代碼備援

如果接口發生改變,代理類中的方法也要修改

代理類服務于一種類型的對象,如果要服務多類型的對象,那麼勢必要為每種類型的對象都生成代理類

       與靜态代理的寫死方式相比,動态代理支援運作時動态生成代理對象這種方式。動态代理能用很少的代碼對一個類的所有方法實作一樣的增強效果,但是不能增強其中的某一個方法,他的好處很明顯,在編碼時代理邏輯與業務邏輯互相獨立,各不影響,減少侵入,降低耦合。

       動态代理可以分為兩種,一種是基于JDk的動态代理一種是基于CGLIB的動态代理。在spring中使用他們的差別是:當目标對象有實作接口時,預設使用jdk動态代理的方式,當目标類沒有實作接口時使用cglib的動态代理方式,并且代理對象以目标對象為父類。

jdk cglib

依賴 基于spring,不需要引用 需要引用第三方類庫

方式 有實作的接口,常見是mybatis的mapper檔案是代理 沒有,原理目标對象作為父類

對targetObject要求 實作接口 不能用final修飾

生成對象的技術 反射,使用了動态生成位元組碼技術 使用位元組碼技術,使用了動态生成位元組碼技術

       沒有代碼的講理論等于耍流氓,下面分享一下我在實踐中的code,同時進行适當的補充。

       有代理類,實實在在的存在代理類,是動态代理的理論基礎,這裡有一個work接口,接口中有一個唱歌的方法,我們想通過通路經紀人(代理對象)來達到讓明星(目标對象)唱歌的方法。

//接口

public interface Work {

void sing();           

}

//目标對象

public class TargetObj implements Work {

@Override
public void sing() {
    System.out.println("明星在唱歌");;
}           

//代理對象

public class ProxyObj implements Work {

Work obj=null;
public ProxyObj() {
    obj=new TargetObj();
}

@Override
public void sing() {
    System.out.println("先到經濟人");
    obj.sing();
}           
void sing();           
@Override
public void sing() {
    System.out.println("明星在唱歌");;
}           

public class MainTest {

//JDK方式發的動态代理
public static void main(String[] args) {
    //1.建立真實的對象
    Work targetObj=new TargetObj();
    //2.建立代理對象
    Work proxyObj = (Work) Proxy.newProxyInstance( 
            targetObj.getClass().getClassLoader(),//真實類使用什麼類加載器
            targetObj.getClass().getInterfaces(),//真實實作的接口
            new InvocationHandler() { //調用處理器
                /**
                 * 處理器的方法
                 * @param proxy 參數一:代理對象,一般不會使用
                 * @param method 參數二:外面的代理對象調用的方法引用,代理對象.sing(),method便是sing這個方法的引用
                 * @param args 參數三:外面的代理對象調用的方法裡面的參數。代理對象.sing(“參數"),args就是“參數”
                 * @return
                 * @throws Throwable
                 */
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    //反射調用
                    return method.invoke(targetObj,args);//方法的傳回參數
                }
            }
    );
    //3讓代理工作
    proxyObj.sing("北冰洋");
}           

       從代碼中我們可以get到兩點

基于jdk的動态代理需要有實作的接口

動态代理的核心:是建立了處理器 InvocationHandler執行個體

在調用目标對象時,會調用代理對象,代理對象中請求目标對象,invoke方法便是調用目标對象的方法生成代理對象的過程,同時增強工作也是在invoke方法中進行的。

<dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>3.2.12</version>
    </dependency>
           

public class TargetObj2 {

public String sing(String args) {
    System.out.println("明星在唱歌");
    return args;
}
public void dance() {
    System.out.println("明星在跳舞");
}           
public static void main(String[] args) {
    //1.真實對象
    TargetObj2 targetObj2=new TargetObj2();
    //2.建立代理對象
    Enhancer enhancer=new Enhancer();
    enhancer.setSuperclass(targetObj2.getClass());
    enhancer.setCallback(new MethodInterceptor() {
        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            //增強功能
            System.out.println("123");
            return method.invoke(targetObj2,objects);
        }
    });
    //建立代理
    TargetObj2 proxyObj = (TargetObj2) enhancer.create();
    //讓代理工作
    String ret= proxyObj.sing("丁香花");
    System.out.println(ret);
}           

       從cglib中我們可以get到兩點

基于cglib的動态代理是沒有實作接口的,他是以目标類作為代理類的父類

cglib動态代理的關鍵是基于enhancer.setCallback方法,和其中的MethodInterceptor() 方法

在MethodInterceptor執行個體中重寫intercept方法中調用invoke方法設定代理對象,最後通過enhancer.create()建立代理對象。

       理論知識會比較空洞和抽象,但是實踐一下我們會有不一樣的收獲。

       感謝您寶貴的閱讀時間。

作者:Viola_tt

來源:CSDN

原文:

https://blog.csdn.net/SweetyoYY/article/details/90743578

版權聲明:本文為部落客原創文章,轉載請附上博文連結!