1. 程式人生 > >Spring 詳解(一)------- AOP

Spring 詳解(一)------- AOP

1. AOP 簡介

​ AOP(Aspect Oriented Programming),通常稱為面向切面程式設計。它利用一種稱為"橫切"的技術,剖解開封裝的物件內部,並將那些影響了多個類的公共行為封裝到一個可重用模組,並將其命名為"Aspect",即切面。所謂"切面",簡單說就是那些與業務無關,卻為業務模組所共同呼叫的邏輯或責任封裝起來,便於減少系統的重複程式碼,降低模組之間的耦合度,並有利於未來的可操作性和可維護性。

2. 示例需求

想要為寫好的 ArithmeticCalculator 新增日誌 ,即每次運算前後新增
採用以下方法太過繁瑣,修改內容需要每個跟著都修改,可維護性差

public interface ArithmeticCalculator {
    int add(int i, int j);
    int sub(int i, int j);

    int mul(int i, int j);
    int div(int i, int j);
}
public class MyArithmeticCalculatorImp implements ArithmeticCalculator {
    public int add(int i, int j) {
        System.out.println("The method add begins with["+i+","+j+"]");
        int result = i + j;
        System.out.println("The method add ends with["+result+"]");
        return result;

    }

    public int sub(int i, int j) {
        System.out.println("The method sub begins with["+i+","+j+"]");
        int result = i - j;
        System.out.println("The method sub ends with["+result+"]");
        return result;
    }

    public int mul(int i, int j) {
        System.out.println("The method mul begins with["+i+","+j+"]");
        int result = i * j;
        System.out.println("The method mul ends with["+result+"]");
        return result;
    }

    public int div(int i, int j) {
        System.out.println("The method div begins with["+i+","+j+"]");
        int result = i / j;
        System.out.println("The method div ends with["+result+"]");
        return result;
    }
}

結果

The method add begins with[1,2]
The method add ends with[3]
-->3
The method mul begins with[5,2]
The method mul ends with[10]
-->10

問題:
程式碼混亂:越來越多的非業務需求(日誌和驗證等)加入後,原有的業務方法急劇膨脹,每個方法在處理核心邏輯的同時還必須兼顧替他多個關注點。

程式碼分散:以日誌需求為例,只是為了滿足這個單一需求,就不得不在多個模組(方法)裡多次重複相同的日誌程式碼,如果日誌需求發生變化,必須修改所有模組。

3. 解決方法一:使用靜態代理

建立乾淨的實現類

public class ArithmeticCalculatorImpl implements ArithmeticCalculator {
    @Override
    public int add(int i, int j) {
        return i + j;
    }
    @Override
    public int sub(int i, int j) {
        return i - j;
    }
    @Override
    public int mul(int i, int j) {
        return i * j;
    }
    @Override
    public int div(int i, int j) {
        return i / j;
    }
}

建立日誌類 MyLogger

/**
 * 建立日誌類
 */
public class MyLogger {
    /**
     * 入參日誌
     * @param a
     * @param b
     */
    public void showParam(int a, int b) {
        System.out.println("The method add begins with["+a+","+b+"]");
    }

    /**
     * 運算結果日誌
     * @param result
     */
    public void showResult(int result) {
        System.out.println("The method add ends with["+3+"]");
    }
}

建立靜態代理類

/**
 * 代理類
 */
public class ProxyLogger implements ArithmeticCalculator {

    //目標類
    private ArithmeticCalculator target;

    //日誌類
    private MyLogger logger;

    public ProxyLogger(ArithmeticCalculator target, MyLogger logger) {
        this.target = target;
        this.logger = logger;
    }
    @Override
    public int add(int i, int j) {
        logger.showParam(i, j);
        int result =  target.add(i,j);
        logger.showResult(result);
        return result;
    }
    @Override
    public int sub(int i, int j) {
        logger.showParam(i, j);
        int result =  target.sub(i,j);
        logger.showResult(result);
        return result;
    }
    @Override
    public int mul(int i, int j) {
        logger.showParam(i, j);
        int result =  target.mul(i,j);
        logger.showResult(result);
        return result;
    }
    @Override
    public int div(int i, int j) {
        logger.showParam(i, j);
        int result =  target.div(i,j);
        logger.showResult(result);
        return result;
    }
}

結果測試

public class Main {
    public static void main(String[] args) {
        ArithmeticCalculator arithmeticCalculator = new ArithmeticCalculatorImpl();
        MyLogger logger = new MyLogger();
        ProxyLogger proxy = new ProxyLogger(arithmeticCalculator, logger);
        System.out.println(proxy.add(1, 9));
        System.out.println(proxy.mul(3, 3));

    }
}
/**
The method add begins with[1,9]
The method add ends with[3]
10
The method add begins with[3,3]
The method add ends with[3]
9
*/

總結

這是一個很基礎的靜態代理,業務類 ArithmeticCalculatorImpl 只需要關注業務邏輯本身,保證了業務的重用性,這也是代理類的優點,沒什麼好說的。我們主要說說這樣寫的缺點:

  • 代理物件的一個介面只服務於一種型別的物件,如果要代理的方法很多,勢必要為每一種方法都進行代理,靜態代理在程式規模稍大時就無法勝任了。

  • 如果介面增加一個方法,比如 ArithmeticCalculatorImpl 增加歸零 changeZero()方法,則除了所有實現類需要實現這個方法外,所有代理類也需要實現此方法。增加了程式碼維護的複雜度。