动机
最近在看ButterKnife源码的时候,居然发现有一个类叫做AbstractProcessor
,并且ButterKnife的View绑定不是依靠反射来实现的,而是使用了编译时的注解,自动生成的.class文件。真是一个神奇的东西啊!!
所以本文就注解与自定义的注解处理器来学习注解。项目Github地址
基础知识
大家应该知道元注解@Retention
吧,它表示注解在什么时候存在,可以分为3个时期:
RetentionPolicy.SOURCE
:表示注解只在java文件里面才有,在编译的时候,这部分注解就会被擦出。类似于@Override
只是给程序员看的。RetentionPolicy.CLASS
:注解在编译的时候会存在,在运行的时候就会擦除。RetentionPolicy.RUNTIME
:在运行的时候会存在,这里就要用到了反射来实现了。
Annotation Processor
注解处理器,在编译期间,JVM会自动运行注解处理器(当然,我们需要将其注册)。虽然我们写的Java代码被编译成class就不能被改动了,但是注解处理器会重新生成其他的java代码,我们可以通过反射来调用新生成的java文件里面的类或方法。然后JVM再对这些生成的java代码进行编译。这是一个递归的过程。
Java API为我们提供了注解处理器的接口,我们只要实现它就可以了。这个类就是AbstractProcessor
,下面就一起来实现它。这个例子我是从【Java二十周年】Java注解处理器,参考过来的。实现功能如下:对于一个类,只要给它的某个加上一个注解,就会自动生成其接口,接口中的方法就是加注解的方法。
public class Man {
@Interface("ManInterface")
public void eat() {
System.out.println("Eat");
}
}
eat
方法加上了@Interface("ManInterface")
,上述就会生成:
public interface ManInterface {
void eat();
}
就是这么粗暴。
然后我们看看注解是怎么定义的:
package wqh.core;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Created on 2016/8/10.
*
* @author 王启航
* @version 1.0
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
public @interface Interface {
String value();
}
注意上面@Target(ElementType.METHOD)
表示该方法只能加在方法上面;@Retention(RetentionPolicy.CLASS)
表示是在编译时存在的。
好了接下来就是重点了:
public class InterfaceProcessor extends AbstractProcessor {
@Override
public synchronized void init(ProcessingEnvironment env){ }
@Override
public boolean process(Set<? extends TypeElement> annoations, RoundEnvironment env) { }
@Override
public Set<String> getSupportedAnnotationTypes() { }
@Override
public SourceVersion getSupportedSourceVersion() { }
}
定义自己的注解处理器
init
对一些工具进行初始化。process
就是真正生成java代码的地方。getSupportedAnnotationTypes
表示该注解处理器可以出来那些注解。getSupportedSourceVersion
可以出来java版本
我们首先看看除去process
的其他三个方法:
public class InterfaceProcessor extends AbstractProcessor {
private Types typeUtils;
private Elements elementUtils;
private Filer filer;
private Messager messager;
@Override
public synchronized void init(ProcessingEnvironment env) {
super.init(env);
elementUtils = env.getElementUtils();
filer = env.getFiler();
typeUtils = env.getTypeUtils();
messager = env.getMessager();
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) {
}