來,帶你手撕一個AOP
本文主要講的是如何使用JDK動態代理 實現簡單的AOP。AOP是啥?如果你想在某些方法執行前後插入一些通用的處理,你可以考慮AOP。
預備知識
Proxy
JDK中提供了一個Proxy
類用於建立動態代理物件的靜態方法,如果在程式中為一個或多個介面動態地生成實現類,就可以使用Proxy來建立動態代理類。Proxy
提供了下面的方法來建立動態代理例項:
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
該代理物件的實現類實現了interfaces所指定的系列介面,執行代理物件的每個方法時都會被替換執行InvocationHandler
物件的invoke
方法
InvocationHandler
InvocationHandler是一個介面,在建立動態代理例項的時候需要把一個InvocationHandler
的實現類傳進去,那麼執行代理物件的每個方法時就會被替換成執行InvocationHandler實現類中的invoke方法,至於這個invoke方法要怎麼實現,就由使用者自己決定。
反射中Method的invoke()
在java.lang.reflect.Method
包中有一個方法
public Object invoke(Object obj, Object... args)
該方法可以實現物件方法的呼叫,它是Method
類的例項方法,該方法中的引數obj
是呼叫方法的物件,args
是用於方法呼叫的引數。
實現過程
Pig介面,就定義了兩個方法
public interface Pig { void info(); void run(); } 複製程式碼
Pig的實現類,我們要做的就是在這兩個方法執行的前後插入自己一些額外的操作
public class FatPig implements Pig { @Override public void info() { System.out.println("我是一頭小肥豬!"); } @Override public void run() { System.out.println("我要跑步啦!"); } } 複製程式碼
LogUtils,需要在代理物件方法執行前後呼叫的方法
public class LogUtils { public void before() { System.out.println("==== 方法開始執行 ===="); } public void after() { System.out.println("==== 方法執行結束 ===="); } } 複製程式碼
PigInvocationHandler,該類實現了InvocationHandler介面。我們定義了一個Object型別的例項變數,因為我們需要在invoke方法中呼叫被代理物件的實現類的對應的方法。在invoke方法中,有兩個關鍵的引數:method
代表正在執行的方法,args
代表呼叫目標方法時傳入的實參。然後我們就可以在執行代理物件的方法前後自由插入自己的方法了。
public class PigInvocationHandler implements InvocationHandler { // 需要被代理的物件 private Object target; public void setTarget(Object target) { this.target = target; } // 執行動態代理物件的所有方法時,都會被替換成執行如下的invoke方法 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { LogUtils logUtils = new LogUtils(); // 執行logUtils物件中的before方法 logUtils.before(); // 以target作為主調來執行method方法 Object result = method.invoke(target, args); // 執行logUtils物件中的after方法 logUtils.after(); return result; } } 複製程式碼
測試類
public class MyProxyFactory { // 為指定的target生成動態代理物件 public static Object getProxy(Object target) { PigInvocationHandler handler = new PigInvocationHandler(); // 為handler設定target物件 handler.setTarget(target); // 建立並返回一個動態代理 return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), handler); } public static void main(String[] args) throws Exception { // 建立一個原始的FatPig物件,作為target Pig target = new FatPig(); // 以指定的target來建立動態代理物件 Pig pig = (Pig) MyProxyFactory.getProxy(target); pig.info(); pig.run(); } } 複製程式碼
執行結果:
==== 方法開始執行 ====
我是一頭小肥豬!
==== 方法執行結束 ====
==== 方法開始執行 ====
我要跑步啦!
==== 方法執行結束 ====
上面實現了簡單的AOP功能,我們可以在invoke方法裡面加上更多的判斷,使得功能更加強大,例如可以根據method引數指定某些方法前後執行特殊的處理。