Android 面向切面編程(AOP)之 AspectJ

概念:

AOP: Aspect Oriented Programming 面向切面編程绷落。

AOP是個概念,AspectJ 是它的一個具體實現(xiàn)。和Java配合使用瘾蛋。

AspectJ:核心是他的編譯器(ajc),就做了一件事矫限,講 AspectJ 的代碼在編譯期插入到目標程序中哺哼。運行時沒啥區(qū)別。ajc 會構建目標程序和AspectJ 代碼的聯(lián)系叼风,在編譯期將 AspectJ 代碼插入被切出的 PointCut中取董。達到AOP的目的。

我們采用 AspectJ 的方式來實現(xiàn) AOP无宿。

術語:

1茵汰、Advice:增強
也叫 通知。增強是織入目標類連接點的一段程序代碼

2孽鸡、JoinPoint:連接點
程序執(zhí)行的某個特點的位置蹂午,比如 類的初始化前后,方法的調用前后等等彬碱。具有邊界性質的點成為連接點豆胸。

3、PointCut:切入點
連接點 相當于 數據庫的記錄巷疼。 切入點 相當于 查詢條件晚胡。
切入點和連接點不是一一對應的,一個切入點可以匹配多個連接點

4、Aspect:切面
切面有切點和連接點組成

5估盘、Weaving:織入
將增強添加到目標類的具體連接點的過程瓷患。AOP 像一個織布機把目標類和增強縫在了一起
根據不同實現(xiàn)技術,三種織入的方式

  • 編譯器織入遣妥,需要特殊的Java編譯器
  • 類裝載期織入擅编,需要特殊的類加載器
  • 動態(tài)代理織入,在運行期為目標類添加增強生成子類的方式

5燥透、Target:目標對象
增強邏輯的目標對象沙咏。

步驟:

1、首先定義一個表達式(PointCut)告訴程序我們要在哪里增加額外的操作班套。
通過這個表達式(PointCut)肢藐,獲得那些需要通知的方法(JoinPoint)。

2吱韭、我們要告訴程序這些方法(JointPoint)如何增強(Advice)

  • 什么時候吆豹?執(zhí)行前?執(zhí)行后理盆?返回前痘煤?
  • 額外具體操作是干甚么?
    我們把這個兩個步驟定義到一個地方(Aspect)猿规。
    涉及到的被修改的對象就是目標對象(Tatget)衷快。
    完成了上面的所有動作,總成織入(Weaving)姨俩。
代碼敲起來

配置
1蘸拔、在應用build.gradle中

 dependencies {
        classpath 'org.aspectj:aspectjtools:1.8.10'
        classpath 'org.aspectj:aspectjweaver:1.8.10'
    }

2、在app build.gradle中

dependencies {
  implementation 'org.aspectj:aspectjrt:1.8.10'
}
import org.aspectj.bridge.IMessage
import org.aspectj.bridge.MessageHandler
import org.aspectj.tools.ajc.Main

final def log = project.logger
final def variants = project.android.applicationVariants

variants.all { variant ->
    if (!variant.buildType.isDebuggable()) {
        log.debug("Skipping non-debuggable build type '${variant.buildType.name}'.")
        return
    }

    JavaCompile javaCompile = variant.javaCompile
    javaCompile.doLast {
        String[] args = ["-showWeaveInfo",
                         "-1.8",
                         "-inpath", javaCompile.destinationDir.toString(),
                         "-aspectpath", javaCompile.classpath.asPath,
                         "-d", javaCompile.destinationDir.toString(),
                         "-classpath", javaCompile.classpath.asPath,
                         "-bootclasspath", project.android.bootClasspath.join(File.pathSeparator)]
        log.debug "ajc args: " + Arrays.toString(args)

        MessageHandler handler = new MessageHandler(true)
        new Main().run(args, handler)
        for (IMessage message : handler.getMessages(null, true)) {
            switch (message.getKind()) {
                case IMessage.ABORT:
                case IMessage.ERROR:
                case IMessage.FAIL:
                    log.error message.message, message.thrown
                    break
                case IMessage.WARNING:
                    log.warn message.message, message.thrown
                    break
                case IMessage.INFO:
                    log.info message.message, message.thrown
                    break
                case IMessage.DEBUG:
                    log.debug message.message, message.thrown
                    break
            }
        }
    }

配置完畢环葵。

新建一個類调窍,添加注解@Aspect 說明這是一個切面。
然后創(chuàng)建我們的切點方法张遭。注解@PointCut

注意里面execution表達式,
.. 任意類型任意多個參數

  • 包名通配符
execution(<修飾符模式>? <返回類型模式> <方法名模式>(<參數模式>) <異常模式>?)

根據需要創(chuàng)建以下注解方法

類型 描述
Before 前置通知, 在目標執(zhí)行之前執(zhí)行通知
After 后置通知, 目標執(zhí)行后執(zhí)行通知
Around 環(huán)繞通知, 在目標執(zhí)行中執(zhí)行通知, 控制目標執(zhí)行時機
AfterReturning 后置返回通知, 目標返回時執(zhí)行通知
AfterThrowing 異常通知, 目標拋出異常時執(zhí)行通知
@Aspect
public class TestAspect {
    @Pointcut("execution(* com.ssy.qbd.MenuActivity.doSomeThing(..))")
    public void pointcut() {

    }

    @Before("pointcut()")
    public void before(JoinPoint point) {
        Log.e("Mr.S", "@Before");
    }
//注意 如果不調用joinPoint.proceed() 此方法不調用
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        return joinPoint.proceed();
    }

    @After("pointcut()")
    public void after(JoinPoint joinPoint) {
        Log.e("Mr.S", "@After");
    }

    @AfterReturning(value = "pointcut()", returning = "returnValue")
    public void afterReeturning(JoinPoint joinPoint, Object returnValue) {
        Log.e("Mr.S", "@AfterReturning" + "--" + returnValue);
    }

    @AfterThrowing(value = "pointcut()", throwing = "throwable")
    public void afterThrowing(Throwable throwable) {
        Log.e("Mr.S", "@AfterThrowing" + "--" + throwable.getMessage());
    }

我們的目標對象

 public String doSomeThing() {
        Intent intent = new Intent(MenuActivity.this, HellowActivity.class);
        startActivity(intent);
        return  "doSomeThing 執(zhí)行完畢";
    }

執(zhí)行結果

2018-12-21 11:08:43.898 24567-24567/com.ssy.qbd E/Mr.S: @Before
2018-12-21 11:08:43.908 24567-24567/com.ssy.qbd E/Mr.S: @Around
2018-12-21 11:08:43.908 24567-24567/com.ssy.qbd E/Mr.S: @After
2018-12-21 11:08:43.908 24567-24567/com.ssy.qbd E/Mr.S: @AfterReturning--doSomeThing 執(zhí)行完畢

實踐完畢邓萨,那么這個面向切面編程,到底是用在什么情況呢菊卷?這幾個方法的實際運用又是如何呢缔恳?我如何控制多個方法呢?不能一個方法一個切面吧的烁?

相信大家會有很多疑問褐耳。

那我們就根據實際情況來,靈活運用我們的 AOP渴庆。

舉例一:

某日,
老大說:S啊,咱們的產品要升級了襟雷,要增加權限控制了刃滓,對于沒有登錄的用戶,嚴格控制耸弄,比如評論必須登錄咧虎,不能匿名了,點贊也要先登錄 计呈、看新聞也要先登錄 砰诵。。捌显。
我:WTF茁彭?我挨個給你加嗎?

  if (isLogin()) {
            Intent intent = new Intent(MenuActivity.this, HellowActivity.class);
            }

哇 好煩啊~~有沒有簡單的扶歪,優(yōu)美的理肺,偷懶的做法呢?

老大說:你試試 AOP善镰。用過你就知道什么是偷懶妹萨,什么優(yōu)美了。
我:好 我試試炫欺。

先寫個注解,標注一下

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CheckLogin {
}

然后就是在我們的方法上添加注解

    @CheckLogin
    public String doSomeThing() {

        Intent intent = new Intent(MenuActivity.this, HellowActivity.class);
        startActivity(intent);


        return "doSomeThing 執(zhí)行完畢";
    }

最后在around 方法里進行我們的操作

  @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {

        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        CheckLogin checkLogin = signature.getMethod().getAnnotation(CheckLogin.class);
        if (checkLogin != null) {
            if (isLogin()) {
                Log.e("Mr.S", "@Around" + "--" + "登錄成功");
                return joinPoint.proceed();
            } else {
                Log.e("Mr.S", "@Around" + "--" + "請登錄");
                Login((Context) joinPoint.getThis());
                return null;
            }


        }
        return joinPoint.proceed();
    }

執(zhí)行結果:

  • 未登錄
2018-12-21 15:20:30.199 29343-29343/com.ssy.qbd E/Mr.S: @Before
2018-12-21 15:20:30.200 29343-29343/com.ssy.qbd E/Mr.S: @Around--請登錄
2018-12-21 15:20:30.200 29343-29343/com.ssy.qbd E/Mr.S: Login--登錄頁面
2018-12-21 15:20:30.200 29343-29343/com.ssy.qbd E/Mr.S: @After
2018-12-21 15:20:30.200 29343-29343/com.ssy.qbd E/Mr.S: @AfterReturning--null
  • 已登錄
2018-12-21 15:21:55.669 29463-29463/com.ssy.qbd E/Mr.S: @Before
2018-12-21 15:21:55.671 29463-29463/com.ssy.qbd E/Mr.S: @Around--登錄成功
2018-12-21 15:21:55.691 29463-29463/com.ssy.qbd E/Mr.S: @After
2018-12-21 15:21:55.691 29463-29463/com.ssy.qbd E/Mr.S: @AfterReturning--doSomeThing 執(zhí)行完畢

我們會發(fā)現(xiàn)乎完,我們可以對這個方法進行任意的操作,執(zhí)行前后加邏輯品洛,也可以屏蔽這個方法树姨,換成咱們的新方法。但是在我們的原方法那里是不用改變的毫别,我們可以更改邏輯娃弓,增加或者減少我們的功能。只要一個注解@CheckLogin岛宦,真實太方便了台丛。

這個例子其實還不算太變態(tài),如果是讓我們把所有的操作都加上日志怎么辦砾肺?我們是不是要在自己的代碼里挽霉,各種更改,這個侵入性太強了变汪,稍有不注意 bug 就鋪天蓋地侠坎。所以面向切面編程的方式。我們會更加輕松的裙盾。

只要是涉及統(tǒng)一處理的地方实胸,AOP基本都是一個利器他嫡。程序員追求的就是一個懶。能省事當然要省事了庐完。

當然 AspectJ 的真正實力遠比我們展現(xiàn)的要強大得多钢属,認識他,運用它门躯,你就會喜歡上它淆党。

參考:
http://www.reibang.com/p/aa1112dbebc7
https://www.cnblogs.com/weizhxa/p/8567942.html

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市讶凉,隨后出現(xiàn)的幾起案子染乌,更是在濱河造成了極大的恐慌,老刑警劉巖懂讯,帶你破解...
    沈念sama閱讀 206,723評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件荷憋,死亡現(xiàn)場離奇詭異,居然都是意外死亡域醇,警方通過查閱死者的電腦和手機台谊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來譬挚,“玉大人锅铅,你說我怎么就攤上這事〖跣” “怎么了盐须?”我有些...
    開封第一講書人閱讀 152,998評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長漆腌。 經常有香客問我贼邓,道長,這世上最難降的妖魔是什么闷尿? 我笑而不...
    開封第一講書人閱讀 55,323評論 1 279
  • 正文 為了忘掉前任塑径,我火速辦了婚禮,結果婚禮上填具,老公的妹妹穿的比我還像新娘统舀。我一直安慰自己,他們只是感情好劳景,可當我...
    茶點故事閱讀 64,355評論 5 374
  • 文/花漫 我一把揭開白布誉简。 她就那樣靜靜地躺著,像睡著了一般盟广。 火紅的嫁衣襯著肌膚如雪闷串。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,079評論 1 285
  • 那天筋量,我揣著相機與錄音烹吵,去河邊找鬼碉熄。 笑死,一個胖子當著我的面吹牛年叮,可吹牛的內容都是我干的具被。 我是一名探鬼主播玻募,決...
    沈念sama閱讀 38,389評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼只损,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了七咧?” 一聲冷哼從身側響起跃惫,我...
    開封第一講書人閱讀 37,019評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎艾栋,沒想到半個月后爆存,有當地人在樹林里發(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 43,519評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡蝗砾,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,971評論 2 325
  • 正文 我和宋清朗相戀三年先较,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片悼粮。...
    茶點故事閱讀 38,100評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡闲勺,死狀恐怖,靈堂內的尸體忽然破棺而出扣猫,到底是詐尸還是另有隱情菜循,我是刑警寧澤,帶...
    沈念sama閱讀 33,738評論 4 324
  • 正文 年R本政府宣布申尤,位于F島的核電站癌幕,受9級特大地震影響,放射性物質發(fā)生泄漏昧穿。R本人自食惡果不足惜勺远,卻給世界環(huán)境...
    茶點故事閱讀 39,293評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望时鸵。 院中可真熱鬧胶逢,春花似錦、人聲如沸寥枝。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽囊拜。三九已至某筐,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間冠跷,已是汗流浹背南誊。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評論 1 262
  • 我被黑心中介騙來泰國打工身诺, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人抄囚。 一個月前我還...
    沈念sama閱讀 45,547評論 2 354
  • 正文 我出身青樓霉赡,卻偏偏與公主長得像,于是被迫代替她去往敵國和親幔托。 傳聞我的和親對象是個殘疾皇子穴亏,可洞房花燭夜當晚...
    茶點故事閱讀 42,834評論 2 345

推薦閱讀更多精彩內容