最近我花了半個小時實現了一個Method的按自定義條件執行的plugin,Condition-Run 。實現場景是由於我所工作的客戶經常會是在同一個程式碼集上實現多個Brand,所以有些功能只會限制是幾個brand呼叫,而其他的呼叫則不該呼叫。還有因為持續互動,我們會不停的release新的功能得到快速的反饋,在這前提下我們會經常遇見在我們剛開發完一個brand的產品程式碼,就要面臨release,所以我們希望其不該對其他的brand產生影響。
面對這樣的需求初級程式設計師有些人肯定會覺得沒什麼了不起的啊,不就是幾個if/else或者switch/case。我和我的團隊對程式碼有一種天生的潔癖,對於太多複雜的if/else和switch/case,以及將來會被移除的臨時非產品程式碼分佈在各處,有一種牴觸心理。
對於有一定工作經驗的程式設計師來說這樣的需求肯定也不是什麼問題,不就是AOP(面向切面程式設計),對,這就是我們所期望的解決方案,把處理的邏輯集中,和產品程式碼的分離。
知道了用AOP,也許你只對了一半,在AOP的世界裡,有兩種實現方式靜態植入和動態代理,最早了AOP實現採用的是靜態植入,然後由於IOC之類的框架的興起,動態代理的實現方式也逐漸興起,取代了靜態植入的方式。但並不是說靜態植入的方式就不再有用武之地的,在這裡我們所採用的AOP框架Aspectj就是一個靜態注入的框架,我們並沒有和spring結合,動態代理的方式有個基本的問題就是你不能直接new這個物件這需要交給框架處理,如果是spring框架的話,這要求你必須是一個spring的bean,以及動態代理會有一些效能的損失。這對於該場景的限制太多,並不是我所期望的。
靜態植入的框架在我以前的部落格中也提到不少,如果感興趣請移步 《IOC/AOP隨筆目錄》。在java世界裡Aspectj,.net世界裡PostSharp(部落格中靜態植入原理分析篇《MSBuild + MSILInect實現編譯時AOP之預覽》)就是這類框架。
回到主題,對於 condition-run如何使用請移步github文件。
這裡簡述如何實現:
1 對於AOP第一步是匹配規則,所以定義一個標記:
@Retention(RUNTIME) @Target({METHOD}) public @interface ConditionRun { Class<? extends Runner> value(); }
其指向一個實現Runner介面的型別。
2:有了匹配規則,我們就可以找到切入點進行AOP邏輯的處理,
@Aspect public class ConditionRunAspect { @Around("methodsToBeConditionRun(conditionRun)") public Object profile(ProceedingJoinPoint pjp, ConditionRun conditionRun) throws Throwable { final Result result = isExec(pjp, conditionRun); if (result.isExec()) { return pjp.proceed(); } return result.getDefaultValue(); } private Result isExec(ProceedingJoinPoint pjp, ConditionRun conditionRun) { try { final Runner runner = conditionRun.value().newInstance(); final MethodSignature signature = (MethodSignature) pjp.getSignature(); return runner.exec(signature, pjp.getArgs()); } catch (Exception e) { throw new RuntimeException("Runner must be empty constructor and make sure the config is ok.", e); } } @Pointcut(value = "@annotation(conditionRun)") public void methodsToBeConditionRun(ConditionRun conditionRun) { } }
這裡在切入方法呼叫之前,new了一個配置的Runner型別(注意必須午餐構造),並呼叫其exec方法獲取是否執行該方法,如果不執行則返回什麼預設值。
Runner runner = conditionRun.value().newInstance(); final MethodSignature signature = (MethodSignature) pjp.getSignature(); return runner.exec(signature, pjp.getArgs());
exec的引數簽名為方法簽名資訊和方法執行時引數。
一切都這麼簡單,現在你可以任意的框架Runner去做適合你的場景業務了。可以參照專案下得sample例項,該例項展示了一個當出入引數為3的時候執行,部位3則返回預設值1.
想想還有那些場景,你是否遇見過某類單元測試我只希望執行在某種固定的場景下?假設在開發圖形應用的時候,我們希望呼叫一些不同平臺的特定api,雖然我們程式碼設計封裝做得很好,但是我們希望有固定的整合測試去cover這部分邏輯,讓我們的測試只執行是固定平臺。