手把手教你用Java實現AOP
介紹
眾所周知,AOP(面向切面程式設計)是Spring框架的特色功能之一。通過設定橫切關注點(cross cutting concerns),AOP提供了極高的擴充套件性。那AOP在Spring中是怎樣運作的呢?當你只能使用core java,卻需要AOP技術時,這個問題的解答變得極為關鍵。不僅如此,在高階技術崗位的面試中,此類問題也常作為考題出現。這不,我的朋友最近參加了一個面試,就被問到了這樣一個棘手的問題——如何在不使用Spring及相關庫,只用core Java的條件下實現AOP。因此,我將在本文中提供一份大綱,幫助大家瞭解如何只用core Java實現一個AOP(當然啦,這種AOP在功能上有一定的侷限性)。注意,本文不是一篇有關Spring AOP與Java AOP的對比研究,而是有關在core Java中藉助固有的設計模式實現AOP的教程。
想必讀者已經知道AOP是什麼,也知道在Spring框架中如何使用它,因此本文只著眼於如何在不用Spring的前提下實現AOP。首先,我們得知道,Spring是藉助了JDK proxy和CGlib兩種技術實現AOP的。JDK dynamic proxy提供了一種靈活的方式來hook一個方法並執行指定的操作,但執行操作時得有一個限制條件:必須先提供一個相關的介面以及該介面的實現類。實踐出真知,讓我們透過一個案例來理解這句吧!現在有一個計算器程式,用於完成一些數學運算。讓我們來考慮下除法功能,此時的問題是:如果core framework 已經具備了一份實現除法的程式碼,我們能否在程式碼執行時劫持(highjack)它並執行額外的校驗呢?答案是肯定的,我將用下面提供的程式碼片段來證明這點。首先來看基礎介面的程式碼:
public interface Calculator { public int calculate( int a , int b); }
該介面實現類的程式碼如下:
public class CalculatorImpl implements Calculator { @Override public int calculate(int a, int b) { return a/b; } }
假設我們既不能修該上面的程式碼,也不能對核心庫進行任何改動,怎樣才能完美地實現校驗功能呢?不如試下JDK dynamic proxy的功能吧。
public class SomeHandler implements InvocationHandler { // Code omitted for simplicity….. @Override public Object invoke(Object proxy, Method method, Object[] params) throws Throwable { // Your complex business validation and logic Object result = method.invoke(targetObject ,params); return result; } }
讓我們通過測試類來看看由JDK dynamic proxy實現的校驗功能的效果如何。
public static void main(String[] args) { CalculatorImpl calcImpl = new CalculatorImpl(); Calculator proxied = (Calculator)ProxyFactory.getProxy (Calculator.class, calcImpl, new SomeHandler(calcImpl)); int result = proxied.calculate(20, 10); System.out.println("FInal Result :::" + result); }
從結果可以看出,簡單地實現功能強大的InvocationHandler介面,我們便能得到一個hooking implementation。按照JDK文件的描述,InvocationHandler介面是藉助一個代理例項(proxy instance)來處理一個方法呼叫的。
現在我們已經知道,InvocationHandler的invoke()方法能夠幫助我們解決問題。那麼再來解決一個新問題——怎樣才能在方法執行的前後執行操作呢?說的更具體一些,我們能通過新增多個aop(before、after、around)來hook一個方法嗎(譯註:原文為add multiple aops,但我認為Handler是充當Aspect的角色)?答案同樣是肯定的。按照以下的步驟建立一個精簡的程式碼模板便能滿足這樣的需求:
- 建立一個抽象類,用於將aop應用於目標物件上。
- 建立名為BeforeHandler 和 AfterHandler的兩個aop。前者在方法執行之前工作,而後者則在方法執行結束後工作。
- 建立一個代理類,使所有的aop handler和目標物件只需作為引數傳入,就能建立一個hook。
- 加入你自己的業務邏輯或者橫切關注點。
- 最後,通過傳入相關的引數建立代理物件(proxy object)。
技術實現概要
(譯註:此處是核心程式碼片段,如果想執行該例項,需進入下方提供的連結下載完整程式碼)
建立一個handler的抽象類:
public abstract class AbstractHandler implements InvocationHandler { private Object targetObject; public void setTargetObject(Object targetObject) { this.targetObject = targetObject; } }
建立名為BeforeHandler和AfterHandler的兩個易擴充套件的handler抽象類:
public abstract class BeforeHandler extends AbstractHandler { public abstract void handleBefore(Object proxy, Method method, Object[] args); public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { handleBefore(proxy, method, args); return method.invoke(getTargetObject(), args); } }
public abstract class AfterHandler extends AbstractHandler { public abstract void handleAfter(Object proxy, Method method, Object[] args); public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = method.invoke(getTargetObject(), args); handleAfter(proxy, method, args); return result; } }
建立Proxy的工廠類:
public class ProxyFactory { public static Object getProxy(Object targetObject, List handlers) { //Code to get the proxy return proxyObject; } else { return targetObject; } } }
以下為測試程式碼:
CalculatorImpl calcImpl = new CalculatorImpl(); BeforeHandler before = new BeforeHandlerImpl(); AfterHandler after = new AfterHandlerImpl(); List<AbstractHandler> handlers = new ArrayList<AbstractHandler>(); handlers.add(before); handlers.add(after); Calculator proxy = (Calculator) ProxyFactory.getProxy(calcImpl, handlers); int result = proxy.calculate(20, 10);
配置
以上的程式碼片段簡明扼要地解釋了AOP在結構上的實現(structural implementation)。當然,如果能通過實際的測試將其運用到現實中去,那就再好不過了。讀者可在下面的連結中獲取完整的工程檔案,並在Java編輯器中配置它們,最後通過其中的測試類來檢驗效果。
總結
希望這篇簡短的有關AOP文章能夠幫助到大家。需說明的是,本文只實現了before和after兩種aop,而另外兩種,即“Around”和“Throw”,則希望讀者自行完成。
相關文章
- 手把手教你用ManagedSQLiteOpenHelper實現資料庫SQLite資料庫
- 手把手教你用 Go 實現一個 mTLSGoTLS
- 手把手教你用java實現二分查詢樹及其相關操作Java
- 手把手教你用redis實現一個簡單的mq訊息佇列(java)RedisMQ佇列Java
- Java實現AOP的集中方式Java
- 手把手教你用RecyclerView實現貓眼電影選擇效果View
- 手把手教你用Python實踐深度學習Python深度學習
- 【Java】手把手理解CAS實現原理Java
- 「Java」手把手理解CAS實現原理Java
- Spring——AOP實現Spring
- AOP如何實現及實現原理
- Spring AOP實現原理Spring
- 使用 libffi 實現 AOP
- 【Spring】AOP實現原理Spring
- Spring之AOP實現Spring
- 【“探探”為例】手把手教你用最少的程式碼實現各種“機器人”機器人
- 手把手教你用Charles抓包
- Spring系列之aAOP AOP是什麼?+xml方式實現aop+註解方式實現aopSpringXML
- Java-JDK動態代理(AOP)使用及實現原理分析JavaJDK
- 從零開始實現一個簡易的Java MVC框架(四)--實現AOPJavaMVC框架
- AOP的簡單實現
- AOP如何實現及其原理
- Spring AOP的實現原理Spring
- Spring AOP實現過程Spring
- AOP概述及實現原理
- Net 實現自定義Aop
- AOP實現系統告警
- Go能實現AOP嗎?Go
- Spring框架系列(9) - Spring AOP實現原理詳解之AOP切面的實現Spring框架
- 教你用Perl實現Smgp協議協議
- 教你用Magent實現Memcached叢集
- 當JAVA註解、AOP、SpEL相遇,更多可能變為了現實Java
- 手把手教你用vue搭建個人站Vue
- [譯] 手把手教你用 Playground 建立 App FrameworkAPPFramework
- 手把手教你用Spuernova生成flutter程式碼Flutter
- AOP的實現深入理解
- spring-AOP(一)實現原理Spring
- Spring AOP 的實現機制Spring
- 利用Proxy.newProxyInstance實現AOP