Aop之AspectJ

AOP之AspectJ

前言:這幾天一直在學(xué)習(xí)aop切面編程,以前一直也有聽過aop但是實(shí)際用的還是比較少,不是很清楚,這周版本剛上線,有點(diǎn)空閑時(shí)間,所以就把之前想學(xué)習(xí)的aop技術(shù)重點(diǎn)花時(shí)間學(xué)習(xí)了下,所以才有這篇博客總結(jié)下學(xué)習(xí)的情況,其實(shí)現(xiàn)在的博客大都是這個(gè)抄寫那個(gè),那個(gè)抄寫這個(gè)的,其實(shí)我認(rèn)為不管怎么抄寫,只要自己掌握了,就沒關(guān)系。

AOP技術(shù)有很多,目前比較出名流行的技術(shù)框架大概是這幾類:1.APT 2.ASM 3.AspectJ,經(jīng)過這幾天的學(xué)習(xí),我還是認(rèn)為AspectJ是最簡單最快的實(shí)現(xiàn)方式,所以接下來,我會對AspectJ重點(diǎn)總結(jié)下這幾天的學(xué)習(xí)。

開始接入AspectJ步驟:

1.首先,需要在項(xiàng)目根目錄的build.gradle中增加依賴:

classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:1.0.8'

2.然后再主項(xiàng)目或者庫的build.gradle中增加AspectJ的依賴和插件依賴:

apply plugin: 'android-aspectjx'
compile 'org.aspectj:aspectjrt:1.8.10'

3.excludeJarFilter解釋:用該屬性,可以過濾減少AspectJ掃描庫文件,用法:

aspectjx {
    //includes the libs that you want to weave
    includeJarFilter 'universal-image-loader', 'AspectJX-Demo/library'

    //excludes the libs that you don't want to weave
    excludeJarFilter 'universal-image-loader'
}

AspectJ語法

Join Point

Join Point是AspectJ前言核心的地方,它就像一把刀,把程序的整個(gè)執(zhí)行過程切成了一段段不同的部分。例如,構(gòu)造方法調(diào)用、調(diào)用方法、方法執(zhí)行、異常等等,這些都是Join Points,實(shí)際上,也就是你想把新的代碼插在程序的哪個(gè)地方,是插在構(gòu)造方法中,還是插在某個(gè)方法調(diào)用前,或者是插在某個(gè)方法中,這個(gè)地方就是Join Points,當(dāng)然,不是所有地方都能給你插的,只有能插的地方,才叫Join Points。插入點(diǎn)的列舉:

JoinPoint 說明
Method call 方法被調(diào)用
Content Cell Content Cell
Method execution 方法執(zhí)行
Constructor call 構(gòu)造函數(shù)被調(diào)用
Constructor execution 構(gòu)造函數(shù)執(zhí)行
Field get 讀取屬性
Field set 寫入屬性
Pre-initialization 與構(gòu)造函數(shù)有關(guān),很少用到
Initialization 與構(gòu)造函數(shù)有關(guān),很少用到
Static initialization static 塊初始化
Handler 異常處理
Advice execution 所有 Advice 執(zhí)行

以上都是AspectJ插入代碼的點(diǎn)。

Pointcuts

Pointcuts 是具體的切入點(diǎn),可以確定具體織入代碼的地方,基本的 Pointcuts 是和 Join Point 相對應(yīng)的,在我理解,實(shí)際上就是在Join Points中通過一定條件選擇出我們所需要的Join Points,所以說,Pointcuts,也就是帶條件的Join Points,作為我們需要的代碼切入點(diǎn),兩者相對應(yīng)的關(guān)系點(diǎn):

Pointcuts 說明
Join Point Pointcuts syntax
Method call call(MethodPattern)
Method execution execution(MethodPattern)
Constructor call call(ConstructorPattern)
Constructor execution execution(ConstructorPattern)
Field get get(FieldPattern)
Field set set(FieldPattern)
Pre-initialization initialization(ConstructorPattern)
Initialization preinitialization(ConstructorPattern)
Static initialization staticinitialization(TypePattern)
Handler handler(TypePattern)
Advice execution advice excution()

Advice

Advice是在插入點(diǎn)的地方植入代碼,在 AspectJ 中有五種類型:Before、After、AfterReturning、AfterThrowing、Around。

Advice 說明
@Before 在執(zhí)行 Join Point 之前
@After 在執(zhí)行 Join Point 之后,包括正常的 return 和 throw 異常
@AfterReturning Join Point 為方法調(diào)用且正常 return 時(shí),不指定返回類型時(shí)匹配所有類型
@AfterThrowing Join Point 為方法調(diào)用且拋出異常時(shí),不指定異常類型時(shí)匹配所有類型
@Around 替代 Join Point 的代碼,如果要執(zhí)行原來代碼的話,要使用ProceedingJoinPoint.proceed()

注意: After 和 Before 沒有返回值,但是 Around 的目標(biāo)是替代原 Join Point 的,所以它一般會有返回值,而且返回值的類型需要匹配被選中的 Join Point 的代碼。而且不能和其他 Advice 一起使用,如果在對一個(gè) Pointcut 聲明 Around 之后還聲明 Before 或者 After 則會失效。

Advice 注解修改的方法必須為 public,Before、After、AfterReturning、AfterThrowing 四種類型修飾的方法返回值也必須為 void,Advice 需要使用 JoinPoint、JoinPointStaticPart、JoinPoint.EnclosingStaticPart 時(shí),要在方法中聲明為額外的參數(shù),@Around 方法可以使用 ProceedingJoinPoint,用以調(diào)用 proceed() 方法.

注意(Advice 不能使用 After 和 Around)

例子用法說明

/**
 * Created by wuminjian on 17/11/2.
 */
@Aspect
public class FragmentAspect {
    private static final String TAG = "FragmentAspect";
    @Before("execution(* com.test.aspectj.MainActivity.on**(..))")
    public void onActivityMethodBefore(JoinPoint joinPoint) throws Throwable {
        String key = joinPoint.getSignature().toString();
        Log.d(TAG, "onActivityMethodBefore: " + key);
    }
}
  • @Aspect:這個(gè)注解的意思是一個(gè)AspectJ文件,編譯器在編譯的時(shí)候,就會自動去解析,然后將代碼注入到相應(yīng)的JPonit中。
  • @Before: 是Advice,也就是具體的插入點(diǎn),是指調(diào)用這個(gè)某個(gè)Jpoint之前插入代碼。
  • execution: 處理JPoint的類型,例如call,execution
    • com.test.aspectj.MainActivity.on*(..)):這個(gè)是重要的表達(dá)式,第一個(gè)[ * ]表示返回值, 表示返回任意類型,后面的com.test.aspectj.MainActivity.on **表示典型的包名路徑,其中可用 * 來進(jìn)行通配.
  • onActivityMethodBefore:是調(diào)用MainActivity.on**方法之前插入的實(shí)際代碼。

通過這種方式編譯后,我們來看下生成的代碼是怎樣的。AspectJ的原理實(shí)際上是在編譯的時(shí)候,根據(jù)一定的規(guī)則解析,然后插入一些代碼,通過aspectjx生成的代碼,會在Build目錄:

CE63E24D-B07B-47DD-8B32-B32233EA7D49.png

通過JD-GUI查看jar文件如下:


77fb8413bb1ed6fb9484.png

我們可以發(fā)現(xiàn),在onCreate的最前面,插入了一行AspectJ的代碼。這個(gè)就是AspectJ的主要功能。

自定義Pointcuts

自定義Pointcuts可以讓我們更加精確的切入一個(gè)或多個(gè)指定的切入點(diǎn).

  • 首先,我們需要自定義一個(gè)注解類,例如——WmjLog
/**
 * Created by wuminjian on 17/11/3.
 */
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.METHOD)
public @interface WmjLog {
    
}
  • 接著,創(chuàng)建一個(gè)切入文件,內(nèi)部通過一個(gè)Pointcut來指定在帶有我們上面自定義注解類WmjLog注解的所有方法上進(jìn)行攔截。
/**
 * Created by wuminjian on 17/11/3.
 */
@Aspect
public class WmjAppLogAspect {

    //在帶有AopLog注解的方法進(jìn)行切入(注:此處的 * *前面都要有一個(gè)空格)
    @Pointcut("execution(@com.test.aspectj.model.WmjLog * *(..))")
    public void wmjLogPointcut() {

    } 
    //注意,這個(gè)函數(shù)必須要有實(shí)現(xiàn),否則Java編譯器會報(bào)錯(cuò)
    @After("wmjLogPointcut()")
    public void onLogPointcutAfter(JoinPoint joinPoint) throws Throwable {
        Log.i("WmjAOP", "onLogPointcutAfter:" + joinPoint.getSignature());
    }
}
  • 最后,在app項(xiàng)目中寫一個(gè)類來測試,代碼如下:
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;

import com.test.aspectj.model.WmjLog;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

    }
    @WmjLog
    public void WmjTestAop(){
        Log.i("AOP","in wmjTestAop");
    }
}

通過這種方式,我們可以非常方便的監(jiān)控指定的Pointcut,從而增加監(jiān)控的粒度,其實(shí)AspectJ很簡單關(guān)鍵就是Points的匹配寫法。

call和execution

在AspectJ的切入點(diǎn)表達(dá)式中,我們前面都是使用的execution,實(shí)際上,還有一種類型——call,execution是在被切入的方法中,call是在調(diào)用被切入的方法前或者后。具體事咧就不說了,大家自己去多試下就知道了。

@Before、@After、@ Around

Before、After

這兩個(gè)Advice應(yīng)該是使用的最多的,所以,我們先來看下這兩個(gè)Advice的實(shí)例,首先看下Before和After:

@Before("execution(* com.test.aspectj.MainActivity.on*(android.os.Bundle))")
public void onActivityMethodBefore(JoinPoint joinPoint) throws Throwable {
    String key = joinPoint.getSignature().toString();
    Log.d(TAG, "onActivityMethodBefore: " + key);
}

@After("execution(* com.test.aspectj.MainActivity.on*(android.os.Bundle))")
public void onActivityMethodAfter(JoinPoint joinPoint) throws Throwable {
    String key = joinPoint.getSignature().toString();
    Log.d(TAG, "onActivityMethodAfter: " + key);
}

經(jīng)過上面的語法解釋,現(xiàn)在看這個(gè)應(yīng)該很好理解了,我們來看下編譯后的類:


ee82fd1b58848ef17751.png

其實(shí)就是在方法的前后插入代碼。

Around

Before和After其實(shí)還是很好理解的,也就是在Pointcuts之前和之后,插入代碼,那么Around呢,從字面含義上來講,也就是在方法前后各插入代碼,是的,他包含了Before和After的全部功能,代碼如下:

@Around("execution(* com.test.aspectj.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)行各種邏輯處理。
在主工程測試代碼:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        testAOP();
    }

    public void testAOP() {
        Log.d("wmj", "testAOP");
    }
}

編譯之后查看class文件如下:


c6690d3cb753127061ee.png

我們可以發(fā)現(xiàn),Around確實(shí)實(shí)現(xiàn)了Before和After的功能,但是要注意的是,Around和After是不能同時(shí)作用在同一個(gè)方法上的,會產(chǎn)生重復(fù)切入的問題。

總結(jié):AspectJ還是很簡單,很好用的,關(guān)鍵是要熟悉他的語法就行,以上還是還有很多小點(diǎn)沒講到,自己多看資料吧,AspectJ其實(shí)編譯是用ajc去編譯的,以上列子集成了一個(gè)apply plugin: 'android-aspectjx'一個(gè)插件,插件幫我們做好了一切,所以我們能直接使用@Aspect注解,以下是幾個(gè)寫的很好的列子:

gradle_plugin_android_aspectjx

hugo

gradle-android-aspectj-plugin

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,238評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,430評論 3 415
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,134評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,893評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,653評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,136評論 1 323
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,212評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,372評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,888評論 1 334
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,738評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,939評論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,482評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,179評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,588評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,829評論 1 283
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,610評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,916評論 2 372

推薦閱讀更多精彩內(nèi)容

  • AspectJ 是 Android 平臺上一種比較高效和簡單的實(shí)現(xiàn) AOP 技術(shù)的方案。 相類似的方案有以下幾種:...
    zhuhf閱讀 20,176評論 30 105
  • AOP之AspectJ - 代碼注入 [TOC] 一、AOP簡介 1.1 什么是AOP編程 AOP是Aspect ...
    everlastxgb閱讀 2,549評論 2 10
  • 看AspectJ在Android中的強(qiáng)勢插入 什么是AOP AOP是Aspect Oriented Program...
    eclipse_xu閱讀 11,658評論 11 84
  • 基本知識 其實(shí), 接觸了這么久的 AOP, 我感覺, AOP 給人難以理解的一個(gè)關(guān)鍵點(diǎn)是它的概念比較多, 而且坑爹...
    永順閱讀 8,291評論 5 114
  • 生活是什么 有時(shí)像云漂浮不定 有時(shí)像雨淋漓酣暢 有時(shí)像太陽熱烈、溫暖 有時(shí)像風(fēng)捉摸不透 生活是什么 有時(shí)像清茶幽香...
    文采樂閱讀 265評論 6 6