AspectJ的基本用法
AOP雖然是方法論,但就好像OOP中的Java一樣,一些先行者也開(kāi)發(fā)了一套語(yǔ)言來(lái)支持AOP。目前用得比較火的就是AspectJ了,它是一種幾乎和Java完全一樣的語(yǔ)言,而且完全兼容Java(AspectJ應(yīng)該就是一種擴(kuò)展Java,但它不是像Groovy[1]那樣的拓展。)。當(dāng)然,除了使用AspectJ特殊的語(yǔ)言外,AspectJ還支持原生的Java,只要加上對(duì)應(yīng)的AspectJ注解就好。所以,使用AspectJ有兩種方法:
完全使用AspectJ的語(yǔ)言。這語(yǔ)言一點(diǎn)也不難,和Java幾乎一樣,也能在AspectJ中調(diào)用Java的任何類(lèi)庫(kù)。AspectJ只是多了一些關(guān)鍵詞罷了。
或者使用純Java語(yǔ)言開(kāi)發(fā),然后使用AspectJ注解,簡(jiǎn)稱(chēng)*@AspectJ*。
AspectJ的配置可以參考另一篇文章Android中使用AspectJ詳解
Join Points介紹
Join Points是AspectJ中的一個(gè)關(guān)鍵概念。Join Points可以看作是程序運(yùn)行時(shí)的一個(gè)執(zhí)行點(diǎn),比如:一個(gè)函數(shù)的調(diào)用可以看作是個(gè)Join Points,如Log.e()這個(gè)函數(shù),e()可以看作是個(gè)Join Points,而調(diào)運(yùn)e()的函數(shù)也可以認(rèn)為是一個(gè)Join Points;設(shè)置一個(gè)變量,或者讀取一個(gè)變量也可以是個(gè)Join Points;for循環(huán)也可以看作是Join Points。
理論上說(shuō),一個(gè)程序中很多地方都可以被看做是Join Points,但是AspectJ中,只有下面所示的幾種執(zhí)行點(diǎn)被認(rèn)為是Join Points:
Join Points | 說(shuō)明 | 示例 |
---|---|---|
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)用某個(gè)函數(shù)的地方。而execution是某個(gè)函數(shù)執(zhí)行的內(nèi)部。 |
constructor call | 構(gòu)造函數(shù)調(diào)用 | 和method call類(lèi)似 |
constructor execution | 構(gòu)造函數(shù)執(zhí)行 | 和method execution類(lèi)似 |
field get | 獲取某個(gè)變量 | 比如讀取DemoActivity.debug成員 |
field set | 設(shè)置某個(gè)變量 | 比如設(shè)置DemoActivity.debug成員 |
pre-initialization | Object在構(gòu)造函數(shù)中做得一些工作。 | |
initialization | Object在構(gòu)造函數(shù)中做得工作 | |
static initialization | 類(lèi)初始化 | 比如類(lèi)的static{} |
handler | 異常處理 | 比如try catch(xxx)中,對(duì)應(yīng)catch內(nèi)的執(zhí)行 |
advice execution | 這個(gè)是AspectJ的內(nèi)容,稍后再說(shuō) |
這里列出了AspectJ所認(rèn)可的JoinPoints的類(lèi)型。實(shí)際上,也就是你想把新的代碼插在程序的哪個(gè)地方,是插在構(gòu)造方法中,還是插在某個(gè)方法調(diào)用前,或者是插在某個(gè)方法中,這個(gè)地方就是Join Points,當(dāng)然,不是所有地方都能給你插的,只有能插的地方,才叫Join Points。
Pointcuts介紹
一個(gè)程序會(huì)有多個(gè)Join Points,即使同一個(gè)函數(shù),也還分為call和execution類(lèi)型的Join Points,但并不是所有的Join Points都是我們關(guān)心的,Pointcuts就是提供一種使得開(kāi)發(fā)者能夠選擇自己需要的JoinPoints的方法。
Advice
Advice就是我們插入的代碼以何種方式插入,有Before還有After、Around。
看個(gè)例子
@Before("execution(* android.app.Activity.on**(..))") public void onActivityMethodBefore(JoinPoint joinPoint) throws Throwable { }
這里會(huì)分成幾個(gè)部分,我們依次來(lái)看:
- @Before:Advice,也就是具體的插入點(diǎn)
- execution:處理Join Point的類(lèi)型,例如call、execution
- (* android.app.Activity.on**(..)):這個(gè)是最重要的表達(dá)式,第一個(gè)*表示返回值,*表示返回值為任意類(lèi)型,后面這個(gè)就是典型的包名路徑,其中可以包含 * 來(lái)進(jìn)行通配,幾個(gè) * 沒(méi)區(qū)別。同時(shí),這里可以通過(guò)&&、||、!來(lái)進(jìn)行條件組合。()代表這個(gè)方法的參數(shù),你可以指定類(lèi)型,例如android.os.Bundle,或者(..)這樣來(lái)代表任意類(lèi)型、任意個(gè)數(shù)的參數(shù)。
- public void onActivityMethodBefore:實(shí)際切入的代碼。
Before和After其實(shí)還是很好理解的,也就是在Pointcuts之前和之后,插入代碼,那么Around呢,從字面含義上來(lái)講,也就是在方法前后各插入代碼,是的,他包含了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í)行原始的方法,在這之前、之后,都可以進(jìn)行各種邏輯處理。
自定義Pointcuts
自定義Pointcuts可以讓我們更加精確的切入一個(gè)或多個(gè)指定的切入點(diǎn)。
首先我們要定義一個(gè)注解類(lèi)
@Retention(RetentionPolicy.CLASS) @Target({ElementType.CONSTRUCTOR, ElementType.METHOD}) public @interface DebugTrace { }
在需要插入代碼的地方加入這個(gè)注解。如在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的切入點(diǎn)表達(dá)式中,我們前面都是使用的execution,實(shí)際上,還有一種類(lèi)型——call,那么這兩種語(yǔ)法有什么區(qū)別呢,對(duì)于Call來(lái)說(shuō):
Call(Before) Pointcut{ Pointcut Method } Call(After)
對(duì)于Execution來(lái)說(shuō):
Pointcut{ execution(Before) Pointcut Method execution(After) }
withincode
這個(gè)語(yǔ)法通常來(lái)進(jìn)行一些切入點(diǎn)條件的過(guò)濾,作更加精確的切入控制。如下
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時(shí)插入代碼,這個(gè)時(shí)候就需要使用到Pointcut和withincode組合的方式,來(lái)精確定位切入點(diǎn)。
@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)容對(duì)大家的學(xué)習(xí)或者工作能帶來(lái)一定的幫助,同時(shí)也希望多多支持腳本之家!
- 在Android項(xiàng)目中使用AspectJ的方法
- AndroidStudio 配置 AspectJ 環(huán)境實(shí)現(xiàn)AOP的方法
- Spring Aop之AspectJ注解配置實(shí)現(xiàn)日志管理的方法
- Android AOP框架AspectJ使用詳解
- 分析java 中AspectJ切面執(zhí)行兩次的原因
- Android中使用AspectJ詳解
- 詳解Spring Aop實(shí)例之AspectJ注解配置
- Spring使用AspectJ注解和XML配置實(shí)現(xiàn)AOP
- java基于AspectJ(面向切面編程)編碼示例分享
- Spring AOP AspectJ使用及配置過(guò)程解析
相關(guān)文章
SpringBoot與knife4j的整合使用過(guò)程
Knife4j?是一個(gè)基于Swagger構(gòu)建的開(kāi)源?JavaAPI文檔工具,主要包括兩大核心功能:文檔說(shuō)明和在線調(diào)試,這篇文章主要介紹了SpringBoot與knife4j的整合使用,需要的朋友可以參考下2024-08-08詳解Java對(duì)象的強(qiáng)、軟、弱和虛引用+ReferenceQueue
這篇文章主要介紹了詳解Java對(duì)象的強(qiáng)、軟、弱和虛引用+ReferenceQueue的相關(guān)資料,需要的朋友可以參考下2017-06-06Java8中AbstractExecutorService與FutureTask源碼詳解
這篇文章主要給大家介紹了關(guān)于Java8中AbstractExecutorService與FutureTask的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2022-01-01詳解spring boot 使用application.properties 進(jìn)行外部配置
這篇文章主要介紹了詳解spring boot 使用application.properties 進(jìn)行外部配置,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-03-03MyBatis 參數(shù)類(lèi)型為String時(shí)常見(jiàn)問(wèn)題及解決方法
這篇文章主要介紹了MyBatis 參數(shù)類(lèi)型為String時(shí)常見(jiàn)問(wèn)題及解決方法,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2017-03-03Java使用Apache POI庫(kù)讀取Excel表格文檔的示例
POI庫(kù)是Apache提供的用于在Windows下讀寫(xiě)各類(lèi)微軟Office文檔的Java庫(kù),這里我們就來(lái)看一下Java使用Apache POI庫(kù)讀取Excel表格文檔的示例:2016-06-06Java學(xué)習(xí)教程之定時(shí)任務(wù)全家桶
這篇文章主要給大家介紹了關(guān)于Java學(xué)習(xí)教程之定時(shí)任務(wù)全家桶的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11Jmeter參數(shù)化獲取序列數(shù)據(jù)實(shí)現(xiàn)過(guò)程
這篇文章主要介紹了Jmeter參數(shù)化獲取序列數(shù)據(jù)實(shí)現(xiàn)過(guò)程,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-07-07