Spring AOP系列(一)— 代理模式

Spring AOP系列(一)— 代理模式

AOP(Aspect Oriented Programming)並無創造或使用新的技術,其底層就是基於代理模式實現。所以咱們先來學習一下代理模式。java

基本概念

定義

代理模式,爲對象提供一種代理,以控制對這個對象的訪問。spring

角色

代理模式也稱爲委託模式,通常有如下三個角色
Alt 代理模式的通用類圖設計模式

  • 抽象主題角色:抽象主題類能夠是抽象類也能夠是接口,是一個最普通的業務類型定義,無特殊要求。
  • 具體主題角色:也被稱爲被委託角色、被代理角色,是具體業務邏輯的實際執行者。
  • 抽象主題角色:也被稱爲委託類,代理類。負責控制具體主題角色的訪問和應用,而且能夠在具體主題角色的業務邏輯執行先後進行預處理和後處理的邏輯。
    從「具體主題角色」的定義咱們能夠明確一點:代理模式中的被代理類和代理類的關係,並不是是說代理類幫助被代理類完成了全部的工做。反而是最核心的工做仍然是由被代理類本身來完成。代理類在其中起的做用就是控制被代理類的訪問、應用,以及在被代理類執行核心業務邏輯的先後進行其它的處理。
    下面的實踐將會幫助咱們更好地理解。

簡單代理模式的基本實現

這裏咱們先按照基本概念中內容,完成一個簡單代理模式的基本實現。設想這樣一個場景:明星與經紀人。經紀人幫明星與外界洽談合做,簽定合同,計算佣金;明星完成演戲、唱歌等工做。
首先定義一個Actor.javaide

package com.leng.proxy;

/**
 * @Classname Actor
 * @Date 2020/9/12 23:30
 * @Autor lengxuezhang
 */
public interface Actor {
    /**
     * 演戲
     */
    public void act();

    /**
     * 唱歌
     */
    public void sing();
}

藝人通常須要會唱歌和演戲。
接着分別實現明星類,實現Actor接口,並傳入姓名。函數

package com.leng.proxy;

/**
 * @Classname Star
 * @Date 2020/9/12 23:32
 * @Autor lengxuezhang
 */
public class Star implements Actor {

    private String name;

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

    @Override
    public void act() {
        System.out.println(name + "在演戲");
    }

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

最後實現經紀人學習

package com.leng.proxy;

/**
 * @Classname Agent
 * @Date 2020/9/12 23:36
 * @Autor lengxuezhang
 */
public class Agent implements Actor {

    private Actor actor = null;

    // 要想實現經紀人代理明星,須要將被代理類對象傳遞給代理類
    public Agent(Actor actor) {
        this.actor = actor;
    }
    
    @Override
    public void act() {
        this.before();
        // 實際調用的是明星的act(),經紀人接活,但最後去演戲的確定是明星本身
        actor.act();
        this.after();
    }

    @Override
    public void sing() {
        this.before();
        actor.sing();
        this.after();
    }

    // 預處理方法
    private void before() {
        System.out.println("談好價錢,簽好合同");
    }

    // 後處理方法
    private void after() {
        System.out.println("清算演出費,和明星分錢");
    }
}

客戶端調用Client.java:測試

package com.leng;

import com.leng.proxy.Agent;
import com.leng.proxy.Star;

/**
 * @Classname Client
 * @Date 2020/9/12 2:40
 * @Autor lengxuezhang
 */
public class Client {
    public static void main(String[] args) {
        Star star = new Star("劉德華");
        Agent agent = new Agent(star);
        agent.act();
        agent.sing();
    }
}

運行結果:this

談好價錢,簽好合同
劉德華在演戲
清算演出費,和明星分錢
談好價錢,簽好合同
劉德華在唱歌
清算演出費,和明星分錢設計

小結代理

  • 代理模式實現的關鍵是,代理類持有被代理類對象,這樣才能執行被代理類自己想要執行的業務邏輯。
  • 代理類能夠定義本身的預處理和後處理方法,使之在被代理類的業務邏輯先後執行。
    以上被稱爲基於代理模式概念的一個例子,實際上代理模式在不一樣場景或要求下會有一些新的特性。如接下來要介紹的普通代理和強制代理。

代理模式的擴展

普通代理

明星的工做通常比較忙,沒有時間和外界有太多溝通和接觸。導演想要找明星演戲,通常只能先去聯繫他的公司經紀人,是聯繫不到明星本人。「導演只能聯繫到經紀人,而不能聯繫明星」,這樣的模式就被稱爲「普通代理」。換句話說:普通代理模式下,客戶端只能訪問代理類,不能訪問被代理類。
上面的Client.java中,直接new了一個明星對象,這就算是直接訪問了被代理類,不能稱爲普通代理。那如何才能實現普通代理呢。關鍵是讓被代理類的建立只能由代理類來完成。修改代碼以下:
只須要修改構造函數

package com.leng.proxy;

/**
 * @Classname Star
 * @Date 2020/9/12 23:32
 * @Autor lengxuezhang
 */
public class Star implements Actor {

    private String name;

    public Star(Agent agent, String name) throws Exception {
        // 必須是經紀人才能建立明星
        if(agent == null) {
            throw new Exception("非經紀人,不能創造明星");
        } else {
            this.name = name;
        }
    }

    @Override
    public void act() {
        System.out.println(name + "在演戲");
    }

    @Override
    public void sing() {
        System.out.println(name + "在唱歌");
    }
}
package com.leng.proxy;

/**
 * @Classname Agent
 * @Date 2020/9/12 23:36
 * @Autor lengxuezhang
 */
public class Agent implements Actor {

    private Actor actor = null;

    // 告訴agent,我須要聯繫哪個"明星"
    public Agent(String name) {
        try {
            this.actor = new Star(this, name);
        } catch (Exception e) {
            System.out.println("建立agent異常");
        }
    }

    @Override
    public void act() {
        this.before();
        actor.act();
        this.after();
    }

    @Override
    public void sing() {
        this.before();
        actor.sing();
        this.after();
    }

    // 預處理方法
    private void before() {
        System.out.println("談好價錢,簽好合同");
    }

    // 後處理方法
    private void after() {
        System.out.println("清算演出費,和明星分錢");
    }
}

客戶端類在使用時,也須要作修改,此時客戶端能夠不直接訪問Star

package com.leng;
import com.leng.proxy.Agent;
/**
 * @Classname Client
 * @Date 2020/9/12 2:40
 * @Autor lengxuezhang
 */
public class Client {
    public static void main(String[] args) {
        Agent agent = new Agent("劉德華");
        agent.act();
        agent.sing();
    }
}

運行結果:

談好價錢,簽好合同
劉德華在演戲
清算演出費,和明星分錢
談好價錢,簽好合同
劉德華在唱歌
清算演出費,和明星分錢
能夠看到運行結果並無發生改變,但確實已經實現了只能以訪問代理類方式訪問非代理類。假如客戶端直接訪問代理類的話,是會拋出異常的。
普通代理模式的優勢:

  • 調用者不關心被代理類的實現,被代理類的修改不會影響高層模塊的調用。
  • 被代理類的訪問權限收口在代理類。

強制代理

先來看一個場景:

王晶想找華仔拍戲,兩人老相識了,因而王晶直接打電話問華仔:「華仔啊,有咩時間?搵你拍片喔"」。
華仔回覆:「你先找個人經紀人談一下吧,看一下個人工做安排」。
而後華仔給王晶本身經紀人的聯繫方式,王晶和經紀人談好以後,華仔就準備去拍王晶的電影了。
這個場景裏有個點,首先王晶其實是不能直接找華仔拍戲,即便有聯繫方式,華仔也不必定給你拍;其次是王晶必須與華仔指定的經紀人溝通具體的拍戲工做。
強制代理的要求是:

  • 不能直接經過代理類訪問被代理類。
  • 不能直接訪問被代理類。
  • 只能聽過被代理類指定的代理類訪問被代理類。
    接下來逐個講解如何修改以前的代碼。首先抽象主題角色中須要增長一個獲取本身代理類的方法,其它不變:
public interface Actor {
	...
    public Actor getProxy();
}

明星類實現該方法,一方面要找到本身的代理,另外一方面其它的唱歌、演戲方法也須要檢查是不是本身指定的代理類訪問的。

package com.leng.proxy;

/**
 * @Classname Star
 * @Date 2020/9/12 23:32
 * @Autor lengxuezhang
 */
public class Star implements Actor {

    private String name;

    private Agent agent = null;

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

    @Override
    public void act() {
        if (isProxy()) {
            System.out.println(name + "在演戲");
        } else {
            System.out.println("請使用指定的代理訪問");
        }
    }

    @Override
    public void sing() {
        if (isProxy()) {
            System.out.println(name + "在唱歌");
        }else {
            System.out.println("請使用指定的代理訪問");
        }
    }

    @Override
    public Actor getProxy() {
        this.agent = new Agent(this);
        return this.agent;
    }

    /**
     * 判斷是不是指定的代理
     * @return
     */
    private boolean isProxy() {
        if(agent == null) {
            return false;
        }
        return true;
    }
}

經紀人類

package com.leng.proxy;

/**
 * @Classname Agent
 * @Date 2020/9/12 23:36
 * @Autor lengxuezhang
 */
public class Agent implements Actor {

    private Actor actor = null;

    // 告訴agent,我須要聯繫哪個"明星"
    public Agent(Actor actor) {
        this.actor = actor;
    }

    @Override
    public void act() {
        this.before();
        actor.act();
        this.after();
    }

    @Override
    public void sing() {
        this.before();
        actor.sing();
        this.after();
    }

    @Override
    public Actor getProxy() {
        //經紀人沒有本身的經紀人,因此就返回本身
        return this;
    }

    // 預處理方法
    private void before() {
        System.out.println("談好價錢,簽好合同");
    }

    // 後處理方法
    private void after() {
        System.out.println("清算演出費,和明星分錢");
    }
}

如今來測試一下,首先看看若是直接訪問被代理類,是否可行

public class Client {
    public static void main(String[] args) {

        Star star = new Star("劉德華");
        star.act();
        star.sing();

    }
}

運行結果:

請使用指定的代理訪問
請使用指定的代理訪問

顯然訪問失敗了。接下來嘗試一下若是經過代理類訪問呢

package com.leng;

import com.leng.proxy.Actor;
import com.leng.proxy.Agent;
import com.leng.proxy.Star;

/**
 * @Classname Client
 * @Date 2020/9/12 2:40
 * @Autor lengxuezhang
 */
public class Client {
    public static void main(String[] args) {
        Actor star = new Star("劉德華");
        Actor agent = new Agent(star);
        agent.act();
        agent.sing();
    }
}

談好價錢,簽好合同
請使用指定的代理訪問
清算演出費,和明星分錢
談好價錢,簽好合同
請使用指定的代理訪問
清算演出費,和明星分錢
能夠看到中間業務邏輯部分仍是錯誤的。ok,那我就按照強制代理的規定來:

package com.leng;

import com.leng.proxy.Actor;
import com.leng.proxy.Star;

/**
 * @Classname Client
 * @Date 2020/9/12 2:40
 * @Autor lengxuezhang
 */
public class Client {
    public static void main(String[] args) {
        // 先定義一個明星
        Actor star = new Star("劉德華");
        // 由這個明星指定經紀人
        Actor agent = star.getProxy();
        agent.act();
        agent.sing();
    }
}

談好價錢,簽好合同
劉德華在演戲
清算演出費,和明星分錢
談好價錢,簽好合同
劉德華在唱歌
清算演出費,和明星分錢
運行結果顯示成功。

小結:強制代理的核心就是經過被代理類指定的代理類,去訪問被代理類的方法。

總結

本篇文章首先介紹了代理模式的基本概念和基本代碼實現。而後進行了一些擴展,介紹了普通代理和強制代理的概念和實現。下一篇將會詳細介紹動態代理的知識。

參考文獻 《設計模式之禪》

相關文章
相關標籤/搜索