一、Java注解处理器

Java注解处理器,不是关注运行时(Runtime)通过反射机制运行处理的注解,而是在编译时(Compile time)处理的注解。

注解处理器(Annotation Processor)javac的一个工具,它用来在编译时扫描和处理注解(Annotation)


二、API

item

desc

RoundEnvironment接口

注释处理工具框架将提供一个注释处理器和一个实现此接口的对象;以便处理器可以查询关于一轮注释处理的信息

Set < ? extends Element > getElementsAnnotatedWith(TypeElement a)

返回使用给定注释类型注释的元素;

Set< ? extends Element > getElementsAnnotatedWith(Class<? extends Annotation> a)

返回使用给定注释类型注释的元素。

Element(接口)

javax.lang.model.element包下面的接口;Represents a program element such as a package, class, or method

Name getSimpleName()

返回此元素的简单名称。

< A extends Annotation > A getAnnotation(Class< A > annotationType)

返回此元素针对指定类型的注解(如果存在这样的注解),否则返回 null。

ElementKind getKind()

返回此元素的类型。

getEnclosedElements

返回此元素直接封装的元素。 类或接口被认为用于封装它直接声明的字段、方法、构造方法和成员类型

Name(接口)

字符的不可变序列;

ElementKind

枚举类,声明了各种程序类型


三、案例

参看《疯狂Java讲义》中的案例去讲解;《Java编程思想》中的注解器讲解是基于JDK1.5的。而注解器相关API在JDK1.6才正式出现。

3.1 案例讲解

  • 自定义三个注解(针对表、主键、其他字段)
  • 分别应用在一个类(Person)的类名和类的属性字段
  • 编写一个注解处理器
  • 使用javac等命令在编译期间解析注解并生成类似与hibernate的xml文件。

3.2 案例

3.2.1 源码

源码位置;不推荐下载

import java.lang.annotation.*;

/**
 *  在编译期间提取注解,所以保留策略,RetentionPolicy.SOURCE 即可
 */
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface PersistentTableName
{
    String tableName();
}
import java.lang.annotation.*;
/**
 * id字段;区别于其他属性。
 */
@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.SOURCE)
public @interface IdProperty
{
    String column();
    String type();
    String generator();
}
import java.lang.annotation.*;

@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.SOURCE)
public @interface Property
{
    String column();
    String type();
}
@PersistentTableName(tableName = "persons_table")
public class Person {
    @IdProperty(column = "person_id", type = "integer", generator = "identity")
    private int id;
    @Property(column = "person_name", type = "string")
    private String name;
    @Property(column = "person_age", type = "integer")
    private int age;


    Person() {

    }

    public Person(int id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getId() {
        return this.id;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return this.name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public int getAge() {
        return this.age;
    }

}

重点理解注解处理器

import javax.annotation.processing.AbstractProcessor;
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.Name;
import javax.lang.model.element.TypeElement;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
mport java.util.Set;

/**
 * AbstractProcessor @since 1.6
 * 在这个之前需要引入 com.sun.mirror.* 相关API
 */
@SupportedSourceVersion(SourceVersion.RELEASE_8)
@SupportedAnnotationTypes({"PersistentTableName","IdProperty","Property"})
public class HibernateAnnotationProcessor extends AbstractProcessor {

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {

        //遍历所有被@PersistentTableName 修饰的类
        for(Element element : roundEnv.getElementsAnnotatedWith(PersistentTableName.class)) {

                Name clazzName = element.getSimpleName();
                PersistentTableName pt = element.getAnnotation(PersistentTableName.class);

                try(PrintStream ps = new PrintStream(new FileOutputStream(clazzName + ".hbm.xml"))){

                    //执行输出
                    ps.println("<?xml version=\"1.0\"?>");
                    ps.println("<!DOCTYPE hibernate-mapping");
                    ps.println("    PUBLIC \"-//Hibernate/Hibernate Mapping DTD 3.0//EN\"");
                    ps.println("    \"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd\">");
                    ps.println("<hibernate-mapping>");
                    ps.print("  <class name=\"" + element);
                    //输出per的table()的值
                    ps.println("\" table=\"" + pt.tableName() + "\">");
                    for (Element  f : element.getEnclosedElements())
                    {
                        //获取指定FieldDeclaration前面的IdProperty Annotation
                        IdProperty id = f.getAnnotation(IdProperty.class);
                        //如果id Annotation不为空
                        if (id != null)
                        {
                            //执行输出
                            ps.println("        <id name=\""
                                    + f.getSimpleName()
                                    + "\" column=\"" + id.column()
                                    + "\" type=\"" + id.type()
                                    + "\">");
                            ps.println("            <generator class=\""
                                    + id.generator() + "\"/>");
                            ps.println("        </id>");
                        }
                        //获取指定FieldDeclaration前面的Property Annotation
                        Property p = f.getAnnotation(Property.class);
                        //如果p Annotation不为空
                        if (p != null)
                        {
                            //执行输出
                            ps.println("        <property name=\""
                                    + f.getSimpleName()
                                    + "\" column=\"" + p.column()
                                    + "\" type=\"" + p.type()
                                    + "\"/>");
                        }
                    }
                    ps.println("    </class>");
                    ps.println("</hibernate-mapping>");

                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                }
        }
        return true;
    }
}

3.2.2 编译文件

:把所有源码都放在同一个文件下,且没有使用package包去管理文件。

(一) 指定javac的编码(有中文注释:采用utf-8

android 运行注解处理器 java注解处理器_android 运行注解处理器

javac -encoding utf-8 文件名.java

例如:

javac -encoding utf-8 HibernateAnnotationProcessor.java

(二) 先编译没有编译的文件;被依赖的文件先编译。依次编译文件。

(三) 指定注解处理器去生成文件。

javac -processor HibernateAnnotationProcessor Person.java

(四)生成结果

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping
    PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="Person" table="persons_table">
        <id name="id" column="person_id" type="integer">
            <generator class="identity"/>
        </id>
        <property name="name" column="person_name" type="string"/>
        <property name="age" column="person_age" type="integer"/>
    </class>
</hibernate-mapping>