Android中使用AspectJ(AOP)实现切面的三种功能(执行前、执行后、环绕)

Andorid Studio中配置AspectJ

app module中

dependencies {
	implementation 'org.aspectj:aspectjrt:1.9.4'
}

project module中

buildscript {
    dependencies {
        classpath 'org.aspectj:aspectjtools:1.9.4'
    }
}

如果需要在AspectJ中打印日志还需要在app module中结尾添加如下代码

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.9",
                         "-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;
            }
        }
    }
}

使用@Before做登录验证

定义一个验证登录的注解

package com.xxx.xxx.annotation;

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

//登录验证
@Target(ElementType.METHOD)//作用域为方法
@Retention(RetentionPolicy.RUNTIME)//范围运行时
public @interface UserLogin {
    String value();
}

实现一个切面类,用于拦截上面所写的注解切面

package com.xxx.xxx.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

/**
 * Describe : 验证登录的切面
 * 若要使用aspectj,需要在类上标记@Aspect
 */
@Aspect
public class UserLoginAspect {
    //定义切面的规则
    //@Pointcut 表示一个切入点
    //格式:execution(@注解的全路径 注解用的地方)
    //这里表示注解UserLogin下的所有方法都会被该切入点拦截
    @Pointcut("execution(@com.xxx.xxx.annotation.UserLogin * *(..))")
    public void methodAnnottatedWithUserLogin() {
    }

    //处理切面内容,指向哪个切入点
    //@Before 在切入点之前运行
    //@After 在切入点之后运行
    //@Around 在切入点前后都运行
    @Before("methodAnnottatedWithUserLogin()")
    public Object checkLogin(ProceedingJoinPoint joinPoint) throws Throwable {
        if (isLogin()) {//在进入某个方法之前,先判断是否登录
            toLogin();//如果没有登录则去登陆,不再继续往下执行该方法
            return null;
        } else {//如果登录则继续往下执行该方法
            Object result = joinPoint.proceed();//继续执行方法
            return result;
        }
    }
}

使用该切面

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
    
    @UserLogin("测试登录控制")
    public void onAspcetJByLogin(View view) {
        //需要登录才能执行的逻辑
    }
}

使用@Around实现性能监控

定义一个性能监控的注解

package com.xxx.xxx.annotation;

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

//性能跟踪
@Target(ElementType.METHOD)//作用域为方法
@Retention(RetentionPolicy.RUNTIME)//范围运行时
public @interface BehaviorTrace {
   String value();
}


package com.xxx.xxx.aspect;

import android.util.Log;

import com.xxx.xxx.annotation.BehaviorTrace;

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;

import java.lang.reflect.Method;

/**
 * Describe : 性能切面类
 * 若要使用aspectj,需要在类上标记@Aspect
 */
@Aspect
public class BehaviorAspect {
    //定义切面的规则
    //1、就再原来的应用中那些注解的地方放到当前切面进行处理
    //execution(注解名   注解用的地方)
    @Pointcut("execution(@com.xxx.xxx.annotation.BehaviorTrace *  *(..))")
    public void methodAnnottatedWithBehaviorTrace() {
    }

    //处理切面内容
    //@Before 在切入点之前运行
    //@After 在切入点之后运行
    //@Around 在切入点前后都运行
    @Around("methodAnnottatedWithBehaviorTrace()")
    public Object checkBehaviorTrace(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        //获取名为login()的方法
        Method loginMethod=methodSignature.getReturnType().getMethod("login");
        //获得类名
        String className = methodSignature.getDeclaringType().getSimpleName();
        //获得当前拦截的方法名
        String methodName = methodSignature.getName();
        //获得当前拦截的方法名上标记的注解中的vaule的值
        String value = methodSignature.getMethod().getAnnotation(BehaviorTrace.class).value();
        //在这里计算当前被拦截的方法所消耗的时间
        long begin = System.currentTimeMillis();
        Object result = joinPoint.proceed();//方法继续执行
        long duration = System.currentTimeMillis() - begin;
        Log.d("alan", String.format("%s功能:%s类的%s方法执行了,用时%d ms",
                value, className, methodName, duration));
        return result;
    }
}

使用

public class MainActivity extends AppCompatActivity {

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

    @BehaviorTrace("检测性能")
    public void onAspcetJByBehaviorTrace(View view) {
        //测试性能,这里使主线程睡5s之内随机的时间,模拟一个耗时操作
        SystemClock.sleep(new Random().nextInt(5000));
    }
}

使用@After做行为日志记录

创建行为日志记录的注解

package com.xxx.xxx.annotation;

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

/**
 * Describe:用户行为日志记录
 */

@Target(ElementType.METHOD)//作用域为方法
@Retention(RetentionPolicy.RUNTIME)//范围运行时
public @interface CourseLog {
    //当前记录来自于哪个用户
    String userId();
}

实现日志记录的切面

package com.xxx.xxx.aspect;

import android.util.Log;

import com.xxx.xxx.annotation.CourseLog;

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

/**
 * Describe: 用户行为日志记录切面
 */
@Aspect
public class CouresLogAspect {
    @Pointcut("execution(@com.xxx.xxx.annotation.CourseLog * *(..))")
    public void getCouresLog() {

    }

    @After("getCouresLog()")
    public Object checkCouresLog(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        //获得类名
        String className = methodSignature.getDeclaringType().getSimpleName();
        //获得当前拦截的方法名
        String methodName = methodSignature.getName();
        //调用时间
        long createTime = System.currentTimeMillis();
        //获得当前拦截的方法名上标记的注解中的userid
        String userid = methodSignature.getMethod().getAnnotation(CourseLog.class).userId();
        //这里相当于将这条行为日志上传,这里只是打印出来
        Log.d("alan", String.format("%s类的%s方法在%l时间被%s用户调用了",
                className, methodName, createTime, userid));
        return null;
    }
}

使用

public class MainActivity extends AppCompatActivity {

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

    @CourseLog(userId = "uid_123456")
    public void onAspectJByCourseLog(View view) {
        //用户调用了该方法后就会上传一条记录
    }
}

发布了113 篇原创文章 · 获赞 48 · 访问量 34万+

猜你喜欢

转载自blog.csdn.net/yehui928186846/article/details/98254778