AspectJ 作为aop的著名工具,现在就让我们进行使用他,不逼逼。
接入有两种方式,一种是组件化式接入,一种是非组件化接入。组件化接入,就是当我们的项目是组件化开发时,有多个moudle。
第一步:在项目gradle中添加aspectJ的依赖
classpath 'org.aspectj:aspectjtools:1.8.6'
第二步:在app gradle中添加依赖和配置
implementation 'org.aspectj:aspectjrt:1.8.13'
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;
}
}
}
}
以上代码放到最下面就好了。
现在配置就完成了
第三步:添加测试:
**
* Created by malei on 2019/2/28.
*/
@Aspect //必须使用@AspectJ标注,这样class DemoAspect就等同于 aspect DemoAspect了
public class DemoAspect {
String TAG = "DemoAspect";
/*
@Pointcut:pointcut也变成了一个注解,这个注解是针对一个函数的,比如此处的logForActivity()
其实它代表了这个pointcut的名字。如果是带参数的pointcut,则把参数类型和名字放到
代表pointcut名字的logForActivity中,然后在@Pointcut注解中使用参数名。
基本和以前一样,只是写起来比较奇特一点。后面我们会介绍带参数的例子
*/
@Pointcut("execution(* com.ml.maskpro.ui.MainActivity.onCreate(..)) ||"
+"execution(* com.ml.maskpro.ui.MainActivity.onStart(..))")
public void logForActivity(){}; //注意,这个函数必须要有实现,否则Java编译器会报错
/*
@Before:这就是Before的advice,对于after,after -returning,和after-throwing。对于的注解格式为
@After,@AfterReturning,@AfterThrowing。Before后面跟的是pointcut名字,然后其代码块由一个函数来实现。比如此处的log。
*/
@Before("logForActivity()")
public void log(JoinPoint joinPoint){
//对于使用Annotation的AspectJ而言,JoinPoint就不能直接在代码里得到多了,而需要通过
//参数传递进来。
Log.e(TAG, joinPoint.toShortString() + " aop拦截");
}
}
这样,我们的MainActivity的onCreate()方法被触发的时候,我们就会进行拦截,并打印指定的内容。
使用组件化的方式接入,就避免了重复编写代码的问题。
接下来,用几步就完成我们的组件化接俄入吧。
第一步:首先让我们创建一个moudle --> aspectJlib
1)moudle 里面的东西都删除(除了build.gradle,但是要被gradle里面的所有内容删除)
2)在aspectJlib里面新增一个“src”文件夹,往src文件夹新增main文件夹,再往main文件夹新增groovy和resources文件夹
3)resources目录下新建文件夹META-INF,META-INF文件夹下新建gradle-plugins文件夹。
4)在groovy目录下新建项目包名,就像Java包名那样。
第二步:打开Module下的build.gradle文件,输入
apply plugin: 'groovy'
apply plugin: 'maven'
dependencies {
implementation gradleApi()//gradle sdk
implementation localGroovy()//groovy sdk
implementation 'com.android.tools.build:gradle:3.0.1'
implementation 'org.javassist:javassist:3.20.0-GA'
implementation 'org.aspectj:aspectjtools:1.8.13'
}
//上传到仓库
uploadArchives {
repositories.mavenDeployer {
pom.groupId = 'com.ml.pro'
pom.artifactId = 'aspectjlib'
pom.version = '1.0.0'
repository(url: uri('../repo'))
}
}
repositories {
jcenter()
}
uploadArchives闭包里面配置的是将插件发布到本地仓库。然后maven本地仓库的目录就是当前项目目录下的repo目录。
第三步:在groovy下的包名下新建一个文件,命名为AspectjPlugin.groovy
package com.ml.pro
import org.aspectj.bridge.IMessage
import org.aspectj.bridge.MessageHandler
import org.aspectj.tools.ajc.Main
import org.gradle.api.Plugin
import org.gradle.api.Project
/**
* @使用ajc编译java代码 , 同 时 织 入 切 片 代 码
* 使用 AspectJ 的编译器(ajc,一个java编译器的扩展)
* 对所有受 aspect 影响的类进行织入。
* 在 gradle 的编译 task 中增加额外配置,使之能正确编译运行。
*/
public class AspectjPlugin implements Plugin {
void apply(Project project) {
project.dependencies {
compile 'org.aspectj:aspectjrt:1.8.9'
}
final def log = project.logger
log.error "========================";
log.error "Aspectj切片开始编织Class!";
log.error "========================";
project.android.applicationVariants.all { variant ->
def 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;
}
}
}
}
}
}
第四步:在resources/META-INF/gradle-plugins目录下新建一个 AspectjPlugin.properties 文件,命名就是刚才创建 .groovy 的名字.
在里面输入:
implementation-class=com.ml.pro.AspectjPlugin
第五步:右侧的gradle Toolbar就会在module下多出一个task,点击uploadArchives这个Task,就会在项目下多出一个repo目录,里面存着这个gradle插件。
这样切面组件的组件化部分就完成了。现在就可以提供给其他module使用了。
第六步:在项目 gadle中添加
buildscript {
repositories {
...
maven { url uri('repo') } //插件仓库路径
}
dependencies {
...
classpath 'com.ml.pro:aspectjlib:1.0.0' //插件
}
}
第七步:先Rebuild Project,最后在app下的build.gradle添加配置
apply plugin: 'AspectjPlugin'
同时也要添加依赖(但是在实际编码时,没有添加依然可以使用):
compile 'org.aspectj:aspectjrt:1.8.9'
这样就完成了,我们就进行测试:
/**
* Created by malei on 2019/2/28.
*/
@Aspect
public class AspectTest {
final String TAG = AspectTest.class.getSimpleName();
/**
* 在MainActivity的所有生命周期的方法中打印log
* @param joinPoint
* @throws Throwable
*/
@Before("execution(* *..MainActivity+.on**(..))")
public void method(JoinPoint joinPoint) throws Throwable {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
String className = joinPoint.getThis().getClass().getSimpleName();
Log.e(TAG, "class:" + className);
Log.e(TAG, "method:" + methodSignature.getName());
}
}