kotlin收藏

Android--AOP架构设计之使用AspectJ监测方法耗时

2021-11-01  本文已影响0人  aruba

AOP为面向切面编程,听着很高大上,实际上就是为了将业务分离,比如我们在网络请求时,需要生成一个签名放入请求头Header,以往的做法是封装一个方法获取签名,AOP只是实现方式不同,将封装方法改为注解形式

AOP概念,将注解的方法、对象等看成是一个连接点,由这些被注解的点构成一个切入点,然后对切入点做具体的处理。这就是三个步骤而已,没什么高端的

AOP也有很多实现方式,AspectJ是利用替换Javac编译器方式,将字节码改变,生成代理类,来达到调用我们注解方法

一、AspectJ注解

使用AspectJ,需要用到它提供的注解:

还有就是最后一步,具体处理方法使用的注解:

二、Gradle配置

AspectJ是一个客户端,拥有自己的编译器,所以要在Gradle中指定,使用AspectJ的编译器进行编译

1.在module的gradle最上面添加:
buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'org.aspectj:aspectjtools:1.9.6'
        classpath 'org.aspectj:aspectjweaver:1.9.6'
    }
}

plugins {
    id 'com.android.application'
}

...
2.上面的gradle中新增编译脚本
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;
            }
        }
    }
}
3.依赖库文件

下载地址:https://gitee.com/aruba/aopapplication/blob/master/app/libs/aspectjrt.jar

完整gradle代码如下:
buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'org.aspectj:aspectjtools:1.9.6'
        classpath 'org.aspectj:aspectjweaver:1.9.6'
    }
}

plugins {
    id 'com.android.application'
}

android {
    compileSdk 30

    defaultConfig {
        applicationId "com.aruba.aopapplication"
        minSdk 21
        targetSdk 30
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}


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;
            }
        }
    }
}

dependencies {

    implementation 'androidx.appcompat:appcompat:1.2.0'
    implementation 'com.google.android.material:material:1.3.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
    implementation files('libs\\aspectjrt.jar')
    testImplementation 'junit:junit:4.+'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}

三、使用

1.定义注解,来用于表示一个连接点
/**
 * 用于注解连接点
 * Created by aruba on 2021/11/1.
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface BehaviorTrace {
    String value();
}
2.使用Aspect

首先使用Aspect注解类,再通过Pointcut注解指定该切面中的切入点(这边就是方法),最后使用Around注解来替换原来的方法执行

/**
 * Created by aruba on 2021/11/1.
 */
@Aspect
public class AspectBehavior {

    //指定使用BehaviorTrace注解的所有包下所有类的所有方法,为一个切入点
    @Pointcut("execution(@com.aruba.aopapplication.annotations.BehaviorTrace * *(..))")
    public void methodAnnotatedWithBehaviorTrace() {
    }

    //争对上面切入点methodAnnotatedWithBehaviorTrace的所有连接点进行处理
    @Around("methodAnnotatedWithBehaviorTrace()")
    public Object weaveJoinPoint(ProceedingJoinPoint joinPoint) {
        Object ret = null;

        try {
            //获取方法签名
            MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
            //获取注解
            BehaviorTrace annotation = methodSignature.getMethod().getAnnotation(BehaviorTrace.class);

            //执行之前记录下时间
            long startTime = System.currentTimeMillis();
            //方法执行
            ret = joinPoint.proceed();
            //方法执行完后的耗时
            long diffTime = System.currentTimeMillis() - startTime;

            String clzName = methodSignature.getDeclaringType().getSimpleName();
            String methodName = methodSignature.getName();

            Log.d("aruba", String.format("功能:%s 类:%s中方法:%s执行耗时:%d ms", annotation.value(), clzName, methodName, diffTime));
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }

        return ret;
    }

}
3.使用自定义注解来注解方法

注解方法就是表示该方法为一个连接点

public class MainActivity extends AppCompatActivity {

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

    @BehaviorTrace("上传")
    public void upload(View view) {
        SystemClock.sleep(300);
    }

    @BehaviorTrace("下载")
    public void download(View view) {
        SystemClock.sleep(400);
    }

    @BehaviorTrace("扫码")
    public void scan(View view) {
        SystemClock.sleep(500);
    }
}

三个按钮点击后的日志如下:


项目地址:https://gitee.com/aruba/aopapplication/tree/master
上一篇下一篇

猜你喜欢

热点阅读