简单聊聊AspectJ

2020-06-02 15:29:32 浏览数 (1)

1.AspectJ引入

考虑一个要在某些应用中实施安全策略的问题。安全性是贯穿于系统所有模块间的问题,而且每一模块都必须添加安全性才能保证整个应用的安全性,并且安全性模块自身也需要安全性. 传统的面向对象编程中,每个单元就是一个类,而类似于安全性这方面的问题,它们通常不能集中在一个类中处理因为它们横跨多个类,这就导致了代码无法重用,它们是不可靠 和不可承继的,这样可维护性差而且产生了大量代码冗余,这是我们不愿意看到的。 使用传统的编程解决此问题非常的困难而且容易产生差错,这就正是 AspectJ 发挥作用的时候了。

2.相关概念

AspectJ 是一种面向方面程序(AOP)设计的基于 Java 的实现。它向 Java 中加入了连接点(Join Point)这个新概念。它向Java语言中加入少许新结构:切点(pointcut)、通知(Advice)、类型间声明(Inter-type declaration)和方面(Aspect)。 切点和通知动态地影响程序流程,类型间声明则是静态的影响程序的类等级结构,而方面则是对所有这些新结构的封装。 连接点是程序流中适当的一点。切点收集特定的连接点集合和在这些点中的值。一个通知是当一个连接点到达时执行的代码,这些都是AspectJ的动态部分。其实连接点就好比是程序中的一条一条的语句,而切点就是特定一条语句处设置的一个断点,它收集了断点处程序栈的信息,而通知就是在这个断点前后想要加入的程序代码。

3.简单实践

3.1配置

整个项目的gradle中如下修改

代码语言:javascript复制
buildscript {
    
    repositories {
        google()
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.1.4'
        classpath 'org.aspectj:aspectjtools:1.9.4'
        classpath 'org.aspectj:aspectjweaver:1.9.4'
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

app.gradle中如下修改

代码语言:javascript复制
dependencies {
    ......
    implementation 'org.aspectj:aspectjrt:1.9.4'
    ......
}

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.2定义注解
代码语言:javascript复制
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface BehaviorTrace {
    String value();
}
3.3实现切面(Aspect)
代码语言:javascript复制
@Aspect
public class BehaviorAspect {

    // 定义切入点函数
    // 切点,执行com.xiaofan.customcontrol.annotation.BehaviorTrace路径下的注解
    // 后面的**..都是切入点指示符,在这里不细讲
    @Pointcut("execution(@com.example.liuxiaojie.aspectjdemo.BehaviorTrace * *(..))")
    public void annoBaviorTrace() {}

    /**
     * 五种通知
     */

    @Before("annoBaviorTrace()")
    public void before() {
        Log.i("wxf","before");
    }

    //执行annoBaviorTrace方法
    @Around("annoBaviorTrace()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        BehaviorTrace anno = methodSignature.getMethod().getAnnotation(BehaviorTrace.class);

        String functionName = anno.value();

        Log.i("tag","方法:" functionName);
        long begin = System.currentTimeMillis();
        //控制方法执行
        Object result= joinPoint.proceed();

        long duration = System.currentTimeMillis() - begin;

        Log.i("tag","方法耗时:" duration " ms");
        return result;
    }

    @After("annoBaviorTrace()")
    public void after() {
        Log.i("tag","After");
    }

    @AfterReturning(value = "annoBaviorTrace()", returning = "returnVal")
    public void afterReturn(Object returnVal) {
        Log.i("tag","afterReturn"   returnVal);
    }

    @AfterThrowing(value = "annoBaviorTrace()", throwing = "e")
    public void afterThrow() {
        Log.i("tag","afterThrow");
    }
}
3.4测试

现在添加如下测试代码进行测试,调用test以后就可以看到对应的log打出

代码语言:javascript复制
    @BehaviorTrace("method")
    public void test() {
        Log.i("wxf","test");
    }
3.5简单总结

先来简单看一下相关的class文件(test方法对应的class文件的内容)

代码语言:javascript复制
    @BehaviorTrace("method")
    public void test() {
        JoinPoint var1 = Factory.makeJP(ajc$tjp_0, this, this);

        try {
            BehaviorAspect.aspectOf().before();
            test_aroundBody1$advice(this, var1, BehaviorAspect.aspectOf(), (ProceedingJoinPoint)var1);
        } catch (Throwable var4) {
            BehaviorAspect.aspectOf().after();
            throw var4;
        }

        BehaviorAspect.aspectOf().after();
        Object var3 = null;
        BehaviorAspect.aspectOf().afterReturn(var3);
    }

可以看到生成JoinPoint,然后按照顺序before,after一步一步执行下去 接下来可以对第二点相关概念进行更为详细的阐述

  • 连接点(Join Point)---就是你需要添加代码的具体的位置,由Aspect自动生成
  • 切点(pointcut)---就是加了Pointcut注解的函数,表示要添加代码的具体的位置,由它来决定Join Point的生成位置
  • 通知(Advice)---就是Before,Around等五种通知
  • 类型间声明(Inter-type declaration)---Aspect内部定义的属性,这个demo没有
  • 方面(Aspect)------就是以上四点的封装,也就是加了Aspect注解的BehaviorAspect类

总结:由pointcut决定Join Point的位置,然后通过Advice函数通知出去 具体可以参考关于 Spring AOP (AspectJ) 你该知晓的一切,《AspectJ程序设计指南.pdf》.第一篇文章里面还有对相关概念的详细解释

0 人点赞