AspectJ是面向切面编程的一个框架,它遵循了Jvm的语法并扩展了java语言,我们知道一个类想要运行起来,是先javac命令变成.class(字节码) 再由java命令把该类加载到jvm中,而AspectJ就在javac命令在.java 文件转变成.class文件的时候植入一些代码,这是在编译期完成的,不会影响性能
但是在使用AspectJ有一些炕,比如:
android studio 3.0.1 gradle-all 4.4要配置一个ndk r17
这要去下载ndk-r17的环境然后配置好
android studio 3.2.1 gradle-all 4.6这是最好的
android studio 3.4.0 gradle-all 5.1.1有过时的api警告
下面讲下aspectJ在Android中的配置
第一步
在根目录的build.gradle中配置如下:
classpath 'org.aspectj:aspectjtools:1.8.9'
classpath 'org.aspectj:aspectjweaver:1.8.9'
第二步
在项目的build.gradle配置
配置一:
buildscript { // 编译时用Aspect专门的编译器,不再使用传统的javac
repositories {
mavenCentral()
}
dependencies {
classpath 'org.aspectj:aspectjtools:1.8.9'
classpath 'org.aspectj:aspectjweaver:1.8.9'
}
}
如图:
配置2
在dependencies中引入依赖库
implementation 'org.aspectj:aspectjrt:1.8.13'
当你引入这个库后再编译就发现报错了:
上面的问题暂是不解决 把gradle 改成4.6 studio改成3.2.1 错误最后去解决 然后还要配置在
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;
}
}
}
}
这个配置跟android和dependencies是同级 别配置错了 这是groovy的语法
上面环境全部配置好了,现在使用AspectJ
第一个案例
统计某个方法执行的时间
先定义一个注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ClickBehavior {
String value();
}
再定义一个切面类
@Aspect // 定义切面类
public class ClickBehaviorAspect {
private final static String TAG = ClickBehaviorAspect.class.getSimpleName();
@Pointcut("execution(@com.example.login.annotation.ClickBehavior * *(..))")
public void pointCut() {}
// 2、对切入点如何处理
@Around("pointCut()")
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();
long begin = System.currentTimeMillis();
Object result = joinPoint.proceed(); // MainActivity中切面的方法
long duration = System.currentTimeMillis() - begin;
Log.e(TAG, "此方法运行的时长:"+duration);
return result;
}
}
使用:
@ClickBehavior("收藏")
private void collect() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.e("ClickBehaviorAspect","收藏");
}
其实AspectJ实现起来还是挺简单的,现在对上面的代码进行说明
Pointcut:切入点,它后面跟随的是表达式,比如execution是固定写法,后面
@com.netease.aop.login.annotation.ClickBehavior * *(..)
是指那个方法中使用到了该ClickBehavior注解,pointCut()方法是随便定义的,不需要具体实现
@Around("pointCut()")是对切点进行处理,@Around括号中的切入点标记要和上面定义的保持一致,
Around:表示在对切入点前后都可以进行一些额外的操作,就是说在你使用到的那个注解方法前后可以进行一些业务操作
Before:表示在对切入点前可以进行一些额外的操作,就是说在你使用到的那个注解方法前可以进行一些业务操作
After:表示在对切入点后可以进行一些额外的操作,就是说在你使用到的那个注解方法后可以进行一些业务操作
当然如果要很了解AspectJ中一些通配符的使用,就去查下,如果是Java后端的哥们,学过Spring的现在是必会.
AOP背后的原理还是使用了动态代理.