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能够为程序提供一写元数据,这些元数据能够在编译、执行时被读取。从而提供很多其它额外的处理信息。

Java凝视Annotation的更多相关文章

  1. Java Basic - Annotation

    使用注解最主要的部分在于对注解的处理,那么就会涉及到注解处理器.      从原理上讲,注解处理器就是通过反射机制获取被检查方法上的注解信息,然后根据注解元素的值进行特定的处理.   注解处理器类库( ...

  2. java注解(Annotation)解析

    注解(Annotation)在java中应用非常广泛.它既能帮助我们在编码中减少错误,(比如最常见的Override注解),还可以帮助我们减少各种xml文件的配置,比如定义AOP切面用@AspectJ ...

  3. java自定义Annotation(载自百度文库)

    java中自定义annotation需要@interface关键字和用到几个内置annotation. 用到的注解有@Target,@Retention,@Documented,@Inherited  ...

  4. Java注解Annotation学习

    学习注解Annotation的原理,这篇讲的不错:http://blog.csdn.net/lylwo317/article/details/52163304 先自定义一个运行时注解 @Target( ...

  5. java注解Annotation

    扯扯注解的蛋 为什么学习注解?学习注解有什么好处?学完能做什么? 1.能够读懂别人的代码,特别是框架相关的代码 2.让编程更加简洁,代码更加清晰 3.让别人高看你一眼 注解是java1.5引入的 概念 ...

  6. java.lang和java.lang.annotation中实现Annotation的类小结

    加了注解,等于打上了某种标记,没加,则等于没有某种标记,以后,其他程序可以用反射来了解你的类上面有无何种标记,看你有什么标记,就去干相应的事.标记可以加在类,方法,字段,包上,方法的参数上. (1)  ...

  7. Java自定义Annotation,通过反射解析Annotation

    创建一个自定义的Annotation import java.lang.annotation.*; import java.lang.reflect.Method; @Documented @Targ ...

  8. java 注解Annotation

    什么是注解?  注解,顾名思义,注解,就是对某一事物进行添加注释说明,会存放一些信息,这些信息可能对以后某个时段来说是很有用处的. java注解又叫java标注,java提供了一套机制,使得我们可以对 ...

  9. Java - 注解 (Annotation)

    Java - 注解 (Annotation)   一.基本的 Annotation     > 使用 Annotation 时要在其前面增加 @符号,并把该 Annotation 当成一个修饰符 ...

随机推荐

  1. 检测iOS系统的定位服务

    [CLLocationManager locationServicesEnabled]检测的是整个iOS系统的位置服务开关

  2. 写PPT的方法

    这个方法是今天同事的方法,看到他的PPT简洁高效,明了,记下了他的方法: 写文字:写框架,这个框架或者内容可以是word形式的,目的是展示内容 找模板:在搜集到的各种ppt模板中选几个适合自己文字的页 ...

  3. [视频] x264 压缩笔记

    转载本站文章请注明,转载自:扶凯[http://www.php-oa.com] 本文链接: http://www.php-oa.com/2009/03/22/x264.html 象x264本身是不能直 ...

  4. poj1562--Oil Deposits

    Description The GeoSurvComp geologic survey company is responsible for detecting underground oil dep ...

  5. kaggle之Grupo Bimbo Inventory Demand

    Grupo Bimbo Inventory Demand kaggle比赛解决方案集合 Grupo Bimbo Inventory Demand 在这个比赛中,我们需要预测某个产品在某个销售点每周的需 ...

  6. linux磁盘管理、新增磁盘、分区、挂载

    1. du -sh 查看目录.文件总大小 -a:全部文件与目录大小都列出来.如果不加任何选项和参数只列出目录(包含子目录)大小. -c:最后加总2. df -h 查看磁盘使用量3. lsblk 查看系 ...

  7. XML 解析中,如何排除控制字符

    XML 解析中,如何排除控制字符 今天在解析一个中文的 XML时,始终报错 PCDATA invalid Char value 21 in Entity ,查询了一下这个 21 的ascii 值,发现 ...

  8. Linux脚本中使用特定JDK

    有时linux系统中装了很多应用,我们又不能覆盖系统中设置的版本,此时我们就需要在脚本文件中设置特定版本. export JAVA_HOME= export CLASSPATH=.:$JAVA_HOM ...

  9. Gengxin讲STL系列——Set

    本系列第二篇blog 第一篇写的心潮澎湃,结果写完一看,这都是些什么玩意= =| Set的中文名称是“集合”.集合,高一数学必修一课本给出的定义已经很明确了,简单来讲就是一个不含重复元素的空间(个人定 ...

  10. 当nginx 500 伪静态错误时,记录解决方法rewrite or internal redirection cycle while processing

    错误日志::rewrite or internal redirection cycle while processing "/index.php/index.php/index.php/in ...