切面思想之集中式登錄架構(gòu)設(shè)計(jì)

如何利用切面的思想實(shí)現(xiàn)集中式登錄?AspectJ

AspectJ 介紹

AspectJ是一個(gè)面向切面編程的框架,它擴(kuò)展了Java語(yǔ)言答倡。AspectJ定義了AOP語(yǔ)法,它有一個(gè)專門(mén)的編譯器用來(lái)生成遵守Java字節(jié)編碼規(guī)范的Class文件驴党。AspectJ還支持原生的Java,只需要加上AspectJ提供的注解即可获茬。在Android開(kāi)發(fā)中港庄,一般就用它提供的注解和一些簡(jiǎn)單的語(yǔ)法就可以實(shí)現(xiàn)絕大部分功能上的需求了倔既。

Pointcut(切入點(diǎn))
告訴代碼注入工具,在何處注入一段特定代碼的表達(dá)式鹏氧。例如渤涌,在哪些 joint points 應(yīng)用一個(gè)特定的 Advice。切入點(diǎn)可以選擇唯一一個(gè)把还,比如執(zhí)行某一個(gè)方法实蓬,也可以有多個(gè)選擇,比如吊履,標(biāo)記了一個(gè)定義成@DebguTrace 的自定義注解的所有方法安皱。

Advice(通知)
注入到class文件中的代碼。典型的 Advice 類型有 before艇炎、after 和 around酌伊,分別表示在目標(biāo)方法執(zhí)行之前、執(zhí)行后和完全替代目標(biāo)方法執(zhí)行的代碼缀踪。 除了在方法中注入代碼居砖,也可能會(huì)對(duì)代碼做其他修改,比如在一個(gè)class中增加字段或者接口驴娃。

Joint point(連接點(diǎn))
程序中可能作為代碼注入目標(biāo)的特定的點(diǎn)奏候,例如一個(gè)方法調(diào)用或者方法入口。

Android 中使用Gradle集成 AspectJ

在Android中集成AspectJ唇敞,主要思想就是hook Apk打包過(guò)程蔗草,使用AspectJ提供的工具來(lái)編譯.class文件。
1、配置我們項(xiàng)目根目錄中的build.gradle

   dependencies {
        classpath 'com.android.tools.build:gradle:3.5.0'

        // 版本界限:As-3.0.1 + gradle4.4-all (需要配置r17的NDK環(huán)境)
        // 或者:As-3.2.1 + gradle4.6-all (正常使用宝踪,無(wú)警告)
        classpath 'org.aspectj:aspectjtools:1.8.9'
        classpath 'org.aspectj:aspectjweaver:1.8.9'
    }

注意:
版本界限:As-3.0.1 + gradle4.4-all (需要配置r17的NDK環(huán)境)
或者:As-3.2.1 + gradle4.6-all (正常使用婿崭,無(wú)警告)
或者:As-3.4.0 + gradle5.1.1-all (有過(guò)時(shí)的API警告)

2、配置app目錄中的build.gradle

apply plugin: 'com.android.application'

//添加的第一處需要添加的代碼 編譯時(shí)用Aspect專門(mén)的編譯器狠轻,不再使用傳統(tǒng)的javac
buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'org.aspectj:aspectjtools:1.8.9'
        classpath 'org.aspectj:aspectjweaver:1.8.9'
    }
}

...

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:28.0.0'
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
    testImplementation 'junit:junit:4.12'
    //添加的第二處需要添加的代碼 引入包
    implementation 'org.aspectj:aspectjrt:1.8.13'
}

//添加的第三處處需要添加的代碼 主要做支持
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;
            }
        }
    }
}
AspectJ的使用

登錄中我們需要做兩點(diǎn)

  • 判斷是否登錄
  • 做用戶行為統(tǒng)計(jì)

如下代碼是自定義注解,用于用戶行為統(tǒng)計(jì)和用戶登錄檢測(cè)彬犯。如果想要學(xué)習(xí)自定義注解向楼,大家可以看一下這篇博客Android 自定義注解(Annotation)

package com.aop.login.annotation;
// 用戶點(diǎn)擊痕跡(行為統(tǒng)計(jì))
@Target(ElementType.METHOD) // 目標(biāo)作用在方法之上
@Retention(RetentionPolicy.RUNTIME)//注解不僅被保存到class文件中,jvm加載class文件之后谐区,仍然存在
public @interface ClickBehavior {
    String value();
}

package com.aop.login.annotation;
// 用戶登錄檢測(cè)
@Target(ElementType.METHOD) // 目標(biāo)作用在方法之上
@Retention(RetentionPolicy.RUNTIME)
public @interface LoginCheck {
}
@Aspect // 定義切面類
public class LoginCheckAspect {
    private final static String TAG = "TAG";
    // 1湖蜕、應(yīng)用中用到了哪些注解,放到當(dāng)前的切入點(diǎn)進(jìn)行處理(找到需要處理的切入點(diǎn))
    // execution宋列,以方法執(zhí)行時(shí)作為切點(diǎn)昭抒,觸發(fā)Aspect類
    // * *(..)) 可以處理ClickBehavior這個(gè)類所有的方法
    @Pointcut("execution(@com.aop.login.annotation.LoginCheck * *(..))")
    public void methodPointCut() {
    }
    // 2、對(duì)切入點(diǎn)如何處理
    @Around("methodPointCut()")
    public Object jointPotin(ProceedingJoinPoint joinPoint) throws Throwable {
        Context context = (Context) joinPoint.getThis();
        if (true) { // 從SharedPreferences中讀取
            Log.e(TAG, "檢測(cè)到已登錄!");
            return joinPoint.proceed();
        } else {
            Log.e(TAG, "檢測(cè)到未登錄灭返!");
            Toast.makeText(context, "請(qǐng)先登錄盗迟!", Toast.LENGTH_SHORT).show();
            context.startActivity(new Intent(context, LoginActivity.class));
            return null; // 不再執(zhí)行方法(切入點(diǎn))
        }
    }
}
@Aspect // 定義切面類
public class ClickBehaviorAspect {

    private final static String TAG = "TAG";

    // 1、應(yīng)用中用到了哪些注解熙含,放到當(dāng)前的切入點(diǎn)進(jìn)行處理(找到需要處理的切入點(diǎn))
    // execution罚缕,以方法執(zhí)行時(shí)作為切點(diǎn),觸發(fā)Aspect類
    // * *(..)) 可以處理ClickBehavior這個(gè)類所有的方法
    @Pointcut("execution(@com.aop.login.annotation.ClickBehavior * *(..))")
    public void methodPointCut() {}

    // 2怎静、對(duì)切入點(diǎn)如何處理
    @Around("methodPointCut()")
    public Object jointPotin(ProceedingJoinPoint joinPoint) throws Throwable {
        // 獲取簽名方法
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();

        // 獲取方法所屬的類名
        String className = methodSignature.getDeclaringType().getSimpleName();

        // 獲取方法名
        String methodName = methodSignature.getName();

        // 獲取方法的注解值(需要統(tǒng)計(jì)的用戶行為)
        String funName = methodSignature.getMethod().getAnnotation(ClickBehavior.class).value();

        // 統(tǒng)計(jì)方法的執(zhí)行時(shí)間邮弹、統(tǒng)計(jì)用戶點(diǎn)擊某功能行為。(存儲(chǔ)到本地蚓聘,每過(guò)x天上傳到服務(wù)器)
        long begin = System.currentTimeMillis();
        Log.e(TAG, "ClickBehavior Method Start >>> ");
        Object result = joinPoint.proceed(); // MainActivity中切面的方法
        long duration = System.currentTimeMillis() - begin;
        Log.e(TAG, "ClickBehavior Method End >>> ");
        Log.e(TAG, String.format("統(tǒng)計(jì)了:%s功能腌乡,在%s類的%s方法,用時(shí)%d ms",
                funName, className, methodName, duration));

        return result;
    }
}

MainActivity.java

public class MainActivity extends Activity {
    private final static String TAG = "TAG";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
    // 登錄點(diǎn)擊事件(用戶行為統(tǒng)計(jì))
    @ClickBehavior("登錄")
    public void login(View view) {
        Log.e(TAG, "模擬接口請(qǐng)求……驗(yàn)證通過(guò)或粮,登錄成功导饲!");
    }
    // 我的專區(qū)點(diǎn)擊事件(用戶行為統(tǒng)計(jì))
    @ClickBehavior("我的專區(qū)")
    @LoginCheck
    public void area(View view) {
        Log.e(TAG, "開(kāi)始跳轉(zhuǎn)到 -> 我的專區(qū) Activity");
        startActivity(new Intent(this, AreaActivity.class));
    }
    // 我的優(yōu)惠卷點(diǎn)擊事件(用戶行為統(tǒng)計(jì))
    @ClickBehavior("我的優(yōu)惠券")
    @LoginCheck
    public void coupon(View view) {
        Log.e(TAG, "開(kāi)始跳轉(zhuǎn)到 -> 我的優(yōu)惠券 Activity");
        startActivity(new Intent(this, CouponActivity.class));
    }

    // 我的積分點(diǎn)擊事件(用戶行為統(tǒng)計(jì))
    @ClickBehavior("我的積分")
    @LoginCheck
    public void score(View view) {
        Log.e(TAG, "開(kāi)始跳轉(zhuǎn)到 -> 我的積分 Activity");
        startActivity(new Intent(this, ScoreActivity.class));
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市氯材,隨后出現(xiàn)的幾起案子渣锦,更是在濱河造成了極大的恐慌,老刑警劉巖氢哮,帶你破解...
    沈念sama閱讀 222,590評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件袋毙,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡冗尤,警方通過(guò)查閱死者的電腦和手機(jī)听盖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,157評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)裂七,“玉大人皆看,你說(shuō)我怎么就攤上這事”沉悖” “怎么了腰吟?”我有些...
    開(kāi)封第一講書(shū)人閱讀 169,301評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)徙瓶。 經(jīng)常有香客問(wèn)我毛雇,道長(zhǎng),這世上最難降的妖魔是什么侦镇? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 60,078評(píng)論 1 300
  • 正文 為了忘掉前任灵疮,我火速辦了婚禮,結(jié)果婚禮上壳繁,老公的妹妹穿的比我還像新娘震捣。我一直安慰自己荔棉,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,082評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布蒿赢。 她就那樣靜靜地躺著江耀,像睡著了一般。 火紅的嫁衣襯著肌膚如雪诉植。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,682評(píng)論 1 312
  • 那天昵观,我揣著相機(jī)與錄音晾腔,去河邊找鬼。 笑死啊犬,一個(gè)胖子當(dāng)著我的面吹牛灼擂,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播觉至,決...
    沈念sama閱讀 41,155評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼剔应,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了语御?” 一聲冷哼從身側(cè)響起峻贮,我...
    開(kāi)封第一講書(shū)人閱讀 40,098評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎应闯,沒(méi)想到半個(gè)月后纤控,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,638評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡碉纺,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,701評(píng)論 3 342
  • 正文 我和宋清朗相戀三年船万,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片骨田。...
    茶點(diǎn)故事閱讀 40,852評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡耿导,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出态贤,到底是詐尸還是另有隱情舱呻,我是刑警寧澤,帶...
    沈念sama閱讀 36,520評(píng)論 5 351
  • 正文 年R本政府宣布抵卫,位于F島的核電站狮荔,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏介粘。R本人自食惡果不足惜殖氏,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,181評(píng)論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望姻采。 院中可真熱鬧雅采,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,674評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至巴刻,卻和暖如春愚铡,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背胡陪。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,788評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工沥寥, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人柠座。 一個(gè)月前我還...
    沈念sama閱讀 49,279評(píng)論 3 379
  • 正文 我出身青樓邑雅,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親妈经。 傳聞我的和親對(duì)象是個(gè)殘疾皇子淮野,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,851評(píng)論 2 361

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

  • WHY 如果說(shuō)OOP(面向?qū)ο蟮某绦蛟O(shè)計(jì))的主要思想是將問(wèn)題歸分到相應(yīng)的對(duì)象(類)中去實(shí)現(xiàn),再把相關(guān)類模塊化使得模...
    野生大P閱讀 959評(píng)論 0 8
  • 作者簡(jiǎn)介:ASCE1885, 《Android 高級(jí)進(jìn)階》作者吹泡。本文由于潛在的商業(yè)目的骤星,未經(jīng)授權(quán)不開(kāi)放全文轉(zhuǎn)載許可...
    asce1885閱讀 2,404評(píng)論 0 7
  • AOP之AspectJ - 代碼注入 [TOC] 一、AOP簡(jiǎn)介 1.1 什么是AOP編程 AOP是Aspect ...
    everlastxgb閱讀 2,540評(píng)論 2 10
  • 晚上,全家人開(kāi)車(chē)去孩子外婆家聚餐泪漂。用完餐回到小院準(zhǔn)備上樓廊营,老公讓我先去開(kāi)門(mén)。我告訴他我沒(méi)帶鑰匙萝勤,今天換了個(gè)包露筒。...
    微光半糖閱讀 221評(píng)論 0 4
  • 曾經(jīng)曾想再重來(lái)一次 選擇留下。 以為想的夠透徹敌卓。 選擇慎式,與當(dāng)初一樣 月還是月 是否不理智就能得到。 不是趟径, 是更卑...
    何嘗_Enya閱讀 984評(píng)論 0 1