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》.第一篇文章里面还有对相关概念的详细解释