使用AspectJ完成AOP切面

面向切面的程序设计:通过横切关注点与业务主体进行一步分离,以提高程序代码的模块程度。

举例来说,比如超级会员、会员、普通用户登录一个系统,那么他们都要使用到登录业务,这时就可以利用横向切面将整个业务分割出来,并植入一些代码,达到统一管理的效果。在Android开发中,可以使用AspectJ框架来实现。

ASpectJ有2个重要的概念:PointCut(切入点)、JointPoint(连接点)切入点,就是要执行某个方法之前,抢先一步在这个方法之前切入并执行一些代码,而这些代码就可以通过JointPoint来完成。

AspectJ主要有3步:

举例:在登录界面点击个人主页,购物车时记录用户的行为信息

打开Android Studio,首先要引入AspectJ, 修改app的build.gradle

apply plugin: 'com.android.application'

buildscript {   //编译使用Aspect的编译器,不再使用传统的javac
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'org.aspectj:aspectjtools:1.8.9'
        classpath 'org.aspectj:aspectjweaver:1.8.9'
    }
}
android {
    compileSdkVersion 29
    buildToolsVersion "29.0.2"
    defaultConfig {
        applicationId "com.example.aspectj_logindemo"
        minSdkVersion 21
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.appcompat:appcompat:1.0.2'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.0'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
    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就成功引进了!

接下来完成用户行为的统计功能,简单界面如图:

当点击”个人主页“和”购物车的时候“记录用户的行为,这些用户信息对开发商很重要。

新建一个自定义注解:

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

    String value();
}

为按钮方法注释注解:

@ClickEvent("login")
public void login(View view) {
    Log.d(TAG, "login: ");
    SharedPreferences.Editor editor = getSharedPreferences("share", Context.MODE_PRIVATE).edit();
    editor.putBoolean(LOGIN, true);
    editor.apply();
    startActivity(new Intent(this, LoginActivity.class));
}

@ClickEvent("personal_page")
public void personal_page(View view) {
    Log.d(TAG, "personal_page: ");
    startActivity(new Intent(this, OtherActivity.class));
}

@ClickEvent("cart")
public void cart(View view) {
    Log.d(TAG, "cart: ");
    startActivity(new Intent(this, OtherActivity.class));
}

这些注解将成为切入点,即:获取所有被注解的方法。

新建一个切面类:

	@Aspect
	public class ClickAspect {
	}

只要为新建的类添加@Aspect注解,哪个这个类就成为切面类了,类名无所谓。

在类中设置切入点:

@Pointcut("execution(@com.example.aspectj_logindemo.ClickEvent * *(..))")
public void methodPointCut(){}

表示被ClickEvent所注解的方法都将成为切入点

然后对切入点进行处理:

@Around("methodPointCut()")
public Object jointPoint(ProceedingJoinPoint joinPoint) throws Throwable{
	.....
}

这个ProceedingJoinPoint对象就是切入点方法,它有个方法是proceed,当ProceedingJoinPoint调用proceed方法时,会等价于调用所切入点切入的方法。

比如当点击登录按钮时,Aspect就会切入这个对应的login()方法,如果:

public Object jointPoint(ProceedingJoinPoint joinPoint) throws Throwable{
    Log.D(TAG, "before method start");
	return joinPoint.proceed();
	Log.D(TAG, "method ending");
}

那么终端就会打印:

before method start
login
method ending

这样一来AOP的效果就达到了!

所以获取用户行为信息就可以写成:

    @Around("methodPointCut()")
    public Object jointPoint(ProceedingJoinPoint joinPoint) throws Throwable{
        Log.d(TAG, "jointPoint: ");
        //获取方法所属的类名
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        String className = methodSignature.getDeclaringType().getSimpleName();

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

        ClickEvent annotation = methodSignature.getMethod().getAnnotation(ClickEvent.class);//获取方法的注解
        String annotationValue = null;
        if(annotation != null) {
            annotationValue = annotation.value();   //获取方法的注解值
        }
        long start= System.currentTimeMillis();
        Log.d(TAG, "jointPoint: >>>>>   Method start");
        Object result = joinPoint.proceed();    //此时对应的方法会执行
        long end = System.currentTimeMillis();
        Log.d(TAG, "jointPoint: >>>>>   Method end");

        //通常的项目是把这些值保存到数据库中,然后在某个时间点(定时1天或者1星期后)发送远程服务器这些开发商才能用来记录用户的行为,这里直接就通过log打印了
        Log.d(TAG, "jointPoint: "+String.format("类名%s,方法名%s,注解值%s, 方法执行时间%d", className,
                methodName, annotationValue, end - start));
        return result;
    }

demo地址:https://download.youkuaiyun.com/download/weixin_45253393/12329619

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

哒哒呵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值