[Spring 系列] Spring AOP 之代理模式①

前言

Hi 我是來自西部大嫖客傑西java

如今已是 2020 春節期間了,先祝你們新年快樂。俗話說的好,人一年四季都是懶懶的。到了春節,人也變得極其容易困,由於春困秋乏嘛。想寫點文章吧,也不知道寫一些啥,因此決定回顧如下 Spring AOP 的一些知識,也算是 2020 年,保持隨時學習的態度。git

關於系列文章

Spring AOP,其實不是一個特別難的東西。它的難點在於,要是你要理解它,你就須要具有一些必須的知識,例如說 JDK 各個版本的特性;例如說動態代理神器 Cglib;例如說 Java 的文件操做等等基礎。github

因此我決定將這一個系列分開來寫。從一點一點的知識點入手,慢慢去看到 Spring AOP 的整個佈局。apache

系列文章有:編程

  • ① Spring AOP 之代理模式
  • ② Spring AOP 之 AOP 詳解與基礎用法
  • ③ Spring AOP 之源碼解析

代理模式

代理模式其實應用很是普遍,在各類編程語言中都有大量的運用。設計模式

那麼什麼是代理模式?代理模式實際上是爲其餘對象提供一種代理以控制對這個對象的訪問。在某些狀況下,一個對象不適合或者不能直接引用另外一個對象,而代理對象能夠在客戶端和目標對象之間起到中介的做用。bash

上面的解釋來自於百度百科,是一個比較正經的含義解讀,值得咱們在學習代理模式的時候反覆理解的概念。若是你沒明白上面的話,我能夠舉一個簡單的小栗子說明一下。編程語言

在生活中,咱們或多或少會聽到一些詞,例如「代理商」/「區域代理」等等。其實這些就是現實版的代理模式。假如說身邊有一個籃球鞋代理商,其實咱們都知道他的鞋子不是他本身生產的,他只是代替實際生產鞋子的廠商拿出去賣,並且咱們也不知道實際上他的貨源到底是來自於哪裏的,由於他的鞋子有多是來自莆田,也可能來自於美國耐克。ide

從上面的例子,咱們能夠總結出一些特色:佈局

  1. 專人作專門的事情,代理商對外負責銷售,生廠商對內負責生產。
  2. 代理商能夠做爲前臺的身份,統一對接全部須要買鞋子的人,屏蔽了實際生廠商。(畢竟代理不只僅能夠掌握一家廠商的貨源,他能夠代理多家廠商)

那麼將上面的特色轉成編程的話,咱們能夠說:

  1. 專人作專門的事情,對於面向對象來講職責更加分明瞭,提升代碼的重用性,可讀性。
  2. 代理對象能夠做爲前臺,這裏有利於屏蔽真實的調用代碼,這樣有利於封裝性,同時也調高了代碼的擴展性。

代理模式的名詞

其實根據上面我講的那個小栗子,咱們能夠輕易分出這個設計模式的組成:

  1. 客戶 (在編程咱們說的是客戶端)
  2. 代理對象 (在編程咱們說的是代理類)
  3. 目標對象 (在編程咱們說的是實際操做對象)

下面下面會示例代碼可讓你加深理解。

代理模式的類型

根據程序不一樣的運行時期這個維度上區分的話,代理模式的類型分爲兩種

  • 靜態代理
  • 動態代理

靜態代理

所謂的靜態代理,簡單來講就是開發人員在開發的過程當中直接硬編碼,將代理對象與目標對象結合,而後再編譯代理類。所謂靜態也就是在程序運行前就已經存在代理類的字節碼文件,代理類和委託類的關係在運行前就肯定了。下面有一段示例代碼。

首先咱們先上一個設計模式的 UML 圖

編寫 Subject 的代碼

public interface Subject {
    public void sale();
}
複製代碼

而後編寫 RealSubject 的代碼

public class RealSubject implements Subject {
    @Override
    public void sale() {
        System.out.println("賣東西");
    }
}
複製代碼

接下來 Proxy 的代碼

public class ProxySubject implements Subject {

    private Subject realSubject;

    public ProxySubject(Subject realSubject) {
        this.realSubject = realSubject;
    }

    @Override
    public void sale() {
        realSubject.sale();
    }
}
複製代碼

最後寫一下 Client 端的代碼

public class Client {
    public static void main(String[] args) {
        Subject realSubject = new RealSubject();
        ProxySubject proxySubject = new ProxySubject(realSubject);
        proxySubject.sale();
    }
}
複製代碼

輸出:

賣東西
複製代碼

動態代理

所謂的動態代理,與靜態代理相反。動態代理是在程序編譯期或運行期,會根據條件來生成代理類。

在動態代理中主要分爲兩種技術:

  1. Cglib
  2. Java Dynamic Proxy

Cglib

這是來自 Cglib 官方學習資料

使用 Cglib 以前咱們先導入一下相關的 lib

<!-- cglib 目前最新版本是 3.3.0-->
    <dependency>
      <groupId>cglib</groupId>
      <artifactId>cglib</artifactId>
      <version>3.3.0</version>
    </dependency>

    <!-- commons-lang3 -->
    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-lang3</artifactId>
      <version>3.9</version>
    </dependency>
  </dependencies>
複製代碼

而後咱們開始編寫 RealObject 目標對象

class RealObject {
    public void execute() {
        System.out.println("賣東西");
    }
}
複製代碼

而後咱們編寫 ProxySubject 代理類

class ProxySubject implements MethodInterceptor {

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("before execute");
        Object result = methodProxy.invokeSuper(o, objects);
        System.out.println("afer execute");
        return result;
    }
}
複製代碼

編寫客戶端

public class CglibProxyTest {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(RealObject.class);
        enhancer.setCallback(new ProxySubject());
        RealObject executor = (RealObject) enhancer.create();
        executor.execute();
    }
}
複製代碼

輸出

before execute
賣東西
afer execute
複製代碼

Java Dynamic Proxy

Java JDK 自己是具有了動態代理的。

編寫接口 Subject

public interface Subject {

    public void sell();

}
複製代碼

編寫目標對象 RealSubject

public class RealSubject implements Subject {
    @Override
    public void sell() {
        System.out.println("賣東西");
    }
}
複製代碼

編寫處理器 ProxyInvocationHandler

public class ProxyInvocationHandler implements InvocationHandler {
    
    Subject subject;

    public ProxyInvocationHandler(Subject subject) {
        this.subject = subject;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before invoke ");
        subject.sell();
        System.out.println("after invoke ");
        return null;
    }
}
複製代碼

編寫客戶端 Client

public class Client {
    public static void main(String[] args) {
        Subject realSubject = new RealSubject();
        ProxyInvocationHandler myInvocationHandler =
                new ProxyInvocationHandler(realSubject);
        Subject proxyClass = (Subject) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Subject.class}, myInvocationHandler);
        proxyClass.sell();
    }
}
複製代碼

結果:

before invoke 
賣東西
after invoke 
複製代碼

拓展

代理模式與裝飾着模式的區別

其實代理模式和裝飾者模式在寫法上有不少類似的地方,可是咱們須要從設計模式的思想上對他們進行分解。

它們的區別在於:

  1. 代理模式是爲了加以控制,而裝飾者模式爲了加強功能。
  2. 代理模式強調的是透明訪問,裝飾模式強調的是自由構建。

結語

我寫代理模式的緣由是由於,這是一個比較重要的設計模式。若是咱們要理解 Spring AOP 的話,那麼咱們就必須認識並簡單運用的了代理模式。有了基本的認識,咱們就能夠去探索 Spring 到底是怎麼經過代理模式實現 AOP 的。

相關文章
相關標籤/搜索