1.首先要知道一点自定义注解:
用来标识注解的注解称为元注解,一共四种:Target指定注解的对象(类,方法,属性),Retention指定注解的生命周期(SOURCE,CLASS,RUNTIME)依次递增,RUNTIME表示保留到运行时。Inherited表示可以被子类继承,Documented表示注解信息添加到java文件中。
更新一下Rentention的理解:SOURCE在编译成class文件的时候被丢弃,一般用于@Override等检查注解。CLASS在jvm加载class时被丢弃,可用于butterknife等在编译期生成代码的。RUNTIME则一直保留到运行时,用于需要在运行时动态获取信息的。
新建一个Annotation文件(或者java文件改class为@interface),注解中有属性的概念,如定义一个属性:
int a() default 1;
default 1顾名思义就是默认值,可以不写。加上生命周期和修饰对象后:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
int a() default 1;
}
找个方法试一试:
@MyAnnotation(a = 2)
public void test(){
}
2.aspectj是在java编译成class字节码的时候向注解处插入一些代码从而实现功能的。AOP指面向切面编程,统一处理项目中的一类问题,例如一种常见的情况,用户登录。很多界面要求先登录才能进入,这里就产生了一个登录判断的需求,可以对这些需求一一写重复的代码判断,也可以使用AOP的思想,在这些地方使用一个注解,注解定义了判断等操作,这样显得简洁明了。
开始操作:
1.导入aspectj:
在libs中加入aspectj的jar包,然后在项目的gradle里配置(提示下载失败,重启就可以了。。。):
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'org.aspectj:aspectjtools:1.8.8'
classpath 'org.aspectj:aspectjweaver:1.8.8'
}
}
在app.gradle末尾加上这些代码(运行aspectj,打印Log等操作):
import org.aspectj.bridge.IMessage
import org.aspectj.bridge.MessageHandler
import org.aspectj.tools.ajc.Main
//标注1
final def log = project.logger
final def variants = project.android.applicationVariants
variants.all { variant ->
//标注2
if (!variant.buildType.isDebuggable()) {
log.debug("Skipping non-debuggable build type '${variant.buildType.name}'.")
return;
}
//标注3
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);
//标注4
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;
}
}
}
}
2.新建一个aspectj注解类:
@Aspect
public class MyAspectj {
@Pointcut("execution(@com.example.zhangyong.myapplication.MyAnnotation * *(..))")
public void process(){
Log.v("zy","process");
}
@Around("process()")
public void beforeProcess(JoinPoint point){
Log.v("zy","beforeProcess");
}
}
@Pointcut表示切入点,也就是要插入代码的对象,这里execution里的路径表示被MyAnnotation修饰的方法,注意这里使用了通配符,里面的()表示方法的参数。
@Around表示插入代码的位置,括号里指定上面定义的那个方法,与之对应的还有@Before,@After。假设修饰的方法叫test,则实际运行时代码执行顺序:Before--test。test--After。而Around只会执行这个@Around修饰的方法,不会执行test,可以在方法内通过参数JoinPoint point来手动执行。
参数不一定是JoinPoint,也可以是ProceedingJointPoint。ProceedingJointPoint可以直接调用proceed执行。JoinPoint定义参考https://www.jianshu.com/p/27b997677149。
3.一个从JoinPoint获取信息的例子:
@Before("process()")
public void beforeProcess(ProceedingJoinPoint point){
//获取签名
Signature signature = point.getSignature();
if (!(signature instanceof MethodSignature)){
//判断修饰的是方法
throw new IllegalStateException("只能修饰方法");
}
MethodSignature methodSignature = (MethodSignature) signature;
MyAnnotation annotation =
methodSignature.getMethod().getAnnotation(MyAnnotation.class);
int a = annotation.a();
try {
//还有一个有参数的重载,方法执行所需的参数都需要传的
point.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
刚才又看到一个例子,可以指定Pointut为所有onCreate等方法来打印生命周期