注解的分类
(1)标注注解
- @Overrride:对覆盖超类中的方法进行标记,如果被标记的方 法并没有实际覆盖超类中的方法,则编译器会发出警告。
- @Deprecate:提示开发者该方法已经废弃,不推荐使用。
- @SupperWarnings:选择性地取消特定代码中的警告。
(2)元注解
用来标注其它注解而创建的新注解,元注解的类型有以下几种:
- @Target:注解所修饰的对象范围
- @Inherited:表示注解可以被继承
- @Documented:表示这个注解应该被JavaDoc工具记录
- @Rentation:用来声明注解的保留策略
- @Repeable:JDK8新增,允许一个注解在同一声明类型(类,属性或方法)上多次使用。
其中@Target注解的取值是一个ElementType类型的枚举,其中有一下几种取值,对应不同的对象范围。
- ElementType.TYPE:能修饰类,接口或者枚举类型。
- ElementType.FIELD:能修饰成员变量
- ElementType. METHOD:能修饰方法
- ElementType. PARAMETER:能修饰参数
- ElementType. CONSTRUCTOR:能修饰构造方法
- ElementType.LOCAL_VARIABLE:能修饰局部变量
- ElementType. ANNOTATION_TYPE:能修饰注解
- ElementType. PACKAGE:能修饰包
- ElementType.TYPE_PARAMETER:能修饰参数声明
- ElementType.TYPE_USE:使用类型
其中@Retention注解有3种类型,分别表示不同级别的保留策略。
-
RetentionPolicy.CLASS:源码级注解,注解信息只会保留在Java源码中,源码在编译后,注解信息将会被丢弃,不会保留在.class文件中。
-
RetentionPolicy.CLASS:编译时注解。注解信息会保留在.java源码以及.class中。当运行java程序时,JVM会丢弃该注解信息,不会保留在JVM中,许多注解类框架,如EventBus,ButterKnife他们都是使用编译时注解信息开发的,针对编译时注解我们采用AbstractProcessor来处理注解信息。
-
RetentionPolicy.RUNTIME:运行注解。当运行Java程序时,JVM也会保留该注解信息,可以通过反射获取该注解信息。
AnnotationProcessor的使用
1、新建annotationslib库(java lib),
package com.example;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.TYPE)
public @interface BindView {
}
2、新建processorlib库(java lib)
package com.example;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeSpec;
import java.io.IOException;
import java.util.Collections;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
@SupportedSourceVersion(SourceVersion.RELEASE_7)
@SupportedAnnotationTypes("com.example.BindView")
public class ButterKnifeProcessor extends AbstractProcessor {
private Messager mMessage;
private Filer filer;
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
mMessage = processingEnvironment.getMessager();
filer = processingEnv.getFiler(); // for creating file
}
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
productHelloWordClass();
for(Element element : roundEnvironment.getElementsAnnotatedWith(BindView.class)){
mMessage.printMessage(Diagnostic.Kind.NOTE,"printMessage : " + element.toString());
}
return true;
}
@Override
public Set<String> getSupportedAnnotationTypes() {
// return super.getSupportedAnnotationTypes();
return Collections.singleton(BindView.class.getCanonicalName());
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
//javapoet生成HelloWorld文件
public void productHelloWordClass(){
MethodSpec main = MethodSpec.methodBuilder("main")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.returns(void.class)
.addParameter(String[].class, "args")
.addStatement("$T.out.println($S)", System.class, "Hello, JavaPoet!")
.build();
TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addMethod(main)
.build();
try {
// build com.example.HelloWorld.java
JavaFile javaFile = JavaFile.builder("com.example", helloWorld)
.addFileComment(" This codes are generated automatically. Do not modify!")
.build();
// write to file
javaFile.writeTo(filer);
} catch (IOException e) {
e.printStackTrace();
}
}
}
javapoet关键类说明
class 说明
JavaFile A Java file containing a single top level class 用于构造输出包含一个顶级类的Java文件
TypeSpec A generated class, interface, or enum declaration 生成类,接口,或者枚举
MethodSpec A generated constructor or method declaration 生成构造函数或方法
FieldSpec A generated field declaration 生成成员变量或字段
ParameterSpec A generated parameter declaration 用来创建参数
AnnotationSpec A generated annotation on a declaration 用来创建注解
在JavaPoet中,JavaFile是对.java文件的抽象,TypeSpec是类/接口/枚举的抽象,MethodSpec是方法/构造函数的抽象,FieldSpec是成员变量/字段的抽象。这几个类各司其职,但都有共同的特点,提供内部Builder供外部更多更好地进行一些参数的设置以便有层次的扩展性的构造对应的内容。
它提供$L(for Literals), $S(for Strings), $T(for Types), $N(for Names)等标识符,用于占位替换。
为了能使用注解处理器,需要用一个服务器文件来注册它,现在我们就来创建这个服务器文件,首先在processor库的main目录下新建resources资源文件夹,接下来在resources文件夹下新建META-INF/services目录文件夹。最后在META-INF/ services中创建javax.annotation.processing.Processor文件,文件的内容是我们上面定义com.example.ButterKnifeProcessor类的完整路径。
appmodle使用注解,在gradle文件中引入两个modle
compile project(':annotationslib')
annotationProcessor project(':processorlib')
build之前会HelloWorld会报错,因为没有生成java文件,在build之后就可以了,在build/generated/source/apt/debug下面生成了HelloWorld文件
如果不使用javax.annotation.processing.Processor文件配置com.example.ButterKnifeProcessor,就可以用auto-service,在processorlib modle中引用
compile 'com.google.auto.service:auto-service:1.0-rc2'
ButterKnifeProcessor使用@AutoService(Processor.class)自动配置
@AutoService(Processor.class)
public class ButterKnifeProcessor extends AbstractProcessor {
private Messager mMessage;
private Filer filer;