需求:有一個接口,裏面有多個方法,如今須要記錄每一個方法的開始與結束。
//接口以下java
public interface Calculator { int add(int i,int j); int sub(int i,int j); }
//接口實現以下程序員
public class CalculatorImpl implements Calculator{ @Override public int add(int i, int j) { int result = i+j; return result; } @Override public int sub(int i, int j) { int result = i-j; return result; } }
1.常規作法,在每一個方法的開始與結束的地方加上log。以下spring
public class CalculatorImpl implements Calculator{ @Override public int add(int i, int j) { System.out.println("The method add begin["+i+","+j+"]"); int result = i+j; System.out.println("The method add end"+result); return result; } @Override public int sub(int i, int j) { System.out.println("The method sub begin["+i+","+j+"]"); int result = i-j; System.out.println("The method sub end"+result); return result; } }
1.1 測試代碼數據庫
public class Main { public static void main(String[] args) { Calculator calculator = new CalculatorImpl(); calculator.add(1, 2); calculator.sub(4, 2); } }
1.2 輸出結果編程
The method add begin[1,2] The method add end3 The method sub begin[4,2] The method sub end2
2.很明顯上述能夠知足每一個方法執行先後輸出log的要求,可是若是輸出的內容稍微改一點,那麼將是大面積的修改log,所以上述方法不是很適合大型項目的log需求。
2.1 採用動態代理方法實現,添加代理類框架
package com.test.aop; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.Arrays; public class CalculatorProxy { //要代理的對象 private Calculator target; //返回代理對象 public Calculator getProxy(){ Calculator proxy = null; //代理對象由哪個類加載器負責加載 ClassLoader loader = target.getClass().getClassLoader(); //代理對象的類型,即其中有哪些方法 Class [] interfaces = new Class[]{Calculator.class}; //當調用代理對象的方法時,執行該代碼 InvocationHandler h = new InvocationHandler() { /** * proxy:正在執行的代理對象,通常不使用該對象 * method:正在被調用的方法 * args:調用方法時傳入的參數 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String methodName = method.getName(); //日誌開始 System.out.println("The method"+methodName+" begin "+Arrays.asList(args)); //執行方法 Object oj = method.invoke(target, args); //日誌結束 System.out.println("The method"+methodName+" end "+oj); return oj; } }; proxy = (Calculator) Proxy.newProxyInstance(loader, interfaces, h); return proxy; } public CalculatorProxy(Calculator target) { this.target = target; } }
2.2 main方法ide
public class Main { public static void main(String[] args) { Calculator target = new CalculatorImpl(); Calculator proxy = new CalculatorProxy(target).getProxy(); int result = proxy.add(1, 2); System.out.println(result); result = proxy.sub(4, 2); System.out.println(result); } }
2.3輸出測試
The methodadd begin [1, 2] The methodadd end 3 3 The methodsub begin [4, 2] The methodsub end 2 2
3.如上的方法徹底能夠知足需求,可是不可能每一個程序員都懂動態代理,加之spring框架自己也提供了相應的解決方法,即AOP(面向切面編程)。舉個簡單的實例,一個業務邏輯由以下部分組成:
1>驗證參數
2>前置日誌
3>方法,即核心實現
4>後置日誌
AOP經常使用術語:
1.切面:如上業務邏輯組成部分,每一個點都是一個切面。
2.通知:切面完成的功能。
3.目標:被通知的對象,即如上業務邏輯組成部分中的 方法。
4.代理:它是一個對象,它的做用是通知目標對象。
5.鏈接點:指的是程序的某個位置,例如:某個方法被調用以前、調用以後,或是拋出異常的位置。
6.切點:每一個類都擁有多個鏈接點,AOP經過切點定位到指定的鏈接點。例如:鏈接點至關於數據庫中的記錄(記錄有多條),切點至關於查詢條件,它能查詢到某一條記錄。開始調用切面的時候叫作切點,簡單的理解爲在哪裏開始調用日誌、事務的地方。this