手机端小强原创文章,java小强个人博客站点
当前位置: 首页 >> 理论 >> JDK工具-apt命令

JDK工具-apt命令

33670 理论 | 2015-7-30

APT(Annotation processing tool) 是一种处理注释的工具,它对源代码文件进行检测找出其中的Annotation,使用Annotation进行额外的处理。

Annotation处理器在处理Annotation时可以根据源文件中的Annotation生成额外的源文件和其它的文件(文件具体内容由Annotation处理器的编写者决定),APT还会编译生成的源文件和原来的源文件,将它们一起生成class文件.使用APT主要的目的是简化开发者的工作量。

因为APT可以编译程序源代码的同时,生成一些附属文件(比如源文件类文件程序发布描述文件等),这些附属文件的内容也都是与源代码相关的,换句话说,使用APT可以代替传统的对代码信息和附属文件的维护工作。

 

如果有过Hibernate开发经验的朋友可能知道每写一个Java文件还必须额外地维护一个Hibernate映射文件(一个名为*.hbm.xml的文件,当然可以有一些工具可以自动生成)下面将使用Annotation来简化这步操作。为了使用系统的apt工具来读取源文件中的Annotation,程序员必须自定义一个Annotation处理器,编写Annotation处理器需要使用JDK-lib目录中的tools.jar 里的如下4个包。
com.sun.mirror.apt: //和APT交互的接口
com.sun.mirror.declaration: //包含各种封装类成员类方法类声明的接口。
com.sun.mirror.type: //包含各种封装源代码中程序元素的接口。
com.sun.mirror.util: //提供了用于处理类型和声明的一些工具。

 

每个Annotation处理器需要实现com.sun.mirror.apt包下的AnnotationProcessor接口,这个接口中定义了一个"process"方法,该方法是由apt调用Annotation处理器时将被用到的。
一个Annotation处理器可以处理一种或多种Annotation类型。
1. 通常情况下Annotation处理器实例是由其相应的工厂返回Annotation处理器工厂应该实现AnnotationProcessorFactory接口APT将调用工厂类的getProcessorFor方法来获得Annotation处理器。
2. 在调用过程中APT将提供给工厂类一个AnnotationProcessorEnvironment对象。
3. AnnotationProcessorEnvironment对象是APT工具与注释环境通信的途径。
使用APT工具来处理源文件时,APT首先检测在源代码文件中包含哪些Annotation,然后APT将查找所需的处理器工厂,并由工厂来返回相应的Annotation处理器。如果该处理器工厂支持这些Annotaion,处理器工厂返回的Annotaion处理器将会处理这些Annotation,如果生成的源文件中再次包含Annotaion,APT将会重复上面过程,直至没有新文件生成。

为了说明使用APT来根据源文件中的注释来生成额外的文件。下面将定义三个Annotation类型,分别用于修饰持久化类,标识属性和普通属性。

修饰表属性

import java.lang.annotation.*;
/**
 * 修饰表属性
 */
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface Persistent {
 String table();
}

修饰标识属性

import java.lang.annotation.*;
/**
 * 修饰标识属性
 */
@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.SOURCE)
public @interface IdProperty {
 String column();
 String type();
 String generator();
}

修饰普通成员变量的Annotation

import java.lang.annotation.*;
/**
 * 修饰普通成员变量的Annotation 
 */
@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.SOURCE)
public @interface Property {
 String column();
 String type();
}

定义了三个Annotation之后,下面我们提供一个简单的Java类文件,这个Java类文件使用了上面三个Annotation来修饰

/**
 * 普通的Java类
 */
@Persistent(table = "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;
 public int getId() {
  return id;
 }
 public void setId(int id) {
  this.id = id;
 }
 public String getName() {
  return name;
 }
 public void setName(String name) {
  this.name = name;
 }
 public int getAge() {
  return age;
 }
 public void setAge(int age) {
  this.age = age;
 }
}

上面Person类是一个非常普通的Java类,但这个普通的Java类使用了@Persistent,@IdProperty,@IdPropery三个Annotation。
下面我们为这三个Annotation提供了一个Annotation处理器该处理器的功能是根据注释来生成一个Hibernate的映射文件。

import com.sun.mirror.apt.*;
import com.sun.mirror.declaration.*;
import com.sun.mirror.type.*;
import com.sun.mirror.util.*;
import java.beans.*;
import java.io.*;
import java.util.*;
import java.lang.reflect.*;
public class HibernateAnnotationProcessor implements AnnotationProcessor {
 // Annotation处理器环境是该处理器与APT交互的重要途径
 private AnnotationProcessorEnvironment env;
 // 构造HibernateAnnotationProcessor对象时获得处理器环境
 public HibernateAnnotationProcessor(AnnotationProcessorEnvironment env) {
  this.env = env;
 }
 // 循环处理每个对象
 public void process() {
  // 遍历每个class文件
  for (TypeDeclaration t : env.getSpecifiedTypeDeclarations()) {
   // 定义一个文件输出流用于生成额外的文件
   FileOutputStream fos = null;
   // 获取正在处理的类名
   String clazzName = t.getSimpleName();
   // 获取类定义前的Persistent Annotation
   Persistent per = t.getAnnotation(Persistent.class);
   // 当per Annotation不为空时才继续处理
   if (per != null) {
    try {
     // 创建文件输出流
     fos = new FileOutputStream(clazzName + ".hbm.xml");
     PrintStream ps = new PrintStream(fos);
     // 执行输出
     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=\"" + t);
     // 输出per的table()的值
     ps.println("\" table=\"" + per.table() + "\">");
     for (FieldDeclaration f : t.getFields()) {
      // 获取指定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 (Exception e) {
     e.printStackTrace();
    } finally { // 关闭输出流
     try {
      if (fos != null) {
       fos.close();
      }
     } catch (IOException ex) {
      ex.printStackTrace();
     }
    }
   }
  }
 }
}

上面的Annotation处理器比较简单与前面通过反射来获取Annotation信息不同的是,这个Annotation处理器使用AnnotationProcessorEnvironment来获取Annotation信息AnnotationProcessorEnvironment包含了一个getSpecifiedTypeDeclarations方法,可获取所有需要处理的类声明,这个类声明可包括类,接口,和枚举等声明,由TypeDeclaration对象表地示与Classc对象的功能大致相似,区别只是TypeDeclaration是静态,只要有类文件就可以获得该对象而Class是动态的必须由虚拟机装载了指定类文件后才会产生。
TypeDeclaration又包含了如下三个常用方法来获得对应的程序元素。
getFields: 获取该类声明里的所有成员变量声明返回值是集合元素FieldDeclaration的集合
getMethods: 获取该类声明里的所有成员声明返回值是集合元素MethodDeclaration的集合
getPackage: 获取该类声明里的包声明返回值是TypeDeclaration

上面三个方法返回的TypeDeclaration,FieldDeclaration,MethodDeclaration都可调用getAnnotation方法来访问修饰它们的Annotation,上面程序中就是获取不同程序元素的Annotation的代码。
提供了上面的Annotation处理器类之后,还应该为该Annotation处理器提供一个处理工厂,处理工厂负责决定该处理器支持哪些Annotation,并通过getProcessorFor方法来生成一个Annotation处理哭对象。

import com.sun.mirror.apt.*;
import com.sun.mirror.declaration.*;
import com.sun.mirror.type.*;
import com.sun.mirror.util.*;
import java.beans.*;
import java.io.*;
import java.util.*;
public class HibernateAnnotationFactory implements AnnotationProcessorFactory {
 // 所有支持的注释类型
 public Collection<String> supportedAnnotationTypes() {
  return Arrays.asList("Property", "IdProperty", "Persistent");
 }
 // 返回所有支持的选项
 public Collection<String> supportedOptions() {
  return Arrays.asList(new String[0]);
 }
 // 返回Annotation处理器
 public AnnotationProcessor getProcessorFor( Set<AnnotationTypeDeclaration> atds, AnnotationProcessorEnvironment env) {
  return new HibernateAnnotationProcessor(env);
 }
}

提供了上面的处理器工厂后就可以使用APT工具来处理上面的Person.java源文件。并根据该源文件来生成一个XML文件。 APT工具位于JDK的安装路径的bin路径下。
运行APT命令时,可以使用-factory选项来指定处理器工厂类。
如下所示 run.cmd,使用前注意配置Java环境变量:

apt -factory HibernateAnnotationFactory Person.java

使用APT工具HibernateAnnotationFactory工厂来处理Person.java后将可以看到在相同路径下,生成了一个Person.hbm.xml文件了。该文件内容如下:

<?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>

运行后文件清单:

JDK工具-apt命令

注:

在运行上图中的run.cmd文件中,如果出现某些类没有编译成.class文件,则需要手功通过javac myClass.java 进行编译,最后得到的Person.hbm.xml 就是我们所要的文件。

推荐您阅读更多有关于“ jdk APT Annotation ”的文章

上一篇:JDK工具-native2ascii命令 下一篇:JDK工具-appletviewer命令

猜你喜欢

发表评论:

个人资料
blogger

java小强
没有思考,人生的路会越走越难!

搜索
分类
最新微语
  • 今天同学问我,最近还在写代码吗?我想了想,这个问题怎么回答呢,我好像确实很长时间,虽然写了一些,但是主要内容已经不是写代码了。然后再想想,自己也7年多了,这么多年了,我收获了什么,我的目标到底是什么。眼看就奔三了,人生啊,开启感叹模式。

    2017-03-30 22:52

  • 也许大家都已经注意到了,今年的房价,好多地方都是翻了一番,跟着就是,各地房东开始变相涨租。今年之所以搬走,就是为此,这两天同学也是如此。很多房东只认钱,别谈感情,伤钱。而对于这个城市来说,你怎么定位自己,你真把自己当成她的一份子?你来此为何?将来何去何从?自己掂量清楚。

    2016-12-05 10:03

  • 为什么一直不写了呢?因为当爸爸了,没空了。今年的冬天,有些寒冷,除了这寒冬带来的不适,更有因乐视公司遇到危机,而带来的同事别离。送别同事,看着空旷的工位,心中有些悲凉。临近年关,此时此刻,该怎么做,似乎不再是脑子一热那么简单了。

    2016-11-24 11:28

  • 已经请假,加上国庆,要很长一段时间不在北京了。919加班,搞的现在有点心累,胸闷,身体不适。看来,我要好好休息一下了。这几天有些冷,2016的冬天,一步步来了,各位亲友,记得添衣加粗啊。

    2016-09-23 17:29

  • 现在是真的有秋天的感觉了,晚上也不热了,白天也凉快了。再来点风,那酸爽,就有一种想出去防风的冲动了。不过因为最近广州的事情压着,我也没办法,搞的特别累,以前是天天加班不想有自由,现在是因为有事情了,被限制了自由。加上公司的一些事情,感觉日子特别的无聊。

    2016-08-31 17:39

  • 更多»

最新文章
热门文章
随机文章