注解的定义格式如下:
public @interface 注解名 {定义体}
定义体就是方法的集合,每个方法实则是声明了一个配置参数.方法的名称作为配置参数的名称,方法的返回值类型就是配置参数的类型.和普通的方法不一样,可以通过default关键字来声明配置参数的默认值.
需要注意:
(1)此处只能使用public或者默认的defalt两个权限修饰符
(2)配置参数的类型只能使用基本类型(byte,boolean,char,short,int,long,float,double)和String,Enum,Class,annotation
(3)对于只含有一个配置参数的注解,参数名建议设置中value,即方法名为value
(4)配置参数一旦设置,其参数值必须有确定的值,要不在使用注解的时候指定,要不在定义注解的时候使用default为其设置默认值,对于非基本类型的参数值来说,其不能为null
关于注解值在javac中使用AnnotationValue类来表示,定义如下:
**
* Represents a value of an annotation type element.
* A value is of one of the following types:
* (1)a wrapper class (such as {@link Integer}) for a primitive type
* (2)String
* (3)TypeMirror
* (4)VariableElement (representing an enum constant)
* (5)AnnotationMirror
* (6)List<? extends AnnotationValue> (representing the elements, in declared order, if the value is an array)
*
*/
public interface AnnotationValue {
/**
* Returns the value.
*
* @return the value
*/
Object getValue();
/**
* Returns a string representation of this value.
* This is returned in a form suitable for representing this value
* in the source code of an annotation.
*
* @return a string representation of this value
*/
String toString();
/**
* Applies a visitor to this value.
*
* @param <R> the return type of the visitor's methods
* @param <P> the type of the additional parameter to the visitor's methods
* @param v the visitor operating on this value
* @param p additional parameter to the visitor
* @return a visitor-specified result
*/
<R, P> R accept(AnnotationValueVisitor<R, P> v, P p);
}
值得一提的是,值可以为数组或者TypeMirror。AnnotationValue的唯一实现类为Attribute,定义如下:
public abstract class Attribute implements AnnotationValue {
/** The type of the annotation element. */
public Type type;
public Attribute(Type type) {
this.type = type;
}
public abstract void accept(Visitor v);
public Object getValue() {
throw new UnsupportedOperationException();
}
public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) {
throw new UnsupportedOperationException();
}
/** A visitor type for dynamic dispatch on the kind of attribute value. */
public static interface Visitor {
void visitConstant(Constant value);
void visitClass(Class clazz);
void visitCompound(Compound compound);
void visitArray(Array array);
void visitEnum(Enum e);
void visitError(Error e);
}
/** A mirror of java.lang.annotation.RetentionPolicy. */
public static enum RetentionPolicy {
SOURCE,
CLASS,
RUNTIME
}
}
(1)Constant The value for an annotation element of primitive type or String.
(2)Class The value for an annotation element of type java.lang.Class,represented as a ClassSymbol.
(3)Compund A compound annotation element value, the type of which is an attribute interface.
(4)Enum The value for an annotation element of an enum type.
(5)Array The value for an annotation element of an array type.
(6)Error
AnnotationMirror类的定义如下:
**
* Represents an annotation. An annotation associates a value with each element of an annotation type.
*
* Annotations should be compared using the equals
* method. There is no guarantee that any particular annotation will always be represented by the same object.
*
*/
public interface AnnotationMirror {
/**
* Returns the type of this annotation.
*
* @return the type of this annotation
*/
DeclaredType getAnnotationType();
/**
* Returns the values of this annotation's elements.
* This is returned in the form of a map that associates elements
* with their corresponding values.
* Only those elements with values explicitly present in the
* annotation are included, not those that are implicitly assuming
* their default values.
* The order of the map matches the order in which the
* values appear in the annotation's source.
*
* <p>Note that an annotation mirror of a marker annotation type
* will by definition have an empty map.
*
* <p>To fill in default values, use {@link
* javax.lang.model.util.Elements#getElementValuesWithDefaults
* getElementValuesWithDefaults}.
*
* @return the values of this annotation's elements,
* or an empty map if there are none
*/
Map<? extends ExecutableElement, ? extends AnnotationValue> getElementValues();
}
其中ExecutableElement的解释为:
Represents a method, constructor, or initializer (static or instance) of a class or interface, including annotation type elements.
根据值类型定义了几个类并通过访问者模式进行访问。其中的Compound类最为重要,如下:
/** A compound annotation element value, the type of which is an attribute interface.
*/
public class Compound extends Attribute implements AnnotationMirror {
/** The attributes values, as pairs. Each pair contains a
* reference to the accessing method in the attribute interface
* and the value to be returned when that method is called to
* access this attribute.
*/
public final List<Pair<MethodSymbol,Attribute>> values;
public Compound(Type type, List<Pair<MethodSymbol, Attribute>> values) {
super(type);
this.values = values;
}
public void accept(Visitor v) { v.visitCompound(this); }
/**
* Returns a string representation of this annotation.
* String is of one of the forms:
* @com.example.foo(name1=val1, name2=val2)
* @com.example.foo(val)
* @com.example.foo
* Omit parensthesis for marker annotations, and omit "value=" when allowed.
*/
public String toString() {
StringBuilder buf = new StringBuilder();
buf.append("@");
buf.append(type);
int len = values.length();
if (len > 0) {
buf.append('(');
boolean first = true;
for (Pair<MethodSymbol, Attribute> value : values) {
if (!first) buf.append(", ");
first = false;
Name name = value.fst.name;
if (len > 1 || name != name.table.names.value) {
buf.append(name);
buf.append('=');
}
buf.append(value.snd);
}
buf.append(')');
}
return buf.toString();
}
public Attribute member(Name member) {
for (Pair<MethodSymbol, Attribute> pair : values){
if (pair.fst.name == member){
return pair.snd;
}
}
return null;
}
public Compound getValue() {
return this;
}
public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) {
return v.visitAnnotation(this, p);
}
public DeclaredType getAnnotationType() { // 实现AnnotationMirror接口中的方法
return (DeclaredType) type;
}
public Map<MethodSymbol, Attribute> getElementValues() { // 实现AnnotationMirror接口中的方法
Map<MethodSymbol, Attribute> valmap = new LinkedHashMap<MethodSymbol, Attribute>();
for (Pair<MethodSymbol, Attribute> value : values){
valmap.put(value.fst, value.snd);
}
return valmap;
}
}
举个具体的例子,如下:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface MzTargetPackage {
// 基本类型的包装类
boolean isCanNull() default true;
int num() default 100;
// String类型
public String version() default "";
// enum类型
public enum Color{ BULE,RED,GREEN};
Color fruitColor() default Color.GREEN;
// 数组类型 {"a","b","c"}
String[] value();
Code compoundTest();
}
类Test中的process()方法使用如上注解,如下:
public class Test {
@MzTargetPackage(isCanNull =true,
num=100,
version = "1.1.0",
fruitColor=MzTargetPackage.Color.BULE,
value={"a,","b","c"},
compoundTest=@Code(author = "closedevice",date="20161225")
)
private void process() {
}
}
查看process()方法中sym属性值中的attributes_field属性,截图如下:
对于第4个和第5个Pair对象的截图如下:
下面来编写相应的注解处理器。,注解处理器可以分为运行时注解处理和编译时注解处理器.运行时处理器需要借助反射机制实现,而编译时处理器则需要借助APT来实现.
无论是运行时注解处理器还是编译时注解处理器,主要工作都是读取注解及处理特定注解,从这个角度来看注解处理器还是非常容易理解的.我们重点来看编译时的注解处理器。
public class CodeProcessor extends AbstractProcessor {
private final String SUFFIX = "Generate";
private Messager messager;
private Filer filer;
private Types typeUtil;
// 返回字符串的集合表示该处理器用于处理那些注解
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
messager = processingEnvironment.getMessager();
filer = processingEnvironment.getFiler();
typeUtil = processingEnvironment.getTypeUtils();
}
// 用来指定支持的java版本,一般来说我们都是支持到最新版本,因此直接返回 SourceVersion.latestSupported()即可
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
// 由注解处理器自动调用,其中ProcessingEnvironment类提供了很多实用的工具类:Filter,Types,Elements,Messager等
@Override
public Set<String> getSupportedAnnotationTypes() {
LinkedHashSet<String> annotations = new LinkedHashSet<String>();
annotations.add(Code.class.getCanonicalName());
return annotations;
}
// annotations 处理注解的过程要经过一个或者多个回合才能完成。每个回合中注解处理器都会被调用,并且接收到一个以在当前回合中已经处理过的注解类型的Type为元素的Set集合。
// roundEnv 通过这个对象可以访问到当前或者之前的回合中处理的Element元素(译注:可以被注解类型注解的元素,如类、方法、参数等等),只有被注解处理器注册过的元素才会被处理。
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
for (Element e : roundEnvironment.getElementsAnnotatedWith(Code.class)) {//find special annotationed element
Code ca = e.getAnnotation(Code.class);
TypeElement clazz = (TypeElement) e.getEnclosingElement();
try {
generateCode(e, ca, clazz);
} catch (IOException x) {
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, x.toString());
return false;
}
}
return true;
}
//generate
private void generateCode(Element e, Code ca, TypeElement clazz) throws IOException {
JavaFileObject f = filer.createSourceFile(clazz.getQualifiedName() + SUFFIX);
messager.printMessage(Diagnostic.Kind.NOTE, "Creating " + f.toUri());
Writer w = f.openWriter();
try {
String pack = clazz.getQualifiedName().toString();
PrintWriter pw = new PrintWriter(w);
pw.println("package " + pack.substring(0, pack.lastIndexOf('.')) + ";"); //create package element
pw.println("\n class " + clazz.getSimpleName() + "Autogenerate {");//create class element
pw.println("\n protected " + clazz.getSimpleName() + "Autogenerate() {}");//create class construction
pw.println(" protected final void message() {");//create method
pw.println("\n//" + e);
pw.println("//" + ca);
pw.println("\n System.out.println(\"author:" + ca.author() + "\");");
pw.println("\n System.out.println(\"date:" + ca.date() + "\");");
pw.println(" }");
pw.println("}");
pw.flush();
} finally {
w.close();
}
}
}
如上继承AbstractProcessor,实现自己的注解处理器。这样每次Javac编译器在生成对应class文件时如果发现集合中的某个注解,那么会调用相应的注解处理器。在init()方法中传入了ProcessingEnvironment类型的参数,通过这个参数可以获取实用的工具类。而在process()方法中传入了RoundEnvironment类型的参数。这两个类型需要重点了解。
public interface RoundEnvironment {
boolean processingOver();
// 上一轮注解处理器是否产生错误
boolean errorRaised();
// 返回上一轮注解处理器生成的根元素
Set<? extends Element> getRootElements();
// 返回包含指定注解类型的元素的集合
Set<? extends Element> getElementsAnnotatedWith(TypeElement a);
// 返回包含指定注解类型的元素的集合
Set<? extends Element> getElementsAnnotatedWith(Class<? extends Annotation> a);
}
通过RoundEnvironment这个类名可以知道,注解处理过程是一个有序的循环过程。在每次循环中,一个处理器可能被要求去处理那些在上一次循环中产生的源文件和类文件中的注解。第一次循环的输入是运行此工具的初始输入。这些初始输入,可以看成是虚拟的第0此的循环的输出。
public interface ProcessingEnvironment {
Map<String,String> getOptions();
// Messager用来报告错误,警告和其他提示信息
Messager getMessager();
// Filter用来创建新的源文件,class文件以及辅助文件
Filer getFiler();
// Elements中包含用于操作Element的工具方法
Elements getElementUtils();
// Types中包含用于操作TypeMirror的工具方法
Types getTypeUtils();
SourceVersion getSourceVersion();
Locale getLocale();
}
在Javac中有两个专门操作Element与TypeMirror的工具方法,分别是JavacElements与JavacTypes,分别实现了Elements与Types接口。下面来了解下两个类。
(1)Element
各个Element的继承体系图如下:
各个元素的具体说明如下:
VariableElement | Represents a field, {@code enum} constant, method or constructor parameter, local variable, |
PackageElement | Represents a package program element. Provides access to information about the package and its members. |
TypeElement | Represents a class or interface program element. Provides access to information about the type and |
ExecutableElement | Represents a method, constructor, or initializer (static or instance) of a class or interface, |
TypeParameterElement | Represents a formal type parameter of a generic class, interface, method,or constructor element. |
Symbol | Root class for Java symbols.It contains subclasses for specific sorts of symbols,such as variables, |
(2)TypeMirror
TypeMirror代表java语言中的类型。Types包括基本类型,声明类型(类类型和接口类型),数组,类型变量和空类型。也代表通配类型参数,可执行文件的签名和返回类型等。TypeMirror类中最重要的是 getKind()
方法,该方法返回TypeKind类型
看一下TypeMirror的整个继承体系图如下:
在IDEA中得到如上图时,需要打开TypeMirror类,将鼠标聚焦在TypeMirror类中,然后在Naviaget中点击Type Hierarchy即可。
所有的Type都间接继承了TypeMirror接口。这个接口中定义了一个getKind()方法,返回TypeMirror的具体类型。
public enum TypeKind {
BOOLEAN,
BYTE,
SHORT,
INT,
LONG,
CHAR,
FLOAT,
DOUBLE,
VOID,
/**
* A pseudo-type used where no actual type is appropriate.
* @see NoType
*/
NONE,
NULL,
ARRAY,
/**
* A class or interface type.
*/
DECLARED,
/**
* A class or interface type that could not be resolved.
*/
ERROR,
/**
* A type variable.
*/
TYPEVAR,
/**
* A wildcard type argument.
*/
WILDCARD,
/**
* A pseudo-type corresponding to a package element.
* @see NoType
*/
PACKAGE,
/**
* A method, constructor, or initializer.
*/
EXECUTABLE,
/**
* An implementation-reserved type.
* This is not the type you are looking for.
*/
OTHER,
/**
* A union type.
* @since 1.7
*/
UNION;
}
自定义的处理器需要被打成一个jar,并且需要在jar包的META-INF/services路径下中创建一个固定的文件javax.annotation.processing.Processor,在javax.annotation.processing.Processor文件中需要填写自定义处理器的完整路径名。
将打出的jar防止到项目的buildpath下即可,javac在运行的过程会自动检查javax.annotation.processing.Processor注册的注解处理器,并将其注册上。
或者使用javac命令来指定注解处理器。与注解处理器的有关的命令有5个,分别如下:
(1)-XprintProcessorInfo 输出有关请求处理程序处理哪些注释的信息
(2)-XprintRounds 输出有关注释处理循环的信息
(3)-processor 路径为包路径,因为指定的路径要通过loadClass()方法来加载
Names of the annotation processors to run. This bypasses the default discovery process.
(4)-processpath
Specifies where to find annotation processors. If this option is not used, then the class path is searched for processors.
(5)-proc: 当指定-proc:none值时不对注解进行处理。
Controls whether annotation processing and compilation are done. -proc:none
means that compilation takes place without annotation processing. -proc:only
means that only annotation processing is done, without any subsequent compilation.
(6)-Xprint 如果有这个命令,则Javac中有个专门的注解处理类PrintingProcessor。
现在写一个类来使用如上的CodeProcess注解处理器,如下:
public class TestAnnotation {
@Code(author = "closedevice",date="20161225")
private void process() {
}
}
当编译这个类时如上的注解处理器就会执行,生成相应的文件。
参考文章:
(1)http://blog.csdn.net/qinxiandiqi/article/details/49182735
(2)http://www.tuicool.com/articles/6rIjIfi
(3)https://www.race604.com/annotation-processing/
(4)注解处理器相关Demo https://github.com/sockeqwe/annotationprocessing101/tree/master/factory