1 AOP各類的實現java
AOP就是面向切面編程,咱們能夠從幾個層面來實現AOP。編程
在編譯器修改源代碼,在運行期字節碼加載前修改字節碼或字節碼加載後動態建立代理類的字節碼,如下是各類實現機制的比較。 性能
類別this |
機制spa |
原理代理 |
優勢日誌 |
缺點code |
靜態AOP對象 |
靜態織入接口 |
在編譯期,切面直接以字節碼的形式編譯到目標字節碼文件中。 |
對系統無性能影響。 |
靈活性不夠。 |
動態AOP |
動態代理 |
在運行期,目標類加載後,爲接口動態生成代理類,將切面植入到代理類中。 |
相對於靜態AOP更加靈活。 |
切入的關注點須要實現接口。對系統有一點性能影響。 |
動態字節碼生成 |
在運行期,目標類加載後,動態構建字節碼文件生成目標類的子類,將切面邏輯加入到子類中。 |
沒有接口也能夠織入。 |
擴展類的實例方法爲final時,則沒法進行織入。 |
|
自定義類加載器 |
在運行期,目標加載前,將切面邏輯加到目標字節碼裏。 |
能夠對絕大部分類進行織入。 |
代碼中若是使用了其餘類加載器,則這些類將不會被織入。 |
|
字節碼轉換 |
在運行期,全部類加載器加載字節碼前,前進行攔截。 |
能夠對全部類進行織入。 |
2 AOP的實現機制
本章節將詳細介紹AOP有各類實現機制。
2.1 動態代理
Java在JDK1.3後引入的動態代理機制,使咱們能夠在運行期動態的建立代理類。使用動態代理實現AOP須要有四個角色:被代理的類,被代理類的接 口,織入器,和InvocationHandler,而織入器使用接口反射機制生成一個代理類,而後在這個代理類中織入代碼。被代理的類是AOP裏所說的 目標,InvocationHandler是切面,它包含了Advice和Pointcut。
2.1.1 使用動態代理
那如何使用動態代理來實現AOP。下面的例子演示在方法執行前織入一段記錄日誌的代碼,其中Business是代理 類,LogInvocationHandler是記錄日誌的切面,IBusiness, IBusiness2是代理類的接口,Proxy.newProxyInstance是織入器。
清單一:動態代理的演示
package sample.proxy; public interface IBusiness { public boolean doBusiness(); }
package sample.proxy; public interface IBusiness2 { public void doBusiness2(); }
package sample.proxy; public class Business implements IBusiness, IBusiness2 { public boolean doBusiness() { System.out.println("執行業務邏輯"); return true; } public void doBusiness2() { System.out.println("執行業務邏輯2"); } }
package sample.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class DynamicProxyDemo { public static void main(String[] args) { // 須要代理的接口,被代理類實現的多個接口都必須在這裏定義 Class[] proxyInterface = new Class[] { IBusiness.class, IBusiness2.class }; // 構建AOP的Advice,這裏須要傳入業務類的實例 LogInvocationHandler handler = new LogInvocationHandler(new Business()); // 生成代理類的字節碼加載器 ClassLoader classLoader = DynamicProxyDemo.class.getClassLoader(); // 織入器,織入代碼並生成代理類 IBusiness2 proxyBusiness = (IBusiness2) Proxy.newProxyInstance( classLoader, proxyInterface, handler); // 使用代理類的實例來調用方法。 proxyBusiness.doBusiness2(); ((IBusiness) proxyBusiness).doBusiness(); } /** * 打印日誌的切面 */ public static class LogInvocationHandler implements InvocationHandler { private Object target; // 目標對象 LogInvocationHandler(Object target) { this.target = target; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 執行原有邏輯 Object rev = method.invoke(target, args); // 執行織入的日誌,你能夠控制哪些方法執行切入邏輯 if (method.getName().equals("doBusiness2")) { System.out.println("記錄日誌"); } return rev; } } }
輸出 Java代碼
輸出 Java代碼