AspectJ的基本用法
AOP雖然是方法論,但就好像OOP中的Java一樣,一些先行者也開發(fā)了一套語言來支持AOP。目前用得比較火的就是AspectJ了,它是一種幾乎和Java完全一樣的語言,而且完全兼容Java(AspectJ應該就是一種擴展Java,但它不是像Groovy[1]那樣的拓展。)。當然,除了使用AspectJ特殊的語言外,AspectJ還支持原生的Java,只要加上對應的AspectJ注解就好。所以,使用AspectJ有兩種方法:
完全使用AspectJ的語言。這語言一點也不難,和Java幾乎一樣,也能在AspectJ中調(diào)用Java的任何類庫。AspectJ只是多了一些關鍵詞罷了。
或者使用純Java語言開發(fā),然后使用AspectJ注解,簡稱*@AspectJ*。
AspectJ的配置可以參考另一篇文章Android中使用AspectJ詳解
Join Points介紹
Join Points是AspectJ中的一個關鍵概念。Join Points可以看作是程序運行時的一個執(zhí)行點,比如:一個函數(shù)的調(diào)用可以看作是個Join Points,如Log.e()這個函數(shù),e()可以看作是個Join Points,而調(diào)運e()的函數(shù)也可以認為是一個Join Points;設置一個變量,或者讀取一個變量也可以是個Join Points;for循環(huán)也可以看作是Join Points。
理論上說,一個程序中很多地方都可以被看做是Join Points,但是AspectJ中,只有下面所示的幾種執(zhí)行點被認為是Join Points:
| Join Points | 說明 | 示例 |
|---|---|---|
| method call | 函數(shù)調(diào)用 | 比如調(diào)用Log.e(),這是一處JPoint |
| method execution | 函數(shù)執(zhí)行 | 比如Log.e()的執(zhí)行內(nèi)部,是一處Join Points。注意它和method call的區(qū)別。method call是調(diào)用某個函數(shù)的地方。而execution是某個函數(shù)執(zhí)行的內(nèi)部。 |
| constructor call | 構造函數(shù)調(diào)用 | 和method call類似 |
| constructor execution | 構造函數(shù)執(zhí)行 | 和method execution類似 |
| field get | 獲取某個變量 | 比如讀取DemoActivity.debug成員 |
| field set | 設置某個變量 | 比如設置DemoActivity.debug成員 |
| pre-initialization | Object在構造函數(shù)中做得一些工作。 | |
| initialization | Object在構造函數(shù)中做得工作 | |
| static initialization | 類初始化 | 比如類的static{} |
| handler | 異常處理 | 比如try catch(xxx)中,對應catch內(nèi)的執(zhí)行 |
| advice execution | 這個是AspectJ的內(nèi)容,稍后再說 |
這里列出了AspectJ所認可的JoinPoints的類型。實際上,也就是你想把新的代碼插在程序的哪個地方,是插在構造方法中,還是插在某個方法調(diào)用前,或者是插在某個方法中,這個地方就是Join Points,當然,不是所有地方都能給你插的,只有能插的地方,才叫Join Points。
Pointcuts介紹
一個程序會有多個Join Points,即使同一個函數(shù),也還分為call和execution類型的Join Points,但并不是所有的Join Points都是我們關心的,Pointcuts就是提供一種使得開發(fā)者能夠選擇自己需要的JoinPoints的方法。
Advice
Advice就是我們插入的代碼以何種方式插入,有Before還有After、Around。
看個例子
@Before("execution(* android.app.Activity.on**(..))")
public void onActivityMethodBefore(JoinPoint joinPoint) throws Throwable {
}
這里會分成幾個部分,我們依次來看:
- @Before:Advice,也就是具體的插入點
- execution:處理Join Point的類型,例如call、execution
- (* android.app.Activity.on**(..)):這個是最重要的表達式,第一個*表示返回值,*表示返回值為任意類型,后面這個就是典型的包名路徑,其中可以包含 * 來進行通配,幾個 * 沒區(qū)別。同時,這里可以通過&&、||、!來進行條件組合。()代表這個方法的參數(shù),你可以指定類型,例如android.os.Bundle,或者(..)這樣來代表任意類型、任意個數(shù)的參數(shù)。
- public void onActivityMethodBefore:實際切入的代碼。
Before和After其實還是很好理解的,也就是在Pointcuts之前和之后,插入代碼,那么Around呢,從字面含義上來講,也就是在方法前后各插入代碼,是的,他包含了Before和After的全部功能,代碼如下:
@Around("execution(* com.xys.aspectjxdemo.MainActivity.testAOP())")
public void onActivityMethodAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
String key = proceedingJoinPoint.getSignature().toString();
Log.d(TAG, "onActivityMethodAroundFirst: " + key);
proceedingJoinPoint.proceed();
Log.d(TAG, "onActivityMethodAroundSecond: " + key);
}
其中,proceedingJoinPoint.proceed()代表執(zhí)行原始的方法,在這之前、之后,都可以進行各種邏輯處理。
自定義Pointcuts
自定義Pointcuts可以讓我們更加精確的切入一個或多個指定的切入點。
首先我們要定義一個注解類
@Retention(RetentionPolicy.CLASS)
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD})
public @interface DebugTrace {
}
在需要插入代碼的地方加入這個注解。如在MainActivity中加入,
public class MainActivity extends AppCompatActivity {
final String TAG = MainActivity.class.getSimpleName();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
logTest();
}
@DebugTrace
public void logTest() {
Log.e(TAG, "log test");
}
}
最后,創(chuàng)建切入代碼
@Pointcut("execution(@com.kun.aspectjtest.aspect.DebugTrace * *..*.*(..))")
public void DebugTraceMethod() {}
@Before("DebugTraceMethod()")
public void beforeDebugTraceMethod(JoinPoint joinPoint) throws Throwable {
String key = joinPoint.getSignature().toString();
Log.e(TAG, "beforeDebugTraceMethod: " + key);
}
log如下

在AspectJ的切入點表達式中,我們前面都是使用的execution,實際上,還有一種類型——call,那么這兩種語法有什么區(qū)別呢,對于Call來說:
Call(Before)
Pointcut{
Pointcut Method
}
Call(After)
對于Execution來說:
Pointcut{
execution(Before)
Pointcut Method
execution(After)
}
withincode
這個語法通常來進行一些切入點條件的過濾,作更加精確的切入控制。如下
public class MainActivity extends AppCompatActivity {
final String TAG = MainActivity.class.getSimpleName();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
aspectJ1();
aspectJ2();
aspectJ3();
}
public void aspectJTest() {
Log.e(TAG, "execute aspectJTest");
}
public void aspectJ1(){
aspectJTest();
}
public void aspectJ2(){
aspectJTest();
}
public void aspectJ3(){
aspectJTest();
}
}
aspectJ1(),aspectJ2(),aspectJ3()都調(diào)用了aspectJTest方法,但只想在aspectJ2調(diào)用aspectJTest時插入代碼,這個時候就需要使用到Pointcut和withincode組合的方式,來精確定位切入點。
@Pointcut("(call(* *..aspectJTest()))&&withincode(* *..aspectJ2())")
public void invokeAspectJTestInAspectJ2() {
}
@Before("invokeAspectJTestInAspectJ2()")
public void beforeInvokeaspectJTestInAspectJ2(JoinPoint joinPoint) throws Throwable {
Log.e(TAG, "method:" + getMethodName(joinPoint).getName());
}
private MethodSignature getMethodName(JoinPoint joinPoint) {
if (joinPoint == null) return null;
return (MethodSignature) joinPoint.getSignature();
}
log如下
04-02 23:44:40.681 12107-12107/ E/MainActivity: execute aspectJTest 04-02 23:44:40.681 12107-12107/ E/AspectTest: method:aspectJTest 04-02 23:44:40.681 12107-12107/ E/MainActivity: execute aspectJTest 04-02 23:44:40.681 12107-12107/ E/MainActivity: execute aspectJTest
以上就是Aspecj的基本使用方法,希望本文的內(nèi)容對大家的學習或者工作能帶來一定的幫助,同時也希望多多支持腳本之家!
相關文章
阿里的Easyexcel讀取Excel文件的方法(最新版本)
這篇文章主要介紹了阿里的Easyexcel讀取Excel文件(最新版本)的方法,本文通過示例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-12-12
Java入門基礎之Java的基本語法與Java所支持的數(shù)據(jù)類型
這篇文章主要介紹了Java入門基礎之Java的基本語法與Java所支持的數(shù)據(jù)類型,熟悉語法和數(shù)據(jù)類型通常是了解一種編程語言的開始,需要的朋友可以參考下2016-02-02
springboot整合redis進行數(shù)據(jù)操作(推薦)
springboot整合redis比較簡單,并且使用redistemplate可以讓我們更加方便的對數(shù)據(jù)進行操作。下面通過本文給大家分享springboot整合redis進行數(shù)據(jù)操作的相關知識,感興趣的朋友一起看看吧2017-10-10
Java Swing null絕對布局的實現(xiàn)示例
這篇文章主要介紹了Java Swing null絕對布局的實現(xiàn)示例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-12-12

