• JAVA基础知识之Annotation


    基本Annotation

    Annotation必须使用工具(APT, Annotation tool)才能处理,Annotation可以在编译,类加载,运行时被读取,并执行相应处理。

    下面介绍一些常用Annotation.

    @Override

    强制一个子类必须覆盖父类的方法,这样如果在子类中将需要覆盖的方法名写错了,在编译阶段就可以被发现

    @Deprecated

    标记某个类,方法等已经过时,编译时会有警告

    @SuppressWarnings

    将会取消编译器的警告。将对程序元素及下面所有子元素起作用。

    用法举例,

     1 package Annotation;
     2 
     3 import java.util.ArrayList;
     4 import java.util.List;
     5 
     6 
     7 public class SuppressWarningsTest {
     8     @SuppressWarnings("unchecked")
     9     public static void main(String[] args) {
    10         @SuppressWarnings("rawtypes")
    11         List<String> list = new ArrayList();
    12         list.add("aa");
    13     }
    14 }

    @SafeVarargs

    Java7 堆污染 警告

    把一个不带泛型的变量赋值给带泛型的变量时,往往会发生 堆污染,例如下面的情况

    1 List list = new ArrayList<Integer>();
    2 //添加元素时引发unchecked异常
    3 list.add(20);
    4 //下面的代码将引起“未经检查的转换”警告,编译、运行时候正常
    5 List<String> ls = list;
    6 //但是访问ls里的元素就会引起运行时异常
    7 System.out.priintln(ls.get(0));

    对于形参个数可变的方法,如果形参又是泛型,就更容易引起 堆污染,例如工具类

    1 public class ErrorUtils
    2 {
    3         public static void faultyMethod(List<String>...listStrArray)
    4         {
    5                 ...
    6                 List[] listArray = listStrArray;
    7                 ...    
    8         }
    9 }

    JAVA6以前,编译器在编译faultyMethod()方法时并不会发出警告,但是在使用这个方法的时候却发出警告:ClassCastException,

    在JAVA7开始,编译器在编译ErrorUtils的时候就发出警告,可见JAVA7更严谨。

    但是在某些情况下,开发者并不希望看到这种警告,于是可以用三种方法屏蔽,

    @SafeVarargs , JAVA7推荐的方式,专门用来屏蔽 堆污染 警告

    @SuppressWarnings("unchecked"),取消所有警告

    @编译时使用-Xlint:varargs选项,不常用。

    @FunctionalInterface

    函数式接口限定,

    JAVA 8中规定,如果接口中只有一个抽象方法(可以用多个其他方法),该接口就是函数式接口。

    而@FunctionalInterface就是做这种编译检查的,如果在一个抽象接口中有两个以上抽象方法,就不再是函数式接口,编译器就会报错。

    JDK的元Annotation

    除了java.lang下的5个基本的Annotation之外,JDK还在java.lang.annotation下提供了6个Meta Annotation (元 Annotation). @Repeatable用来专门定义java8的重复注解。 其他的几个Annotation(@Retention, @Target, @Documented, @Inherited)则用来修饰其他的Annotation定义。

    @Retention

    用来修饰别的Annotation, 指定被修饰的Annotation的作用范围候(java文件中,还是class文件中,还是运行时)。

    @Retention的使用格式为  @Retention(value=xxxx),也可以简化为@Retention(xxxx),当注解的成员变量只有"value"时就可以简写。

    value可以有以下三中取值

    • RetentionPolicy.SOURCE: 仅在源码中可见,被修饰的Annotation只保留在java源码中,编译时直接丢弃。
    • RetentionPolicy.CLASS:保留在CLASS文件中,java程序运行时候不会读取。  这是默认值。
    • RetentionPolicy.RUNTIME,在运行时可见,也是记录在CLASS文件中,但是在运行的时候JVM还会从被修饰的Annotation中提取信息,程序也可以通过反射获取Annotation中的信息。

    用法举例,

    1 //下面被@Retention修饰的注解Testable将会保留到运行时
    3 @Retention(value=RetentionPolicy.RUNTIME)
    5 //上面一行也可以简写为
    7 //@Retention(RetentionPolicy.RUNTIME)
    8 public @interface Testable{}

    @Target

    也是用来修饰别的Annotation. 用于指定被修饰的Annotation只会对哪些程序元素(构造器,成员变量,成员方法等)起作用。value的取值如下,

    • ElementType.ANNOTATION_TYPE:指定该策略的annotation只能用来修饰Annotation
    • ElementType.CONSTRUCTOR:只能修饰构造器
    • //以下value取值可顾名思义
    • ElementType.FIELD
    • ElementType.LOCAL_VARIABLE
    • ElementType.METHOD
    • ElementType.PACKAGE
    • ElementType.PARAMETER
    • ElementType.TYPE:可修饰类,接口(包括注解类型)或枚举定义。

    用法举例

    1 @Target(ElementType.FIELD)
    2 //只能修饰成员变量
    3 public @interface ActionListenerFor{}

     @Documented

    用来修饰别的annotation. 被修饰的annotation也会被javadoc提取

    例如下面的用法,

    1 @Retention(RetentionPolicy.RUNTIME)
    2 @Target(ElementType.METHOD)
    3 //定义Testable Annotation会被javadoc工具提取
    4 @Documented
    5 public @interface Testable {}


    在具体类中使用

    1 public class MyTest
    2 {
    3     //@Testable也会被javadoc提取
    4     @Testable
    5     public void info()
    6     {
    7         ...
    8     }
    9 }

    @Inherited

    用来修饰其他annotion, 被修饰的annotation具有继承性。例如下面。

    定义可继承的annotation: Inheritable

    1 @Target(ElementType.TYPE)
    2 @Retention(RetentionPolicy.RUNTIME)
    3 @Inherited
    4 public @interface Inheritable {}

    在基类上使用Inheritable,

    1 //使用@Inheritable修饰的Base类
    2 @Inheritable
    3 class Base {}

    Base的子类中,即使没有显示使用Inheritable Annotation, 但会从父类继承,

    1 //使用@Inheritable修饰的Base类
    2 @Inheritable
    3 class Base {}
    4 public class InheritableTest extends Base {}

    自定义的Annotation

    除了java.lang下的4个基本annotation,我们也可以自定义annotation.

     自定义annotation

    根据annotation是否可以包含成员变量,可以把annotation分为如下两类,即

    • 标记Annotation:无成员变量,只利用自身是否存在来提供信息,例如@Override
    • 元数据Annotation:包含成员变量,接收更多元数据。

    定义一个annotation的方式与定义接口非常相似(继承自Annotation接口),以@inteface开头,可以有成员变量(及默认值),比如下面这样,

    1 //自定义一个annotatio,包含两个成员变量,而且有默认值
    2 public @interface MyTag
    3 {
    4   String name() default "xiao ming"
    5   int age() default 2
    6 }

    下面是使用我们自定义的annotation

    1 public class Test
    2 {
    3     @MyTag(name="xx", age=6)
    4     public void info()
    5     {
    6         ...
    7     }
    8     ...
    9 }

     提取Annotation信息

    Annotation不会自己生效,而是需要写专门的程序来提取这些信息。从Java5开始,java.lang.reflect提供的反射API增加了运行时读取Annotation的能力,

    当然只有将Annotation定义成@Retention(RetentionPolicy.RUNTIME)修饰,该Annotation才会在运行时可见。

    Java5在java.lang.reflect下增加了AnnotatedElement接口,代表程序中可以接受注解的程序元素,主要实现类有Class, Constructor, Field, Method, Package等。AnnotatedElement是所有程序元素(Class, Constructor, Field, Method等)的父接口,所以当程序通过反射获取了某个类的AnnotatedElement对象(如Class, Constructor, Field, Method等)之后,就可以用下面方法访问Annotation的信息。

    <A extends Annotation> A getAnnotation(Class<A> annotationClass) , 返回程序元素上指定类型的annotation

    <A extends Annotation> A getDeclaredAnnotation(Class<A> annotationClass) , JDK8新增。 获取直接修饰程序元素的指定类型的annotation

    Annotation[] getAnnotations(): 返回程序元素上的所有注解

    Annotation[] getDeclaredAnnotations(); 返回直接修饰该程序元素的所有Annotation

    boolean isAnnotationPresent(Class<? extends Annotation) annotationClass. 元素上是否存在指定类型的annotation

    <A extends Annotation> A[] getAnnotationByType(Class<A> annotationClass , JDK8中允许重复注解,获取修饰该程序元素指定类型的多个annotation

    <A extends Annotation> A[] getDeclaredAnnotationByType(Class<A> annotationClass , JDK8中允许重复注解,获取直接修饰该程序元素指定类型的多个annotation

     下面举例,

     1 //获取Test类的info方法的所有注解
     2 Annotation[] aArray = Class.forName("Test").getMethod("info").getAnnotations();
     3 //遍历
     4 for (Annotation an :  aArray)
     5 {
     6     System.out.println(an);
     7 
     8     //如果要获取某个注解里的元数据,则可以将注解强制类型转换成所需注解类型,
     9     //然后通过注解对象的抽象方法来访问这些元数据
    10     // 如果tag注解是MyTag1类型
    11     if (tag instanceof MyTag1) {
    12         System.out.println("Tag is : "+tag);
    13         //强制转换
    14         System.out.println("tag.name(): "+((MyTag1)tag).method1());
    15         System.out.println("tag.name(): "+((MyTag1)tag).method2());
    16     }
    17     // 如果tag注解是MyTag2类型
    18     if (tag instanceof MyTag1) {
    19         System.out.println("Tag is : "+tag);
    20         //强制转换
    21         System.out.println("tag.name(): "+((MyTag2)tag).method1());
    22         System.out.println("tag.name(): "+((MyTag2)tag).method2());
    23     }
    24 }

    下面这个例子,模拟JUnit的@Test注解,即只有在方法上加了@Test注解之后,JUnit才会执行这个方法。

    首先自定一个Testable注解, 要@Retention来限定注解在程序运行时也有效,

     1 package ann;
     2 
     3 import java.lang.annotation.ElementType;
     4 import java.lang.annotation.Retention;
     5 import java.lang.annotation.RetentionPolicy;
     6 import java.lang.annotation.Target;
     7 
     8 //自定义一个注解Testable,使用JDK的两个元注解来修饰他它。Retention设置注解保留时间到运行时,Target设置注解只对方法起作用
     9 @Retention(value=RetentionPolicy.RUNTIME)
    10 @Target(ElementType.METHOD)
    11 public @interface Testable{
    12     
    13 }

    写一个测试目标类MyTest,里面有8个方法,但是只有5个加了@Testable注解,

     1 package ann;
     2 
     3 public class MyTest {
     4     //使用@Testable注释的方法指定该方法是可测试的
     5     @Testable
     6     public static void m1() {
     7         
     8     }
     9     
    10     public static void m2() {
    11         
    12     }
    13     
    14     @Testable
    15     public static void m3() {
    16         throw new IllegalArgumentException("参数错误!");
    17     }
    18     
    19     @Testable
    20     public static void m4() {
    21         
    22     }
    23     
    24     @Testable
    25     public static void m5() {
    26         
    27     }
    28     
    29     public static void m6() {
    30         
    31     }
    32     
    33     @Testable
    34     public static void m7() {
    35         
    36     }
    37     
    38     public static void m8() {
    39         
    40     }
    41 }

    如前面所说,注解并不会自动起作用,而是需要我们写额外的代码,通过反射去获取注解信息,并执行相应操作,

    下面写一个专门的注解处理类,用来读取MyTest类中的Testable注解,从而控制MyTest类中哪些方法可以执行,

     1 package ann;
     2 
     3 import java.lang.reflect.Method;
     4 
     5 public class ProcessorTest {
     6     public static void process(String clazz) throws SecurityException, ClassNotFoundException {
     7         int passed = 0;
     8         int failed = 0;
     9         //遍历clazz对应类中的所有方法
    10         for (Method m : Class.forName(clazz).getMethods() ) {
    11             // 如果该方法使用了@Testable注解
    12             if (m.isAnnotationPresent(Testable.class)) {
    13                 try {
    14                     //调用m方法
    15                     m.invoke(null);
    16                     //测试成功,passed加1
    17                     passed++;
    18                 } catch (Exception ex) {
    19                     System.out.println("方法 "+m+" 运行失败,异常: "+ex.getCause());
    20                     failed++;
    21                 }
    22             }
    23         }
    24         // 统计测试结果
    25         System.out.println("共运行了: "+(passed + failed)+ " 个方法,其中成功了 "+passed+" 个,失败了  "+failed+" 个。");
    26     }
    27 }

    再写一个测试类RunTests如下,我们执行RunTests类去测试MyTest类,实际是先通过上面的ProcessorTest类通过反射去读取MyTest类中的注解信息,决定哪些方法可以执行,就执行哪些方法,这个过程就跟JUnit一样。

    1 package ann;
    2 
    3 public class RunTests {
    4     public static void main(String[] args) throws SecurityException, ClassNotFoundException {
    5         ProcessorTest.process("ann.MyTest");
    6     }
    7 }

    执行上面的测试类可以看到如下测试结果,

    1 方法 public static void ann.MyTest.m3() 运行失败,异常: java.lang.IllegalArgumentException: 参数错误!
    2 共运行了: 5 个方法,其中成功了 4 个,失败了  1 个。

    上面的例子只是通过反射来简单判断程序中是否有指定注解存在,下面的例子中,将读取注解的元数据信息,并根据信息做出相应操作。

    下面通过注解来为Button绑定监听事件,这能简化编程,

    首先自定义一个注解,将用来修饰Button,简单直观地说明Button将会使用哪个监听器

     1 package ann;
     2 
     3 import java.awt.event.ActionListener;
     4 import java.lang.annotation.ElementType;
     5 import java.lang.annotation.Retention;
     6 import java.lang.annotation.RetentionPolicy;
     7 import java.lang.annotation.Target;
     8 
     9 @Target(ElementType.FIELD)
    10 @Retention(value=RetentionPolicy.RUNTIME)
    11 public @interface ActionListenerFor {
    12     //定义一个成员变量,用来设置元数据
    13     //该listener成员变量用于保存监听器实现类
    14     Class<? extends ActionListener> listener();
    15 }

    上面的自定义注解中,定义了一个成员变量 listener(),对应一个监听器ActionListener类的子类。在下面的Button上添加@ActionListenerFor注解,通过listener成员指定Button监听器的实现类。

     1 package ann;
     2 
     3 import javax.swing.JButton;
     4 import javax.swing.JFrame;
     5 import javax.swing.JPanel;
     6 
     7 public class AnnotationTest {
     8     private JFrame mainWin = new JFrame("使用注解绑定事件监听器");
     9     //使用Annotation为ok绑定事件监听器
    10     @ActionListenerFor(listener=OkListener.class)
    11     private JButton ok = new JButton("确定");
    12     //使用Annotation为cancel绑定事件监听器
    13     @ActionListenerFor(listener=CancelListener.class)
    14     private JButton cancel = new JButton("取消");
    15     public void init() {
    16         //初始化界面
    17         JPanel jp = new JPanel();
    18         jp.add(ok);
    19         jp.add(cancel);
    20         mainWin.add(jp);
    21         ActionListenerInstaller.processAnnotations(this);
    22         mainWin.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    23         mainWin.pack();
    24         mainWin.setVisible(true);
    25     }
    26     public static void main(String[] args) {
    27         new AnnotationTest().init();
    28     }
    29 }

    对应上面两个按钮,各写一个监听实现类,

     1 package ann;
     2 
     3 import java.awt.event.ActionEvent;
     4 import java.awt.event.ActionListener;
     5 
     6 import javax.swing.JOptionPane;
     7 
     8 public class OkListener implements ActionListener {
     9 
    10     @Override
    11     public void actionPerformed(ActionEvent e) {
    12         // TODO Auto-generated method stub
    13         JOptionPane.showMessageDialog(null, "单击了确认按钮");
    14     }
    15 
    16 }
     1 package ann;
     2 
     3 import java.awt.event.ActionEvent;
     4 import java.awt.event.ActionListener;
     5 
     6 import javax.swing.JOptionPane;
     7 
     8 public class CancelListener implements ActionListener{
     9 
    10     @Override
    11     public void actionPerformed(ActionEvent e) {
    12         // TODO Auto-generated method stub
    13         JOptionPane.showMessageDialog(null, "单击了取消按钮");
    14     }
    15     
    16 }

    当然如果只是在上面AnnotationTest的注解中指定了Button的实现类,已然是无法实现Button和对应监听器的绑定的,我们需要专门写一个处理注解的类ActionListenerInstaller去处理,

     1 package ann;
     2 
     3 import java.awt.event.ActionListener;
     4 import java.lang.reflect.Field;
     5 
     6 import javax.swing.AbstractButton;
     7 
     8 public class ActionListenerInstaller {
     9 
    10     public static void processAnnotations(Object obj) {
    11         // TODO Auto-generated method stub
    12         try {
    13             //获取obj对象的类
    14             Class cl = obj.getClass();
    15             //获取指定obj对象的所有成员变量,并遍历每个成员变量
    16             for (Field f : cl.getDeclaredFields()) {
    17                 //将该成员变量设置成可自由访问
    18                 f.setAccessible(true);
    19                 //获取该成员变量上ActionListenerFor类的 Annotaion
    20                 ActionListenerFor a = f.getAnnotation(ActionListenerFor.class);
    21                 //获取成员变量f的值
    22                 Object fObj = f.get(obj);
    23                 //如果f是AbstractButton的实例,且a不为null
    24                 if (a != null && fObj != null && fObj instanceof AbstractButton) {
    25                     //获取a注解里的listener元数据(它是一个监听器)
    26                     Class<? extends ActionListener> listenerClazz = a.listener();
    27                     //使用反射来创建listenr对象
    28                     ActionListener al = listenerClazz.newInstance();
    29                     AbstractButton ab = (AbstractButton)fObj;
    30                     //为ab按钮添加事件监听器
    31                     ab.addActionListener(al);
    32                 }
    33             }
    34         } catch (Exception e) {
    35             e.printStackTrace();
    36         }
    37     }
    38 
    39 }

    上面程序通过Class<? extends ActionListener> listenerClazz = a.listener();就得到了AnnotationTest中通过注解描述的Button的监听实现类,OkListener和CancelListener。然后通过addActionListener()方法将Button和注解中对应的监听实现类进行了绑定。

    这样,我们每次添加Button时,只需要一行注解,就轻松实现了事件监听器的绑定。

    运行AnnotationTest,点击 确定 或者 取消 , 可以见到触发的相应事件得到了响应。程序执行效果如下,

    重复注解

    在JAVA8以前,如果需要在一个程序元素上添加同类型的重复注解,需要像下面这样,

    1 @Results({@Result(name="failure", location="failed.jsp"), @Result(name="success", location="succ.jsp")})
    2 public Action FooAction{...}

    而在JAVA8后,则可以直接添加同类型的多个注解,但是需要先添加@Repeatable注解,

    首先定义一个FKTag注解,

     1 @Retention(RetentionPolicy.RUNTIME)
     2 
     3 @Target(ElementType.TYPE
     4 
     5 public @interface FKTag {
     6 
     7   String name() default "Johnny";
     8 
     9   int age();
    10 
    11 }

    还需要定义一个FKTags注解

    1 @Retention(RetentionPolicy.RUNTIME)
    2 
    3 @Target(ElementType.TYPE
    4 
    5 public @interface FKTags {
    6 
    7   FKTag[] value();
    8 
    9 }

    这样,可在定义@FKTag时,添加下面注解

    1 @Repeatable(FKTags.class)

    最后,就可以在程序元素上重复使用FKTag注解了

    1 @FKTag(age=5)
    2 
    3 @FKTag(name="xiaoming", age=9}

    实际上,JAVA8的这种写法只是以前版本的简写,其内部实现已然是数组形式的。

    JAVA8中的type Annotation

    JAVA8以前,Annotation只能用在程序元素声明语句上,JAVA8开始引入了Type Annotation,可以用在任何程序元素地方(声明,类型转换时,初始化对象时等等, 以实现对程序任何地方进行干涉处理的目的。让程序中编译时进行更严格的检查,增加程序的健壮性。

    JAVA8以前我们通过Target指定Annotation对什么程序元素有效(类,成员变量,成员方法等),在JAVA8开始,新增Target参数ElementType.TYPE_USE使得Annotation可以用在任何地方。

    例如我们先定义一个Type Annotation

    1 @Target(ElementType.TYPE_USE)
    2 @interface NotNull{}

    下面是简单用法,

     1 public class TypeAnnotationTest implements @NotNull Serialiable
     2 {
     3     //方法形参中使用Type Annotation
     4     public static void main(@NotNull String[] args) throws @NotNull FileNotFoundException
     5     {
     6         Object obj = "abcd.txt"
     7         //强制类型转换时使用Type Annotation
     8         String str = (@NotNull String)obj;
     9         // 创建对象时使用Type Annotation
    10         Object win = new @NotNull JFrame("测试");
    11     }
    12     // 泛型中使用Type Annotation
    13     public void foo(List<@NotNULL String> info(){}
    14 }

    可以看到用了Type Annotation之后可以在编译时更方便更严格地进行检查,增强了程序的健壮性。

    编译时处理Annotation

    通过我们自己编写的注解处理类  APT (Annotation Processing Tool), 可以对源码中的Annotation进行检查并处理,还能针对Annotation信息添加进额外的信息,这个机制可以大大简化我们的编程。

    例如在Hibernate中,每一个数据表class文件都会对应一个*.hbm.xml文件,这个xml文件就是通过APT根据java文件中的Annotation信息在编译过程中生成的。

    通过javac -processor,我们可以在编译时指定一个注解处理器,实现编译期间的注解处理。注意,这个注解处理器需要先编译好,才能用做其他类的注解处理器。

    指定的注解处理器,是需要继承AbstractProcessor类,并且实现process方法。

    下面来演示Hibernate的关联的xml自动生成的实现过程,

    首先我们自定义三个普通的注解,分别表示数据库表名,主键字段,属性字段

     1 import java.lang.annotation.Documented;
     2 import java.lang.annotation.ElementType;
     3 import java.lang.annotation.Retention;
     4 import java.lang.annotation.RetentionPolicy;
     5 import java.lang.annotation.Target;
     6 
     7 @Target(ElementType.TYPE)
     8 @Retention(RetentionPolicy.SOURCE)
     9 @Documented
    10 public @interface Persistent {
    11     String table();
    12 }
     1 import java.lang.annotation.Documented;
     2 import java.lang.annotation.ElementType;
     3 import java.lang.annotation.Retention;
     4 import java.lang.annotation.RetentionPolicy;
     5 import java.lang.annotation.Target;
     6 
     7 @Target(ElementType.FIELD)
     8 @Retention(RetentionPolicy.SOURCE)
     9 @Documented
    10 public @interface Id {
    11     String column();
    12     String type();
    13     String generator();
    14 }
     1 import java.lang.annotation.Documented;
     2 import java.lang.annotation.ElementType;
     3 import java.lang.annotation.Retention;
     4 import java.lang.annotation.RetentionPolicy;
     5 import java.lang.annotation.Target;
     6 
     7 @Target(ElementType.FIELD)
     8 @Retention(RetentionPolicy.SOURCE)
     9 @Documented
    10 public @interface Property {
    11     String column();
    12     String type();
    13 }

    我们在具体的JAVA类中使用上面三个注解,假设现在有一张数据库表person_inf, 我们为其建立一个实体类Person

     1 @Persistent(table="person_inf")
     2 public class Person {
     3     @Id(column="person_id", type="integer", generator="identity")
     4     private int id;
     5     @Property(column="person_name", type="string")
     6     private String name;
     7     @Property(column="person_age", type="age")
     8     private int age;
     9     
    10     //无参构造
    11     public Person() {}
    12     
    13     // 有参构造
    14     public Person(int id, String name, int age) {
    15         this.id = id;
    16         this.name = name;
    17         this.age = age;
    18     }
    19 }

    上面的类中使用了三种注解,但是这些注解不会自动生效,需要我们为其专门写一个注解处理类(APT)HibernateAnnotationProcessor,

    在注解处理类中,我们根据注解中对类变量的属性描述,生成一个Person.hbm.xml文件,

     1 import java.io.FileOutputStream;
     2 import java.io.PrintStream;
     3 import java.util.Set;
     4 
     5 import javax.annotation.processing.AbstractProcessor;
     6 import javax.annotation.processing.RoundEnvironment;
     7 import javax.annotation.processing.SupportedAnnotationTypes;
     8 import javax.annotation.processing.SupportedSourceVersion;
     9 import javax.lang.model.SourceVersion;
    10 import javax.lang.model.element.Element;
    11 import javax.lang.model.element.ElementKind;
    12 import javax.lang.model.element.Name;
    13 import javax.lang.model.element.TypeElement;
    14 
    15 @SupportedSourceVersion(SourceVersion.RELEASE_7)
    16 @SupportedAnnotationTypes({"Persistent", "Id", "Property"} )
    17 public class HibernateAnnotationProcessor extends AbstractProcessor {
    18 
    19     @Override
    20     public boolean process(Set<? extends TypeElement> annotations,
    21             RoundEnvironment roundEnv) {
    22         PrintStream ps = null;
    23         try {
    24             for (Element t : roundEnv.getElementsAnnotatedWith(Persistent.class)) {
    25                 //获取类名
    26                 Name clazzName = t.getSimpleName();
    27                 //获取注解
    28                 Persistent per = t.getAnnotation(Persistent.class);
    29                 ps =new PrintStream(new FileOutputStream(clazzName + ".hbm.xml"));
    30                 ps.println("<?xml version="1.0"?>");
    31                 ps.println("<!DOCTYPE hibernate-mapping PUBLIC");
    32                 ps.println("    "-//Hibernate/Hibernate "+"Mapping DTD 3.0//EN"");
    33                 ps.println("    "http://www.hibernate.org/dtd/" + "hibernate-mapping-3.0.dtd">");
    34                 ps.println("<hibernate-mapping>");
    35                 ps.println("    <class name=""+t);
    36                 //输出per的table值
    37                 ps.println("" table="" + per.table() + "">");
    38                 for (Element f : t.getEnclosedElements()) {
    39                     //只处理成员变量上的Annotation
    40                     if (f.getKind() == ElementKind.FIELD) {
    41                         Id id = f.getAnnotation(Id.class);
    42                         if (id != null) {
    43                             ps.println("        <id name="" 
    44                                             + f.getSimpleName()
    45                                             + "" + column="" + id.column()
    46                                             +  "" + type="" + id.type()
    47                                             + "">");
    48                             ps.println("        <generator class=""
    49                                             + id.generator() + ""/>");
    50                             ps.println("        </id>");    
    51                         }
    52                         //获取成员变量前的Property
    53                         Property p = f.getAnnotation(Property.class);
    54                         if (p != null) {
    55                             ps.println("        <property name=""
    56                                             +f.getSimpleName()
    57                                             +"" column="" + p.column()
    58                                             +"" type="" + p.type()
    59                                             +""/>");
    60                         }
    61                     }
    62                 }
    63                 ps.println("    </class>");
    64                 ps.println("</hibernate-mapping>");
    65             }
    66         } catch (Exception ex) {
    67             ex.printStackTrace();
    68         } finally {
    69             if (ps != null) {
    70                 try {
    71                     ps.close();
    72                 }catch (Exception ex) {
    73                     ex.printStackTrace();
    74                 }
    75             }
    76         }
    77         
    78         return true;
    79     }
    80 
    81 }

    执行下面两行编译语句,

    1 javac HibernateAnnotationProcessor.java
    2 javac -processor HibernateAnnotationProcessor Person.java

    之后会发现生成了一个Person.hbm.xml文件如下,

     1 <?xml version="1.0"?>
     2 <!DOCTYPE hibernate-mapping PUBLIC
     3     "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
     4     "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
     5 <hibernate-mapping>
     6     <class name="Person
     7 " table="person_inf">
     8         <id name="id" + column="person_id" + type="integer">
     9         <generator class="identity"/>
    10         </id>
    11         <property name="name" column="person_name" type="string"/>
    12         <property name="age" column="person_age" type="age"/>
    13     </class>
    14 </hibernate-mapping>

    通过以上 javac -processer的方法,就实现了编译期间使用处理器对类的注解进行处理的目的,上面生成的xml文件就可以供Hibernate后面使用。

  • 相关阅读:
    python爬虫开发与项目实践-学习笔记(一)
    python之TypeError
    学习笔记-python
    python学习之Unable to locate element
    Chrome浏览器之 webdriver(Chrome version must be >= 63.0.3239.0)
    POJ 1830 开关问题 高斯消元
    HDU 4135 Co-prime 容斥原理
    HDU 1796 How many integers can you find 容斥原理
    卡特兰数,组合数,斯特林数,逆元模板
    HDU 6134 Killer Names 数学 斯特林数
  • 原文地址:https://www.cnblogs.com/fysola/p/6187606.html
Copyright © 2020-2023  润新知