注解的定义格式如下:

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, resource variable, or exception parameter.
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 its members. Note that an enum type is a kind of class and an annotation type is a kind ofinterface.
ExecutableElement
Represents a method, constructor, or initializer (static or instance) of a class or interface,including annotation type elements.
TypeParameterElement

Represents a formal type parameter of a generic class, interface, method,or constructor element.A type parameter declares a {@link TypeVariable}.
Symbol
Root class for Java symbols.It contains subclasses for specific sorts of symbols,such as variables, methods and operators,types, packages.

(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

  

关于注解Annotation第一篇的更多相关文章

  1. 关于注解Annotation第二篇

    写一个注解使用类,如下: public class Test { @Code(author = "mazhi",date="20170611") private ...

  2. spring 第一篇(1-1):让java开发变得更简单(下)转

    spring 第一篇(1-1):让java开发变得更简单(下) 这个波主虽然只发了几篇,但是写的很好 上面一篇文章写的很好,其中提及到了Spring的jdbcTemplate,templet方式我之前 ...

  3. JUnit扩展:引入新注解Annotation

    发现问题 JUnit提供了Test Suite来帮助我们组织case,还提供了Category来帮助我们来给建立大的Test Set,比如BAT,MAT, Full Testing. 那么什么情况下, ...

  4. spring boot实战(第一篇)第一个案例

    版权声明:本文为博主原创文章,未经博主允许不得转载.   目录(?)[+]   spring boot实战(第一篇)第一个案例 前言 写在前面的话 一直想将spring boot相关内容写成一个系列的 ...

  5. 小BUG大原理 | 第一篇:重写WebMvcConfigurationSupport后SpringBoot自动配置失效

    一.背景 公司的项目前段时间发版上线后,测试反馈用户的批量删除功能报错.正常情况下看起来应该是个小BUG,可怪就怪在上个版本正常,且此次发版未涉及用户功能的改动.因为这个看似小BUG我了解到不少未知的 ...

  6. 【第一篇】ASP.NET MVC快速入门之数据库操作(MVC5+EF6)

    目录 [第一篇]ASP.NET MVC快速入门之数据库操作(MVC5+EF6) [第二篇]ASP.NET MVC快速入门之数据注解(MVC5+EF6) [第三篇]ASP.NET MVC快速入门之安全策 ...

  7. Java注解Annotation学习

    学习注解Annotation的原理,这篇讲的不错:http://blog.csdn.net/lylwo317/article/details/52163304 先自定义一个运行时注解 @Target( ...

  8. spring beans源码解读之--Bean的注解(annotation)

    随着spring注解的引入,越来越多的开发者开始使用注解,这篇文章将对注解的机制进行串联式的讲解,不求深入透彻,但求串起spring beans注解的珍珠,展示给大家. 1. spring beans ...

  9. JAVA提高五:注解Annotation

    今天我们学习JDK5.0中一个非常重要的特性,叫做注解.是现在非常流行的一种方式,可以说因为配置XML 比较麻烦或者比容易查找出错误,现在越来越多的框架开始支持注解方式,比如注明的Spring 框架, ...

随机推荐

  1. MEF程序设计指南

    ############################################################################################## ##### ...

  2. Linux 部署 tomcat 常用命令

    1.  文件夹重命名 mv somedir somedir1 2. 授权所有子目录 chmod -R 777 somedir 3.授权单个目录 chmod 777 somedir 4.实时打印控制台日 ...

  3. 11) 生成可执行jar文件 maven-shade-plugin

    搜索 site:maven.apache.org maven-assembly-plugin http://maven.apache.org/plugins/maven-assembly-plugin ...

  4. Lucene原理一

    Lucene 是一个高效的,基于Java 的全文检索库. 所以在了解Lucene之前要费一番工夫了解一下全文检索. 那么什么叫做全文检索呢?这要从我们生活中的数据说起. 我们生活中的数据总体分为两种: ...

  5. iOS Development和iOS Distribution有什么区别

    http://zhidao.baidu.com/link?url=T9od33JuA7jjxzfyV-wOjuVLSNUaqpc9aoCu2HjfYfHBuRLW1CNDii0Bh9uvG6h-GeJ ...

  6. 一、配置etcd数据库

      etcd服务作为Kubernetes集群的主数据库,在安装Kubernetes各服务之前需要首先安装和启动. 1. 安装etcd yum -y install etcd 2. 修改etcd配置文件 ...

  7. bootstrap-table 中取主键字段的问题,主键名不叫id

    问题 :取不到数据行的主键 要绑定的数据字段 RoleId rolename adddate RoleId 为主键是唯一的 bootstraptable的配置 uniqueId: "Role ...

  8. think in java 手记(一)

    工作之余,不知道要做些什么.该做的事情都完成的差不多了,想看一下spring的东西,毕竟这些东西用的多.但是又想看一下关于javaee的东西,这个里面的设计模式多.想了一会儿,觉得这些无非都是工具,j ...

  9. bash基本命令速查表

    来源:https://github.com/skywind3000/awesome-cheatsheets/blob/master/languages/bash.sh ################ ...

  10. UWP开发---DIY星级评分控件

    一,需求来源 在开发韩剧TV UWP过程中,遇到了星级评分的控件问题,在安卓和html中很容易用现有的轮子实现星级评分,搜索了一下目前UWP还未有相关文章,在WPF的一篇文章中使用Photo shop ...