Spring 詳解(一)------- AOP
阿新 • • 發佈:2018-12-31
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()方法,則除了所有實現類需要實現這個方法外,所有代理類也需要實現此方法。增加了程式碼維護的複雜度。