- 2023-05-10 17:13:41
- 9776 热度
- 0 评论
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>
运行后文件清单:
注:
在运行上图中的run.cmd文件中,如果出现某些类没有编译成.class文件,则需要手功通过javac myClass.java 进行编译,最后得到的Person.hbm.xml 就是我们所要的文件。
- Spring(403)
- Boot(208)
- Spring Boot(187)
- Java(82)
- Cloud(82)
- Spring Cloud(82)
- Security(60)
- Spring Security(54)
- Boot2(51)
- Spring Boot2(51)
- Redis(31)
- SQL(29)
- Mysql(25)
- IDE(24)
- Dalston(24)
- JDBC(22)
- IDEA(22)
- mongoDB(22)
- MVC(22)
- Web(21)
- CLI(20)
- Alibaba(19)
- SpringMVC(19)
- SpringBoot(17)
- Docker(17)
- Git(16)
- Eclipse(16)
- Vue(16)
- ORA(15)
- JPA(15)
- Apache(15)
- Mybatis(14)
- Oracle(14)
- jdk(14)
- Tomcat(14)
- Linux(14)
- HTTP(14)
- XML(13)
- JdbcTemplate(13)
- OAuth(13)
- Nacos(13)
- Pro(13)
- Data(12)
- JSON(12)
- OAuth2(12)
- stream(11)
- int(11)
- Myeclipse(11)
- not(10)
- Bug(10)
- maven(9)
- Map(9)
- Hystrix(9)
- ast(9)
- APP(8)
- Bit(8)
- API(8)
- session(8)
- Window(8)
- Swagger(8)
- Github(7)
- JavaMail(7)
- Cache(7)
- File(7)
- mail(7)
- IntelliJ(7)
- windows(7)
- too(7)
- HTML(7)
- RabbitMQ(6)
- star(6)
- and(6)
- Excel(6)
- Log4J(6)
- pushlet(6)
- apt(6)
- read(6)
- Freemarker(6)
- WebFlux(6)
- JSP(6)
- Bean(6)
- error(6)
- nginx(6)
- Server(6)
- ueditor(6)
- jar(6)
- ehcache(6)
- UDP(6)
- rdquo(5)
- PHP(5)
- Struts(5)
- string(5)
- Syntaxhighlighter(5)
- script(5)
- Tool(5)
- Controller(5)
- swagger2(5)
- ldquo(5)
- input(5)
- Servlet(5)