• Java凝视Annotation


    Java凝视Annotation

    JDK 5開始,Java添加了对元数据(MetaData)的支持,也就是Annotation(凝视)。Annotation提供了一种为程序元素设置元数据的方法。程序元素包含修饰包、类、构造器、方法、成员变量、參数、局部变量。从某些方面来看,Annotation就想修饰符一样,可用于程序元素的声明。这些信息被存储在Annotation的”name = value”对中。

    须要注意的是, Annotation是一个接口,程序能够通过反射来获取指定程序元素的Annotation对象,然后通过Annotation对象来取得凝视里的元数据,且不会影响程序代码的运行。不管添加、删除Annotation,代码都始终如一地运行。假设希望让程序中的Annotation在运行时起一定的作用,仅仅有通过某种配套的工具对Annotation中的信息进行訪问和处理,訪问和处理Annotation的工具统称为APT(Annotation ProcessingTool)。

     

    一、Annotation基础

    前面已经讲到APT负责提取Annotation里包括的元数据,那么我们先了解一下4个基本Annotation的使用,须要在Annotation前面加上@符号。以修饰符的形式放在程序元素的前面。

    4个主要的Annotation例如以下:

    • @Override

    • @Deprecated

    • @SuppressWarnings

    • @SafeVarargs

    1、@Override——限定重写父类方法

    顾名思义,@Override就是用来重写方法的,它仅仅能作用于方法。不能作用于其它程序元素。它能够强制一个子类必须覆盖父类的方法。通常情况下会被省略。比如以下的程序指定了子类Circle的draw()方法必须重写父类方法:

    class Shape{

       void draw(){

          System.out.println("画一个形状");

       }

    }

    class Circle extends Shape{

       //将@Override省略效果同样

       @Override

       void draw() {

          System.out.println("画一个圆形");

       }

    }

    可是@Override作为一个标识。能够告诉我们该方法在其父类中是不是存在,或者我们须要重写的方法是否跟父类中的方法一致。比如上面的程序,假设我把draw()写成darw()。系统将会报编译错误。

     

    2、@Deprecated——标示已过时

    用于表示某个程序元素(类、方法等)已过时。当其它程序使用已过时的类、方法时,编译器将会给出警告。比如:

    public classTest{

       public static void main(String[] args) {

          //以下调用shape的draw方法时。编译器会给出警告

          new Shape().draw();

       }

    }

     

    class Shape{

       @Deprecated

       void draw(){

          System.out.println("画一个形状");

       }

    }

     

    3、@supressWarnings——抑制编译器警告

    指示被该Annotation修饰的程序元素(以及该程序元素中的全部子元素)取消显示指定的编译器警告。@SupressWarnings会抑制作用于该程序元素的全部子元素,比如。使用@SupressWarnings修饰某个类取消显示某个编译器警告,同一时候又修饰该类里的某个方法取消显示还有一个编译器警告,那么该方法将会同一时候取消显示这两个编译器警告。

    在通常情况下。假设程序中使用没有泛型限制的集合。将会引起编译器的警告。为了避免编译器的警告。能够使用@SupressWarnings修饰。比如:

    //关闭整个类里的编译器警告

    @SuppressWarnings("unchecked")

    public class Test{

       public static void main(String[]args) {

          List<String> tempList = new ArrayList();

       }

    }

     

    4、Java7的“堆污染”警告与@SafeVarargs

    要理解堆污染,首先看一个样例:

         List list = new ArrayList<Integer>();

          list.add(10);//加入元素时引发unchecked异常

          //以下代码引起“未经检查的转换”的警告。但编译、执行时全然正常

          List<String> temp = list;

          //但仅仅要訪问temp里的元素,就会引起执行时异常

          System.out.println(temp.get(0));

    如上所看到的,将list赋值给temp就造成了所谓的“堆污染”当把一个不带泛型的对象赋值给一个带泛型的变量时。往往就会引发“堆污染”。

    对于形參个数可变的方法,假设该形參的类型是泛型,就更easy引发“堆污染”了,比如以下的方法中,相当于把List<String>赋值给了List。

    该方法首先将listArray与listStrArray指向同一内存地址。然后将整型集合赋值给listArray的第一个元素,再使用listStrArray取出第一个元素时。将引发ClassCastException异常:

      public voidtemp(List<String>... listStrArray){

          List[] listArray = listStrArray;

          List<Integer> tempList = Arrays.asList(99);

          listArray[0] = tempList;

          String s = listStrArray[0].get(0);

    }

    而我们这里要讲的@SafeVarargs,就是Java7专门用来一直“堆污染”警告而提供的Annotation。除此之外。还有一种方法就是我们上面所将的。使用@SuppressWarning(“unchecked”)修饰。

     

    二、JDK的元Annotation

    你可能会觉得这4个Annotation使用起来不太方便,觉得Annotation仅仅有这么几个功能。事实上,这仅仅是Annotation的凤毛麟角,假设我们想深入了解Annotation,那就得自己定义Annotation。

    在讲自己定义Annotation之前,首先要说的是元Annotation。

    由于Annotation并非程序元素,甚至对其进行增删改也不会影响程序的正常执行。那么假设我们想让Annotation在程序执行时也起作用。该怎么办呢?我们要讲的元Annotation就是来干这个的,它用来修饰其它的Annotation定义,使Annotation具有不同的作用域。

    1、使用@Retention

    我们在创建自己定义Annotation时。能够使用@Retention来修饰这个Annotation,用于指定被修饰的Annotation的作用域。@Retention包括一个RetentionPolicy类型的value成员变量,其值能够使例如以下3个:

    • RetentionPolicy.Class:默认值,编译器将吧Annotation存储于class文件里,但执行Java程序时。JVM不会保留Annotation,即仅仅存储但不參与程序执行;

    • RetentionPolicy.RUNTIME:编译器将吧Annotation存储于class文件里,执行Java程序时,JVM也会保留Annotation,即存储且參与程序执行。

    • RetentionPolicy.SOURCE:编译器不会存储Annotation到class文件里,执行Java程序时JVM也不会保留Annotation。即不存储不保留。

    非常多时候我们须要通过反射获取凝视信息,所以就须要使用value属性值为RetentionPolicy.RUNTIME的@Retention。比如以下我们定义了一个名为Param的Annotation。而且能够通过反射来获取它的凝视信息:

    @Retention(RetentionPolicy.RUNTIME)

    public @interface Param {

       long id();

       String name();

       int sex() default 1;

    }

     

    2、使用@Target

    @Target也能够用来修饰Annotation定义,它用于指定被修饰的Annotation能用于修饰哪些程序单元,其value值有例如以下几个:

    • ElementType.ANNOTATION_TYPE:指定该策略的Annotation仅仅能修饰Annotation;

    • ElementType.CONSTRUCTOR:仅仅能修饰构造器;

    • ElementType.FIELD:仅仅能修饰成员变量;

    • ElementType.LOCAL_VARIABLE:仅仅能修饰局部变量;

    • ElementType.METHOD:仅仅能修饰方法定义;

    • ElementType.PACKAGE:仅仅能修饰包定义;

    • ElementType.PARAMETER:能够修饰參数。

    • ElementType.TYPE:能够修饰类、接口(包含凝视类型)或枚举定义。

    以下的样例中。定义Param仅仅能修饰方法:

    @Target(ElementType.METHOD)

    public @interface Param {

       long id();

       String name();

       int sex() default 1;

    }

    3、使用@Document

    @Document用于指定被它修饰的Annotation类将被javadoc工具提取成文档。假设定义Annotation类时使用了@Document修饰,则全部使用该Annotation修饰的程序元素的API文档中将会包括该Annotation说明。举个样例来说明:

    @Retention(RetentionPolicy.RUNTIME)

    @Target(ElementType.METHOD)

    @Documented

    public @interface Param {

       long id();

       String name();

       int sex() default 1;

    }

    public class Dulven {

       public static void main(String[]args) { }

       @Param(id=1001001,name="GISirFive")

       public void toPerson() { }

    }

    javadoc提取API,打开API后是这种:

     

    假设在定义Param时不加@Document,就不会显示@Param()

    4、使用@Inherited

    @Inherited用于指定被其修饰的Annotation定义具有继承性,被该Annotation修饰的类假设有子类,则子类将会自己主动被该Annotation修饰。

    举个样例:

    @Inherited

    public @interface Param {

       long id();

       String name();

       int sex() default 1;

    }

    @Param(id=110, name="singer")

    class Base{  }

     

    public class Dulven extends Base{

       public static void main(String[]args) {

       System.out.println(Dulven.class.isAnnotationPresent(Param.class));

       }

    }

    上面程序执行会输出true。

                                                  

     

    三、自己定义Annotation

    除了这4个主要的Annotation。细心的程序猿可能会发现还有非常多未知的Annotation。事实上除了这4个主要的Annotation外。大多数都是我们自己定义的,我们能够通过自己定义Annotation来使代码更通俗易懂。

    1、定义Annotation

    定义一个新的Annotation与定义一个接口类似,须要使用@interfacekeyword。比如以下定义了一个名为Param的Annotation,并在Test类中使用它:

    public @interface Param {    }

     

    @Param

    public class Test {

       public static void main(String[]args) {     }

    }

    在默认情况下,Annotation可用于修饰不论什么程序元素。包含类、接口、方法等。

    如普通方法一样,Annotation还能够带成员变量,Annotation的成员变量在Annotation定义中以无形參的方法形式来声明,其方法名和返回值定义了该成员变量的名字和类型,比如:

    public @interface Param {

       long id();

       String name();

       int sex() default 1;

    }

    一旦在Annotation里定义了成员变量。使用时就须要为其指定值,当然我们也能够为成员变量指定默认值,默认值制定方法如同上面的sex。其默认值为1。这样我们在使用时就不须要为其指定值了。比如:

    @Param(id=1001, name="旺旺")

    public class Animal {

       public static void main(String[]args) { }

    }

    我们能够将Annotation按是否包括成员变量分为两类:

    • 标记Annotation:指未定义成员变量的Annotation。

      这样的Annotation仅利用自身的存在与否来为我们提供信息,比如@Override、@Deprecated等。

    • 元数据Annotation:指包括成员变量的Annotation,由于它们能够接受很多其它的元数据。

     

    2、提取Annotation信息

    当开发人员使用Annotation修饰了类、方法、Field等成员之后,正如Annotation的定义所言,Annotaion不能影响程序代码的运行。不管添加、删除Annotation,代码都始终如一的运行。仅仅有通过apt工具对Annotation中的信息进行訪问和处理。我们才干让程序中的Annotation在程序运行时起一定的作用。所以这些Annotation不会自己生效,必须由开发人员提供对应的工具来提取并处理Annotation信息。

    Java使用Annotation接口来代表程序元素前面的凝视,该接口是全部Annotation类型的付接口。Java5在java.lang.reflect(主要包括一些实现反射功能的工具类)包下新增了AnnotatedElement接口,该接口代表程序中能够接受凝视的程序元素。该接口主要有例如以下几个实现类:

    Class:类定义

    Constructor:构造器定义

    Field:类的成员变量定义

    Method:类的方法定义

    Package:类的包定义

    Java5開始。该包所提供的反射API扩充了读取执行时Annotation的能力。

    当一个Annotation类型被定义为执行时Annotation(怎样定义执行时,一会第三章会解说)后,该Annotation才会在执行时可见,JVM才会在状态*.class文件时读取保存在class文件里的Annotation。

    AnnotatedElement接口是全部程序元素(如Class、Method、Constructor等)的父接口。所以程序通过反射获取了某个类的AnnotatedElement对象(如Class、Method、Constructor等)之后。程序就能够调用该对象的例如以下3个方法来訪问Annotation信息。

    • getAnnotation(Class<T>annotationClass):返回该元素上存在的指定类型的凝视,假设该类型的凝视不存在。则返回null;

    • Annotation[]getAnnotations():返回该程序元素上存在的全部凝视;

    • booleanisAnnotationPresent(Class<?

      extends Annotation> annotationClass):推断该程序元素上是否存在指定类型的凝视,若存在则返回true,否则返回false。

    以下的程序用于获取Dulven类中的toPerson方法里的全部凝视,并将其打印出来:

    public class Dulven {

       public static void main(String[]args) throws NoSuchMethodException, SecurityException,ClassNotFoundException {

          Annotation[] annotations = Class.forName("com.Dulven").getMethod("toPerson").getAnnotations();

          for (Annotation annotation : annotations)

              System.out.println(annotation);

       }

       @Param(id=1001001,name="小明")

       public void toPerson() { }

    }

    假设须要获取某个凝视里的元数据。则能够将凝视强制转换成所需的凝视类型,然后通过凝视对象的抽象方法来訪问这些元数据。比如:

    public class Dulven {

       public static void main(String[]args) throws NoSuchMethodException, SecurityException,ClassNotFoundException {

          Dulven dulven = new Dulven();

          Annotation[] annotations =dulven.getClass().getMethod("toPerson").getAnnotations();

          for (Annotation annotation : annotations) {

              if(annotation instanceof Param){

                 System.out.println("凝视内容:" + annotation);

                 Param param = (Param)annotation;

                 System.out.println("id:" + param.id() + ", name:" + param.name() + ", sex:" + param.sex());

              }

          }

       }

       @Param(id=1001001,name="小明")

       public void toPerson() { }

    }

    3、使用Annotation的演示样例

    以下通过两个样例。来帮助大家深入理解Annotation。

    以下创建了一个名为Kangshifu的Annotation,它没有不论什么成员变量,仅仅是用来标记哪些方法是属于康师傅的:

    //使该Annotation在程序执行时起作用

    @Retention(RetentionPolicy.RUNTIME)

    //该Annotation仅仅能修饰方法

    @Target(ElementType.METHOD)

    public @interface Kangshifu {   }

    以下创建了一个BJFactory类,里面有多个方法。@Kangshifu用来标记北京工厂中哪些方法是能够生产康师傅产品的:

    public class BJFactory {

       //用来标记该方法用于生产康师傅产品

       @Kangshifu

       public static void creatType1(){   }

       public static void creatType2(){   }

       @Kangshifu

       public static void creatType3(){   }

       @Kangshifu

       public static void creatType4(){

          throw newNullPointerException("原料不足");

       }

       public static void creatType5(){   }

    }

    我们已经知道。只使用凝视来标记程序元素,对程序是不会有不论什么影响的,即使使用了@Retention修饰的Annotation也不行,这是Java凝视的一条正要原则。

    要让程序中的凝视起作用,接下来必须为这些凝视提供一个处理工具。

    以下的凝视处理工具会分析目标类,假设目标类中的方法使用的@Kangshifu凝视修饰,则通过反射来执行该測试方法:

    //康师傅产品线

    public class KSFProcess {

       public static voidprocessFilter(String clazz) throwsSecurityException,

          ClassNotFoundException{

          int passed = 0;

          int failed = 0;

          //遍历clazz相应的类里的全部方法

          for (Method m : Class.forName(clazz).getMethods()){

              //该方法使用了@Kangshifu修饰

              if(m.isAnnotationPresent(Kangshifu.class)){

                 try {

                    //调用m方法

                    m.invoke(null);

                    passed++;

                 } catch (Exception e) {

                    System.out.println("生产流程" + m + "执行异常:" + e.getCause());

                    failed++;

                 }

              }

          }

          //统计測试结果

          System.out.println("该工厂共检測到生产流程" + (passed + failed) + "个,当中"

                 + failed + "个失败。" + passed + "个正常!");

       }

    }

    KSFProcess类眼下仅仅包括一个processFilter方法,是康师傅公司用来检測各代理生产商的产品线的,该方法将会分析clazz參数所代表的类。并执行该类里使用的@Kangshifu修饰的方法。

      public static void main(String[] args) {

          try {

              KSFProcess.processFilter("com.BJFactory");

          } catch (SecurityException | ClassNotFoundExceptione) {

              // TODO 自己主动生成的 catch 块

              e.printStackTrace();

          }

    }

    运行processFilter方法,将打印例如以下信息:

    生产流程publicstatic void com.BJFactory.creatType4()执行异常:java.lang.NullPointerException:原料不足

    该工厂共检測到生产流程3个,当中1个失败,2个正常!

    上面这个样例仅仅是一个标记Annotation,程序通过推断该Annotation是否存在,来决定是否运行方法。以下的样例中。介绍了怎样使用Annotation来简化事件编程。我们知道普通情况下都是通过某个方法来为事件源绑定事件监听器,以下的样例中,是通过@ActionListener来为事件源绑定监听器:

    @Retention(RetentionPolicy.RUNTIME)

    @Target(ElementType.FIELD)

    public @interface ActionListenerFor {

       //定义一个成员变量,用于设置元数据

       //该listener成员变量用于保存监听器实现类

       Class<?

    extends ActionListener> listener();

    }

    首先定义了一个@ActionListenerFor凝视,我们要使用该凝视指定一个listener成员变量,该成员变量用于指定监听器的实现类。以下程序使用@ActionListenerFor凝视来为两个button绑定事件监听器:

    public class AnnotaionTest {

       private JFrame frame = new JFrame("使用凝视绑定事件监听器");

       //使用Annotation为okbutton绑定监听器

       @ActionListenerFor(listener = OKListener.class)

       private JButton btnOK = new JButton("确定");

       @ActionListenerFor(listener = CancelListener.class)

       private JButton btnCancel = new JButton("取消");

      

       public void init(){

          JPanel panel = new JPanel();

          panel.add(btnOK);

          panel.add(btnCancel);

          frame.add(panel);

          frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

          frame.pack();

          frame.setVisible(true);

       }

       public static void main(String[]args) {

          new AnnotaionTest().init();

       }

      

    }

     

    class OKListener implements ActionListener{

     

       @Override

       public void actionPerformed(ActionEvent e) {

          JOptionPane.showMessageDialog(null"单击了确认button");;

       }

      

    }

     

    class CancelListener implements ActionListener{

     

       @Override

       public void actionPerformed(ActionEvent e) {

          JOptionPane.showMessageDialog(null"单击了取消button");

       }

      

    }

    上面程序定义了两个Jbuttonbutton。并使用@ActionListenerFor凝视为其绑定了事件监听器。当中listener用于指定每一个button的监听器实现类。

    看到这里。相信新手们都会疑惑不解。由于我们不知道监听器类和凝视、凝视和button是怎么联系起来的。即监听器是怎样作用到button上的呢?正如之前所说,假设只在程序中使用凝视是不会起到不论什么作用的,我们须要通过凝视处理工具来处理程序中的凝视,使其与其修饰的元素联系起来。

    所以,我们须要定义一个凝视处理类,用来分析目标对象中的全部Field,假设该Field前使用了@ActionListenerFor修饰,则取出该Annotation中的listener元数据,并依据该数据来绑定事件监听器:

    public class ActionListenerInstaller {

       //处理Annotation的方法,当中object是包括Annotation的对象

       public static voidprocessAnnotations(Object object){

          try {

              //获取Object相应的类

              Class clazz =object.getClass();

              //遍历该类下全部已声明的Field

              for (Field field : clazz.getDeclaredFields()) {

                 //将该Feild设置成可自由訪问

                 field.setAccessible(true);

                 //推断该Field是否被@ActionListenerFor修饰

                 boolean b = field.isAnnotationPresent(ActionListenerFor.class);

                 if(!b) continue;

                 ActionListenerFor listenerFor = field.getAnnotation(ActionListenerFor.class);

                 //获取Field实际相应的对象

                 Object fObject = field.get(object);

                 //JButton继承于AbstractButton,假设fObject存在且继承于AbstractButton

                 if(fObject != null && fObject instanceof AbstractButton){

                    //获取凝视里的元数据listener

                    Class<?

    extends ActionListener> tempListener =listenerFor.listener();

                    //使用反射来创建listener类的对象

                    ActionListener actionListener =tempListener.newInstance();

                    AbstractButton button =(AbstractButton)fObject;

                    button.addActionListener(actionListener);

                 }

              }

          } catch (Exception e) {

              e.printStackTrace();

          }

       }

    }

    最后,我们须要将处理类在ActionTest类的init()方法中运行:

      public void init(){

          JPanel panel = new JPanel();

          panel.add(btnOK);

          panel.add(btnCancel);

          frame.add(panel);

          frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

          frame.pack();

          frame.setVisible(true);

          ActionListenerInstaller.processAnnotations(this);

    }

    执行上面的演示样例程序,点击确定,会弹出消息对话框。

     

    四、编译时处理Annotation

    APT(Annotation Processing Tool)是一种处理凝视的工具,它对源码文件进行检測,找出当中的Annotation后,对Annotation进行额外的处理。

    使用APT的主要目的,是简化开发人员的工作量。由于APT能够在编译程序源代码的同一时候生成一些附属文件(比方源文件、类文件、程序公布描写叙述文件等),这些负数文件的内容也都与源代码相关。所以,使用APT能够取代传统的对代码信息和附属文件的维护工作。

    了解过Hebernate早期版本号的读者都知道:没写一个Java类文件。还必须额外地维护一个Hibernate映射文件(名为*.hbm.xml的文件。也有一些工具能够自己主动生成)。以下将使用Annotation来简化这不操作。

    Java提供的javac.exe工具有一个-processor选项。该选项可指定一个Annotation处理器,假设在编译Java源文件时通过该选项指定了Annotation处理器,那么这个Annotation处理器将会在编译时提取并处理Java源文件里的Annotation。

    每一个Annotation处理器都须要实现javax.annotation.processing包下的Processor接口。

    只是实现该接口必须实现它里面全部的方法,因此一般会採用继承AbstractProcessor的方式来实现Annotation处理器。一个Annotation处理器能够处理一种或者多种Annotation类型。

    以下示范一下怎样使用APT依据源文件里的凝视来生成额外的文件。首先定义3中Annotation类型,分别作用于持久化类、标识属性和普通成员属性。

    @Retention(RetentionPolicy.SOURCE)

    @Target(ElementType.TYPE)

    @Documented

    public @interface Persistent {

       String table();

    }

    上面这个Annotation用于修饰类、接口等类型声明,这个Annotation使用了@Retention指定其仅在源代码中存在。无法通过反射来读取其信息。

    @Retention(RetentionPolicy.SOURCE)

    @Target(ElementType.FIELD)

    @Documented

    public @interface Id {

       String column();

       String type();

       String generator();

    }

    上面这个Annotation用于修饰标识属性。

    @Retention(RetentionPolicy.SOURCE)

    @Target(ElementType.FIELD)

    @Documented

    public @interface Property{

       String column();

       String type();

    }

    上面这个Annotation用于修饰普通成员变量。以下我们写一个简单的Java类,并使用这三个Annotation来修饰。

    @Persistent(table="person_inf")

    public class Person {

       @Id(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 Person() {  }

      

       public Person(int id, String name, int age){

          this.id = id;

          this.name = name;

          this.age = 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;

       }

    }

    以下我们为这三个Annotation提供一个Annotation处理器,该处理器的功能是依据凝视来生成一个Hibernate映射文件(假设不懂Hibernate也没有关系。仅仅须要知道我们能够依据这些Annotation来生成一份xml就可以)。

    @SupportedSourceVersion(SourceVersion.RELEASE_7)

    //指定可处理@Persistent、@Id、@Property三个Annotation

    @SupportedAnnotationTypes({"Persistent","Id","Property"})

    public classHibernateAnnotationProcessor extends AbstractProcessor{

       //循环处理每一个须要处理的程序对象

       @Override

       public boolean process(Set<? extends TypeElement>annotations,

              RoundEnvironmentroundEnv) {

          //定义一个文件输出流。用于生成额外的文件

          PrintStreamps = null;

          try {

              for (Element t :roundEnv.getElementsAnnotatedWith(Persistent.class)){

                 //获取正在处理的类名

                 NameclazzName = t.getSimpleName();

                 //获取类定曾经的@Persistent Annotation

                 Persistent per = t.getAnnotation(Persistent.class);

                 ps= newPrintStream(newFileOutputStream(clazzName + ".hbm.xml"));

                 ps.println("<?xml version="1.0"?>");

                 ps.println("<!DOCTYPE hibernate-mapping PUBLIC");

                 ps.println(" "-//Hibernate/HibernateMapping DTD 3.0//EN"");

                 ps.println("   "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">");

                 ps.println("<hibernate-mapping>");

                 ps.print("<class name="" + t);

                 //输出per的table()的值

                 ps.println("" table=""+ per.table() + "">");

                 for (Element f :t.getEnclosedElements()) {

                    //仅仅处理Field上的Annotation

                    if(f.getKind() ==ElementKind.FIELD){

                        //获取Field定义前的@Id Annotation

                       Id id = f.getAnnotation(Id.class);

                       //当@Id Annotation存在时,输出<id.../>元素

                       if(id != null){

                          ps.println("    <idname="" + f.getSimpleName()

                                 +"" column="" + id.column()

                                 +"" type="" + id.type() + "">");

                          ps.println("    <generatorclass="" + id.generator() + ""/>");

                          ps.println("    </id>");

                       }

                       //获取Field定义前的@Property Annotation

                       Property p = f.getAnnotation(Property.class);

                       if(p != null){

                          ps.println("    <propertyname="" + f.getSimpleName()

                                 +"" column="" + p.column()

                                 +"" type="" + p.type() + "" />");

                       }

                    }

                 }

                 ps.println(" </class>");

                 ps.println("</hibernate-mapping>");

                

              }

          }catch(Exception e) {

              e.printStackTrace();

          }

          finally{

              if(ps != null){

                 try {

                    ps.close();

                 }catch(Exception e2) {

                    e2.printStackTrace();

                 }

              }

          }

          return true;

       }

    }

    上面的Annotation处理器事实上很easy,与前面通过反射来获取Annotation信息不通的是,这个Annotation处理器使用RoundEnviroment来获取Annotation信息,RoundEnviroment里包括了一个getElementsAnnotatedWith()方法。能够依据Annotation获取须要处理的程序单元,这个程序单元由Element代表。Element里包括了一个getKind()方法,该方法返回Element所代表的程序单元。返回值能够使ElementKind.Class(类)、ElementKind.Field(成员变量)。。。

    除此之外,Element还包括一个getEnclosedElements()方法,该方法可用于获取该Element里定义的全部程序单元,包括Field、方法、构造器、内部类等。

    接下来程序仅仅处理Field前面的Annotation。因此程序先推断这个Element必须是ElementKind.FIELD。

    再接下来程序调用了Element提供的getAnnotation(Class clazz)方法来获取改动该Element的Annotation。获取到Field上的@Id、@Property之后,接下来就依据他们提供的信息运行输出。

    提供了上面的Annotation处理器类之后,接下来就能够使用带-processor选项的javac.exe命令来编译Person.java了。比如例如以下命令:

    Javac–processor HibernateAnnotationProcessor Person.java

    使用上一句的前提是HibernateAnnotationProcessor已经编译,即当前文件夹下存在HibernateAnnotationProcessor.class文件。

    通过上面的命令编译Person.java后,将能够看到在同样路径下生成了一个Person.hbm.xml文件,该文件就是依据Person.java里的Annotation生成的。该文件内容例如以下:

    <?xml version="1.0"?

    >

    <!DOCTYPE hibernate-mapping PUBLIC

         "-//Hibernate/HibernateMapping DTD 3.0//EN"

         "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

    <hibernate-mapping>

    <class name=">Person"table="person_inf">

               <idname="id" column="person_id" type="integer">

               <generatorclass="identity"/>

               </id>

               <propertyname="name" column="person_name" type="string"/>

               <propertyname="age" column="person_age" type="integer"/>

         </class>

    </hibernate-mapping>

    对照上面的XML文件和Person.java中的Annotation就能够看出,它们是全然相应的,由于XML文件是依据Person.java中的Annotation生成的。从生成的这份XML文件能够看出。通过使用APT工具确实能够简化程序开发,程序猿仅仅须要把一写关键信息通过Annotation写在程序中,然后使用APT工具就能够生成额外的文件。

     

    总结文章,主要介绍了Java的Annotation支持。包含3个基本Annotation的使用方法。4个用于修饰Annotation的元Annotation的使用方法,怎样自己定义并使用Annotation。以及怎样使用APT工具来处理Annotation。通过使用Annotation能够为程序提供一写元数据,这些元数据能够在编译、执行时被读取。从而提供很多其它额外的处理信息。

     

  • 相关阅读:
    JavaScript 字符串常用操作
    Redis分布式锁
    CSS布局之-水平垂直居中
    vuejs学习笔记(1)--属性,事件绑定,ajax
    Angular.js学习笔记 (一)
    几年前写的一个支持多数据库切换的设计
    bundle中vim相关快捷键的使用
    使用Bundle进行VIM插件的管理
    javascript代码在线测试
    线程池的创建
  • 原文地址:https://www.cnblogs.com/shoshana-kong/p/10831238.html
Copyright © 2020-2023  润新知