初窺AspectJ
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