標籤:相對 處理 pid success 很多 utf-8 roc enter 需要
AspectJ可以說是Java中當之無愧的黑魔法。說它是黑魔法,一方面是因為它很強大,能夠解決一些傳統編程方法論解決不了的問題,而另一方面,它也相當的晦澀,有著比較陡峭的學習曲線。
本文將帶大家探索下AspectJ是什麼,能做什麼,以及如何來做,希望通過本文能夠讓大家初窺AspectJ之門道
AOP是什麼
相信很多人第一次聽說AOP是在學習spring的時候,筆者也是。這個概念其實困擾了我很久,到底是AOP?AOP是Aspect Oriented Programming的縮寫,和OOP(Object Oriented Programming)一樣,都代表一種編程思想。不同的是,OOP是對世界萬物的抽象,而AOP做的則是對業務處理過程的抽象,一定程度上說,AOP是OOP思想的一種延續,對程式進行了進一步的封裝。
那麼AOP到底能夠解決什麼問題呢?以在現有系統上增加一個安全性原則為例,我們需要在各個模組的代碼中不同地方添加代碼,進行安全性原則的檢查。這種方式實現起來很複雜,容易出錯,而且沒法複用。這裡描述的安全問題就是一個橫切關注點問題,開發人員需要找到所有需要關注的代碼斷,在現有代碼中間插入新的業務代碼(就好像對現有代碼做了切分)。類似這裡安全性原則的問題還有很多,比如tracing等。
AspectJ基本概念
AspectJ是AOP的Java實現版本,定義了AOP的文法,可以說是對Java的一個擴充。相對於Java,AspectJ引入了join point(連接點)的概念,同時引入三個新的結構,pointcut(切點), advice(通知),inter-type declaration(跨型別宣告)以及aspect。其中pointcut和advice是AspectJ中動態額部分,用來指定在什麼條件下切斷執行,以及採取什麼動作來實現切面操作。顧名思義,這裡的pointcut就是用來定義什麼情況下進行橫切,而advice則是指橫切情況下我們需要做什麼操作,所以說pointcut和advice會動態影響程式的運行流程。從某種角度上說,pointcut(切點)和我們平時用IDE偵錯工具時打的斷點很類似,當程式執行到我們打的斷點的地方的時候(運行到滿足我們定義的pointcut的語句的時候,也就是join point連接點),我們可以執行一段指令碼(執行advice中定義的行為)。
而AspectJ中的inter-type declaration(跨型別宣告)則是AspectJ中靜態部分,它影響的是程式的靜態結構,比如成員變數,類之間的關係等。Aspect則是對前三個結構的封裝,類似於java中的類。
第一個AspectJ程式
這裡我們先不去具體探討AspectJ的文法問題,而重點關注如何用AspectJ寫一個簡單的Demo。這裡我用的開發環境是IntelliJ,且項目使用maven來構建。
maven依賴
要運行AspectJ程式,首先要引入AspectJ運行時的依賴:
<!--aspectj runtime classes --><dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.8.9</version></dependency>
除了運行時依賴,還需要aspectjweaver.jar
:
<!--to introduce aspect to java class in load time--><dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.9</version></dependency>
一個簡單的類
先寫一個簡單的類:
package cc.databus.aspect;public class Account { double balance = 200; public boolean withdraw(int amount) { if (balance < amount) { return false; } balance = balance - amount; return true; } @Override public String toString() { return "Account{" + "balance=" + balance + ‘}‘; }}
該類定義了一個Account類,並提供了withdraw(取款)的方法。
aspect定義
建立一個AccountAspect.aj
檔案來記錄取款前後的資訊:
// aspect package cc.databus.aspect;public aspect AccountAspect { // define a pointcut to pick up invoking Accont.withdraw pointcut callWithDraw(int amount, Account account): call(boolean Account.withdraw(int)) && args(amount) && target(account); // advice definition executing before enterring method body before(int amount, Account acc): callWithDraw(amount, acc) { System.out.println("Start withdraw " + amount + " from " + acc); } after(int amount, Account acc) returning (Object ret): callWithDraw(amount, acc) { System.out.print("Finish withdraw, return " + ret +", account after withdraw is: " + acc); }}
通過IntelliJ可以很方便的建立aspect檔案,在包上面右鍵->New->Aspect:
正如你所見,上面的AccountAspect.aj定義了AspectJ的pointcut,advice以及aspect。
aspect織如(weaving)
Weaving....很奇怪的詞。。。這裡指的是將aspect中定義的advice植入到運行時的過程。這裡我們使用一個maven外掛程式來講aspect織如,這個外掛程式是Mojo AspectJ Maven Plugin:
<plugins> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>aspectj-maven-plugin</artifactId> <version>1.7</version> <configuration> <complianceLevel>1.8</complianceLevel> <source>1.8</source> <target>1.8</target> <showWeaveInfo>true</showWeaveInfo> <verbose>true</verbose> <encoding>utf-8</encoding> </configuration> <executions> <execution> <goals> <!--use this goal to weave all your main classes--> <goal>compile</goal> <!--use this goal to weave all your test classes--> <goal>test-compile</goal> </goals> </execution> </executions> </plugin></plugins>
OK,下面我們寫一個UT來測試下aspect是否生效了:
import cc.databus.aspect.Account;import org.junit.Before;import org.junit.Test;import static org.junit.Assert.assertFalse;import static org.junit.Assert.assertTrue;public class TestAccount { private Account account; @Before public void before() { account = new Account(); } @Test public void given20AndMin10_whenWithdraw5_thenSuccess() { assertTrue(account.withdraw(5)); } @Test public void given20AndMin10_whenWithdraw100_thenFail() { assertFalse(account.withdraw(100)); }}
運行測試,如果命令列有如下的輸出,則表示aspect成功織入我們的代碼,並且pointcut成功切入了Account.withdraw的調用:
Start withdraw 5 from Account{balance=200.0}Finish withdraw, return true, account after withdraw is: Account{balance=195.0}Process finished with exit code 0
總結
總的來說,AspectJ是一個相當晦澀難懂的技術,但是不得不承認它很強大。本文在從理論出發,先介紹AOP以及AspectJ的基本概念,然後以一個簡單的Demo程式介紹了如何在項目中使用AspectJ。
文章同步發布在我的個人部落格https://jianyuan.me上,歡迎拍磚。
傳送門: 初窺AspectJ
初窺AspectJ