注解JAVA

AspectJ+AOP+注解实现用户行为统计

2019-10-01  本文已影响0人  Chenyangqi

本文Demo地址:https://github.com/DaLeiGe/AndroidSamples/tree/master/AspectJAOP

AspectJ概念

AspectJ 是使用最为广泛的 AOP 实现方案
适用于 Java 平台
官网地址:http://www.eclipse.org/aspectj/
AspectJ 是在静态织入代码,即在编译期注入代码的
结合自定义注解实现AOP切面实现用户行为统计

AspectJ使用

只需要关注两个点

举个栗子

要实现统计某个方法的执行次数,在不更改且不增加原有代码逻辑的前提下通过AspectJ和自定义注解的方式实现

AspectJ环境配置

Project glide

// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
    repositories {
        google()
        jcenter()
        
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.2.1'

        // 版本界限:As-3.0.1 + gradle4.4-all (需要配置r17的NDK环境)
        // 或者:As-3.2.1 + gradle4.6-all (正常使用,无警告)
        classpath 'org.aspectj:aspectjtools:1.8.9'
        classpath 'org.aspectj:aspectjweaver:1.8.9'
    }
}

allprojects {
    repositories {
        google()
        jcenter()
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

Module glide

apply plugin: 'com.android.application'

// 版本界限:As-3.0.1 + gradle4.4-all (需要配置r17的NDK环境)
// 或者:As-3.2.1 + gradle4.6-all (正常使用,无警告)
buildscript { // 编译时用Aspect专门的编译器,不再使用传统的javac
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'org.aspectj:aspectjtools:1.8.9'
        classpath 'org.aspectj:aspectjweaver:1.8.9'
    }
}

android {
    compileSdkVersion 28
    buildToolsVersion "29.0.0"
    defaultConfig {
        applicationId "com.cyq.aspectjaop"
        minSdkVersion 21
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:28.0.0'
    testImplementation 'junit:junit:4.12'
    implementation 'org.aspectj:aspectjrt:1.8.13'
}


// 版本界限:As-3.0.1 + gradle4.4-all (需要配置r17的NDK环境)
// 或者:As-3.2.1 + gradle4.6-all (正常使用,无警告)
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;
            }
        }
    }
}

自定义注解ClickBehavior,作用在方法之上

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

// 用户点击痕迹(行为统计)  IoC容器
@Target(ElementType.METHOD) // 目标作用在方法之上
@Retention(RetentionPolicy.RUNTIME)
public @interface ClickBehavior {
    String value();
}

在要监听的业务方法上添加注解

// 用户行为统计
    @ClickBehavior("测试参数")
    public void coupon(View view) {
        Log.e(TAG, "开始跳转到 -> 我的优惠券 Activity");
        startActivity(new Intent(this, OtherActivity.class));
    }

然后就可以使用AspectJ定义切面类ClickBehaviorAspect
这个类有两个功能

import com.cyq.aspectjaop.annotation.ClickBehavior;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;

@Aspect // 定义切面类
public class ClickBehaviorAspect {

    private final static String TAG = "netease >>> ";

    // 1、应用中用到了哪些注解,放到当前的切入点进行处理(找到需要处理的切入点)
    // execution,以方法执行时作为切点,触发Aspect类
    // * *(..)) 可以处理ClickBehavior这个类所有的方法
    @Pointcut("execution(@com.cyq.aspectjaop.annotation.ClickBehavior * *(..))")
    public void methodPointCut() {}

    // 2、对切入点如何处理
    @Around("methodPointCut()")
    public Object jointPotin(ProceedingJoinPoint joinPoint) throws Throwable {
        // 获取签名方法
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();

        // 获取方法所属的类名
        String className = methodSignature.getDeclaringType().getSimpleName();

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

        // 获取方法的注解值(需要统计的用户行为)
        String funName = methodSignature.getMethod().getAnnotation(ClickBehavior.class).value();

        // 统计方法的执行时间、统计用户点击某功能行为。(存储到本地,每过x天上传到服务器)
        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("统计了:%s功能,在%s类的%s方法,用时%d ms",
                funName, className, methodName, duration));
        return result;
    }
}

执行结果如下

2019-10-01 00:13:55.263 10479-10479/com.cyq.aspectjaop E/netease >>>: ClickBehavior Method Start >>> 
2019-10-01 00:13:55.263 10479-10479/com.cyq.aspectjaop E/netease >>>: 模拟接口请求……验证通过,登录成功!
2019-10-01 00:13:55.263 10479-10479/com.cyq.aspectjaop E/netease >>>: ClickBehavior Method End >>> 
2019-10-01 00:13:55.264 10479-10479/com.cyq.aspectjaop E/netease >>>: 统计了:登录功能,在MainActivity类的login方法,用时0 ms

通过如上方式实现用户行为统计,可以避免更改原有业务逻辑

上一篇下一篇

猜你喜欢

热点阅读