第一节 概述
关于annotation是什么东西,百度一下会有很多关于它的描述,在这里就不用再copy了,我们还是以一个示例开始吧,在开始示例之前,我们先来一个小故事。
一:故事
有一个不学无术的孩子,整日游手好闲,用时下流行的话来形容我觉得最恰当不过,那就是“吃老一族”,依仗着父母现在还能蹦跶,自己好吃懒做无所事事,有一天突然看到台湾呕像歌星菜10的演唱会海报,此时他就准备让老爸帮他倒腾一张贵宾票观看演出,要知道菜10的演唱会贵宾票那是相当难买了,但是老爸为了成全儿子,还是通过各种渠道想尽办法帮他料理,老爸找到了主办单位,经过一番金钱的沟通,最终人家愿意让他的儿子以贵宾的待遇参加演唱会,可是他儿子还不愿意主动的找到主办方亮出自己的身份,他的意思就是自己一进演出现场就要有服务人员识别自己贵宾的身份(都懒到什么程度了),无奈,经过老爸与主办方的一再思考,最后主办方灵机一动,我们可以给他一张卡,只要他带上这个卡,我们就会认出他来,这个卡就是“败家子”卡,到时候让他戴在胸前,父亲大赞此计甚妙!回家之后就给他儿子说了,当然他儿子也就以败家子贵宾的身份观看了演出,在进站口,检票人员一眼就找见了“败家子”并且经过牌号他刚好是“250”,如此幸运,除了享受贵宾待遇之外他还获得了其他的待遇。
二:实现故事
故事讲完了,纯属虚构,现在我们以程序的形式进行一下说明,整个的流程在我们看来就是进检票口然后看演唱会,那么贵宾,以及特殊待遇都属于额外的收获,不用和我们的主程序发生任何关系,现在来实现一下。
定义一个annotation
package com.wangwenjun.annatation.userdefined;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
@Documented
public@interfaceVIPAnnotation {
String value();
}
下面是一个观看演出的代码
package com.wangwenjun.annatation.userdefined;
publicclass Concert {
@VIPAnnotation(250)
publicvoid attend(){
System.out.println("观看演出......");
}
}
注意我们在atten方法上面添加了我们的注解VIPAnnotation
好了,我们再写一个主办方的控制程序
package com.wangwenjun.annatation.userdefined;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Main {
public static void main(String[] args) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, SecurityException, NoSuchMethodException {
Class<Concert> clazz = Concert.class;
Object object = clazz.newInstance();
Method method = clazz.getMethod("attend", new Class[]{});
//首先都是可以看演出的
method.invoke(object, new Object[]{});
//如果他是vip
if(method.isAnnotationPresent(VIPAnnotation.class)){
//获得实现了的标记
VIPAnnotation vipAnnotation = method.getAnnotation(VIPAnnotation.class);
int sequcence=vipAnnotation.value();
System.out.println("享受贵宾待遇....");
if(250==sequcence){//看是否为第250号
//还可以享受贵宾待遇
System.out.println("和演员合影.......");
}
}
}
}
执行结果如下:
观看演出......
享受贵宾待遇....
和演员合影.......
通过上面的示例可以看得出来看演出的类并不知道会有额外功能的存在,在看演出的代码中我们并没有看得到一点点关于额外功能类的引用,但是在程序执行之后我们反倒添加了这样的功能,这样让看演出的类专心的做自己的工作,根本不用去关心其他的事情,剩下的事情有别人去做,例子虽然不大,但是已经有了面向切面AOP的味道,我们将看演出作为一个关注点进行了横切,这就是Annotation在程序中起到的作用。
再接下来的文字中会有详细的关于Annotation的说明。
第二节 Jdk自带Annotation
Annotation在jdk5.0中就已经出现了,我们也会经常的使用到,常见的有如下三种:
● @Override
● @Deprecated
● @SuppressWarnings
好了我们就上面的三个jdk自带的Annotation展开详细的解释和演示。
一:@Override Annotation
我们知道在面向对象中我们常常会从父类中继承某一些方法进行,有时候为了进行扩展可能会对父类的方法进行重写,这就是面向对象的一个特征------重写。
那么override的作用到底有什么用呢,他是为了确保子类正确的继承了父类的某一些方法,也就是强制的保证子类确实重写了父类的某些方法,我们就拿Object的toStirng方法进行继承重写
package com.wangwenjun.annatation;
publicclass AnnotationTest {
@Override
public String toString() {
return"this is a test String";
}
publicstaticvoid main(String[] args) {
AnnotationTest test = new AnnotationTest();
System.out.println(test);
}
}
打印结果为:this is a test String
如果我们将toStirng方法的写成tostirng,程序不是会报错的,因为Object不是一个抽象类,并没有强制要求子类进行继承,但是我们在tostring方法中就不能加@Override,因为父类中的确没有tostring方法。
二:@Deprecated Annotation
我们再jdk文档中经常会看到某些方法已经不被推荐使用了,但是他又不能直接最新的版本中进行删除(因为考虑到兼容性的问题)那么添加一个@Deprecated标注,告诉使用者该方法不建议使用,可能会出现某些问题,好了,用代码演示之。
package com.wangwenjun.annatation;
publicclass DeprecatedAnnotation {
@Deprecated
publicvoid methodOne(){
System.out.println("just print a String");
}
publicstaticvoid main(String[] args) {
DeprecatedAnnotation deprecate = new DeprecatedAnnotation();
deprecate.methodOne();
}
}
我们可以清楚的看到,方法已经被删除线标示了出来,执行的时候可能不会出现错误,能照常运行。
运行结果:just print a String
还需要说明的一点是如果子类继承了父类的某一个@Deprecated方法,那么子类的该方法也是会不建议使用的,所以他存在一个父类到子类的传递过程。
三:@SuppressWarnings Annotation
该Annotation稍微复杂一些,熟悉泛型的人应该很清楚编译器经常报出的SuppressWarnings警告。比如我们在jdk5.0以后的编译器进行集合的编码,没有使用泛型,编译器会给我们报出unchecked的警告,代码如下
package com.wangwenjun.annatation;
import java.util.ArrayList;
import java.util.List;
publicclass SuppressWarningsAnnotation {
publicstaticvoid main(String[] args) {
List list = new ArrayList();
list.add("test");
list.add("test2");
System.out.println(list.size());
}
}
当然运行是肯定没有任何问题的,上述的代码只不过是打印了链表的长度。但是编译器给我们的警告我们怎样让它消失呢,这个时侯@SuppressWarnings 标记就派上用场了,看名字也可以看得出来后面是复数的形式,说明会有很多个警告可供我们填写,代码示例
package com.wangwenjun.annatation;
import java.util.ArrayList;
import java.util.List;
@SuppressWarnings("unchecked")
publicclass SuppressWarningsAnnotation {
publicstaticvoid main(String[] args) {
List list = new ArrayList();
list.add("test");
list.add("test2");
System.out.println(list.size());
}
}
上述代码和第一次给出的代码没有任何的不同,只是多了@SuppressWarnings ("unchec ked")这样的一句标注,此时警告已经完全消失了。
如果我们的代码中出现了不被建议使用的方法,也就是出现了Deprecated的方法,此时编译器还是会给我们警告的,那么我们应该如何处理呢,看一下的代码
package com.wangwenjun.annatation;
import java.util.ArrayList;
import java.util.List;
@SuppressWarnings({"unchecked","deprecation"})
publicclass SuppressWarningsAnnotation {
publicstaticvoid main(String[] args) {
List list = new ArrayList();
DeprecatedAnnotation deprecate = new DeprecatedAnnotation();
list.add("test");
list.add("test2");
System.out.println(list.size());
deprecate.methodOne();
}
}
此时methodone方法还是会有删除线的标示,因为他确实是一个deprecated的方法,但是编译器不会给我们出现警告提示了。至于为什么SuppressWarnings的使用方式会有两种方式,在后文中会进行详细的阐述。
第三节 自定义Annotation
使用过spring框架,ejb框架,cxf,junit2等支持Annotation技术的框架的人都会很清楚,其中除了jdk自带的一些Annotation以外,我们还可以自己定义很多Annotation帮助我们进行框架的搭建,这一小节我们将逐步的进行自定义Annotation的说明。
Ok,开始我们的第一个Annotation程序之旅吧。
一:Quick Start
定义Annotation的时候和定义接口的方式很类似,只不过再interface前面加了@,我感觉在这一点sun公司确实有一些吝啬,为何不开辟一个新的类型标识符呢,呵呵,当然这样的抱怨也是无关紧要的,只要能达到目的即可。牢骚也不不多发了,直接上codes。
package com.wangwenjun.annatation.userdefined;
public@interfaceUserdefinedAnnotation {
}
代码很简单,没有任何的结构,只是一个空的Annotation,接下来演示一下如何对其进行使用。
package com.wangwenjun.annatation.userdefined;
publicclass UseAnnotation {
@UserdefinedAnnotation
publicstaticvoid main(String[] args) {
System.out.println("hello");
}
}
上述的代码简单到再也不能简单,甚至简单到说明不了任何问题,只是从它上面可以看得出来我们如何自定一个Annotation,并且如何的使用它,我们并没有给自定义Annotation中添加任何的属性,甚至没有指定其保持力(Retention),可继承性(Inherited),标注对象(Target)等,再接下来的描述中我们会逐步进行说明。
二:Annotation属性值
Annotation属性值大致有以下三种:
● 基本类型
● 数组类型
● 枚举类型
我们在下面的文字中将会一个个的进行演示和说明。
1:基本串类型
package com.wangwenjun.annatation.userdefined;
public@interfaceUserdefinedAnnotation {
int value();
String name();
String address();
}
上面是一个自定义的annotation,可能稍微有一些复杂,只不过是为了更多的说明一下问题,可以看出来在定义属性的时候有点像interface定义方法一样,每一个属性名称之后需要加上括号,接下来看看如何使用。
package com.wangwenjun.annatation.userdefined;
publicclass UseAnnotation {
@UserdefinedAnnotation(value=123,name="wangwenjun",address="火星")
publicstaticvoid main(String[] args) {
System.out.println("hello");
}
}
如果在使用UserdefinedAnnotation时候不给相关的属性赋值,会出现错误。
需要说明的一点事如果一个annotation中只有一个属性名字叫value,我没在使用的时候可以给出属性名也可以省略。
public@interfaceUserdefinedAnnotation {
int value();
}
package com.wangwenjun.annatation.userdefined;
publicclass UseAnnotation {
@UserdefinedAnnotation(value=123)
publicstaticvoid main(String[] args) {
System.out.println("hello");
}
}
也可以写成如下的形式
@UserdefinedAnnotation(123)
publicstaticvoid main(String[] args) {
System.out.println("hello");
}
直接对其进行了省略。如果定义的属性名字不叫value,那么属性名字是不可以省略的哦!那是因为value属性名是annotation默认的一个属性名,这一点很重要的哦,千万不能忽略。
2:数组类型
我们在自定义annotation中定义一个数组类型的属性,代码如下:
public@interfaceUserdefinedAnnotation {
int[] value();
}
我们如何使用呢,代码如下:
publicclass UseAnnotation {
@UserdefinedAnnotation({123})
publicstaticvoid main(String[] args) {
System.out.println("hello");
}
}
注意1:其中123外面的大括号是可以被省略的,因为只有一个元素,如果里面有一个以上的元素的话,花括号是不能被省略的哦。比如{123,234}。
注意2:其中属性名value我们在使用的时候进行了省略,那是因为他叫value,如果是其他名字我们就不可以进行省略了必须是@UserdefinedAnnotation(属性名={123,234})这样的格式。
3:枚举类型
自从jdk5.0以后,java引进了枚举类型,我个人比较喜欢这样的方式尤其在进行业务逻辑判断的if或者switch子句中使用很方便,而且还不容易出错,大多时候都是作为一个函数的形式参数而存在,关于枚举类型的使用请查看相应的doc文档。
我们定义一个enum类型
package com.wangwenjun.annatation.userdefined;
publicenum DateEnum {
Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday
}
然后在定义一个annotation
package com.wangwenjun.annatation.userdefined;
public@interfaceUserdefinedAnnotation {
DateEnum week();
}
可以看出annotation中的属性类型为enum类型的,接下来我们看看他如何来使用
package com.wangwenjun.annatation.userdefined;
publicclass UseAnnotation {
@UserdefinedAnnotation(week=DateEnum.Sunday)
publicstaticvoid main(String[] args) {
System.out.println("hello");
}
}
在使用上也是很方便的,直接用来进行相关的引用,这样再应用的过程中就不会出现错误。
4:默认值
有时候我们在使用annotation的时候某一些属性值是会被经常使用到的,或者说他会有一个默认值给我们直接进行使用,那么我们在定义annotation的时候就可以为属性直接给出默认值,下面进行一下简单的示例。
public@interfaceUserdefinedAnnotation {
String name() default"zhangsan";
}
在使用的时候我们可以不进行指定
publicclass UseAnnotation {
@UserdefinedAnnotation()
publicstaticvoid main(String[] args) {
System.out.println("hello");
}
}
当然我们也可以自己对其进行重新的设置,其中数组和枚举类型的默认值基本上类似,就不多做赘述了,自己进行测试即可。
5:注意
● Annotation是不可以继承其他接口的,这一点是需要进行注意,这也是annotation的一个规定吧。
● Annotation也是存在包结构的,在使用的时候直接进行导入即可。
● Annotation类型的类型只支持原声数据类型,枚举类型和Class类型的一维数组,其他的类型或者用户自定义的类都是不可以作为annotation的类型,我查看过文档并且进行过测试。
三:Retention标记
Retention标记是告知编译器如何来处理我们自定义的annotation,指示注释类型的注释要保留多久。如果注释类型声明中不存在 Retention 注释,则保留策略默认为RetentionPolicy.CLASS。
查看他的源代码,会发现他有一个属性value
,
类型为RetentionPolicy,RetentionPolicy是一个枚举类型。其中有三个类型的值分别代表不同的意思。
CLASS编译器将把注释记录在类文件中,但在运行时 VM 不需要保留注释。 |
RUNTIME编译器将把注释记录在类文件中,在运行时 VM 将保留注释,因此可以反射性地读取。 |
SOURCE编译器要丢弃的注释 |
下面是一段简短的定义代码示例。
package com.wangwenjun.annatation.userdefined;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public@interfaceUserdefinedAnnotation {
String name() default"zhangsan";
}
从上面的实例中我们自定义的annotation是一个runntime范围的annotation,也就是说他会保持在源文件中并且也会在运行时由JVM自动调用。
我们先对它有一个感性的认识,知道有这么一个东西,在后面的文章中我会以一个示例对其进行详细的说明(会涉及到反射的相关东西)。其实Retention的名字翻译过来就是“保持力”的意思,说明的很清楚,就是我的annotation存放在哪些地方,也就是说我的annotation他的影响力到底在哪。
四:AnnotatedElement
在jdk5.0以后java反射包增加了这样一个接口,主要是用来对annotation进行操作的,其中AccessibleObject, Class, Constructor, Field, Method, Package都对其进行了实现继承。总共有以下四个方法:
|
getAnnotation(Class<T> annotationType) |
|
getAnnotations() |
||
getDeclaredAnnotations() |
||
boolean |
isAnnotationPresent(Class<? extends Annotation> annotationType) |
在接下来的章节中我们会进行说明。
五:Target 标记
在我们之前的实例中我们定义的annotation可以放在一个类的任何位置,那么我们是否可以对annotation的位置进行设置呢,答案是可以的,这就是我们所要说的的Target 标记,他里面也有一个枚举类型的属性value,其中枚举类型为ElementType,有很多自定义的属性,如下所示:
ANNOTATION_TYPE |
CONSTRUCTOR |
FIELD |
LOCAL_VARIABLE |
METHOD |
PACKAGE |
PARAMETER |
TYPE |
上面的列表已经讲述的很清楚了,我们就不再进行说明了,直接来一个小程序进行演示吧,还是先来一个annotation,假设我们的annotation只能放在方法的前面
package com.wangwenjun.annatation.userdefined;
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
public@interfaceUserdefinedAnnotation {
String name() default"zhangsan";
}
package com.wangwenjun.annatation.userdefined;
publicclass UseAnnotation {
@UserdefinedAnnotation()
publicstaticvoid main(String[] args) {
System.out.println("hello");
}
}
我们的annotation只能放在main方法上面放在其他的位置会出现错误。
六:Documented 标记
这个annotation非常简单,也非常容易理解,使用过javadoc命令的人都会很清楚,我们可以用javadoc命令将方法,类,变量等前面的注释转换成文档,在默认的情况下javadoc命令不会将我们的annotation生成再doc中去的,所以使用该标记就是告诉jdk让它也将annotation生成到doc中去,比如:
package com.wangwenjun.annatation.userdefined;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Documented
public@interfaceUserdefinedAnnotation {
String name() default"zhangsan";
}
七:Inherited标记
该标记的意思就是说,比如有一个类A,在他上面有一个标记annotation,那么A的子类B是否不用再次标记annotation就可以继承得到呢?答案是肯定的,我们做一个简单的演示,首先我们有一个annotation
package com.wangwenjun.annatation.userdefined;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public@interfaceUserdefinedAnnotation {
String name() default"zhangsan";
}
接着定义一个父类
package com.wangwenjun.annatation.userdefined;
@UserdefinedAnnotation
publicclass ParentClass {
}
父类什么都没有干,只是一个空的类,并且有UserdefinedAnnotation的标记,然后我们写一个继承他的子类。
package com.wangwenjun.annatation.userdefined;
publicclass ChildClass extends ParentClass{
}
我们准备工作都已经做完了,现在就是利用反射机制进行一下简短的测试
package com.wangwenjun.annatation.userdefined;
publicclass TestInherited {
publicstaticvoid main(String[] args) {
Class<ChildClass> clazz = ChildClass.class;
boolean isExist=clazz.isAnnotationPresent(UserdefinedAnnotation.class);
if(isExist){
System.out.println("子类继承了父类的annotation");
}else{
System.out.println("子类没有继承父类的annotation");
}
}
}
打印结果为:子类继承了父类的annotation。可以看到子类果然继承了父类的annotation标记。
第四节 Annotation的应用
为了都能使用到我们上面所说的知识点,我们就拿junit来作为我们的演示,再junit1.4版本以前还不支持annotation的方式,我们为了编写junit代码必须extends TestCase类,并且所有的方法都要以test开始,假设我们现在要实现一个junit这样的框架,当然只是为了说明问题,不可能有junit那样的强大,我们模拟一个测试用例,并且模拟一个可以执行测试用例的命令
我们写一个测试用例
package com.wangwenjun.annatation.userdefined;
publicclass TestCase {
publicvoid testMethod1(){
System.out.println("method1");
}
publicvoid testMethod2(){
System.out.println("method2");
}
publicvoid nonTestSuffixMethod(){
System.out.println("-----------------");
}
}
再写一个执行测试用例的命令
package com.wangwenjun.annatation.userdefined;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
publicclass ExecuteTestCase {
privatefinalstatic String SUFFIX = "test";
publicstaticvoid main(String[] args) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
Class<TestCase> clazz = TestCase.class;
Object object = clazz.newInstance();
Method[] methods = clazz.getMethods();
for(Method m:methods){
//如果方法是以test开头的话就执行
if(m.getName().startsWith(SUFFIX)){
m.invoke(object, new Object[]{});
}
}
}
}
打印结果为:
method1
method2
可以看出最后一个方法nonTestSuffixMethod没有被执行,因为他并没有以test开头。
我们已经实现了一个类似于junit框架的程序,当然我们并没有继承类似于junit框架中的TestCase。
然后我们模拟一个junit1.4版本以后的实现,首先我们实现一个annotation
package com.wangwenjun.annatation.userdefined;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public@interfaceTest {
}
我们写了一个Test标注,然后我们把我们的测试用进行了一下修改
package com.wangwenjun.annatation.userdefined;
publicclass TestCase {
@Test
publicvoid method1(){
System.out.println("method1");
}
@Test
publicvoid method2(){
System.out.println("method2");
}
@Test
publicvoid nonTestSuffixMethod(){
System.out.println("nonTestSuffixMethod");
}
}
然后将我们的执行程序进行一下修改
package com.wangwenjun.annatation.userdefined;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
publicclass ExecuteTestCase {
publicstaticvoid main(String[] args) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
Class<TestCase> clazz = TestCase.class;
Object object = clazz.newInstance();
Method[] methods = clazz.getMethods();
for(Method m:methods){
Class<Test> testClass = Test.class;
//如果某个方法前面加上了标注@Test我们就执行它
if(m.isAnnotationPresent(testClass)){
m.invoke(object, new Object[]{});
}
}
}
}
执行结果为: method1
method2
nonTestSuffixMethod
可以看到连最后的一个方法nonTestSuffixMethod都进行了执行。
好了我们的例子结束了,但是我们的文档并没有结束,下面我们将要着力介绍一个基于annotation的框架。
第五节 介绍Xmappr
好像在2010年一月初的时候刚刚发布了0.9版本,这个框架的主要作用就是用来进行读取配置文件(xml)的,但是和以前我们常用的有所不同,他是直接用java的对象和xml进行映射,在我们以前读取xml的时候,哪怕是使用了jdom,dom4j,xpath等的相关API,获得字段仍然失去了他本来的属性,比如说他之前是一个integer类型的,我们获取到的一律都是string类型的,如果配置文件数量比较多,配置项比较多,我们还要垒一些看似一样的代码。
一:概要
Xmappr是一个使用纯Java编写的处理XML的类库,提供一种非常简单的方式来处理XML,可以将任意XML与Java类相映射。
主要特性:
映射通过注释或外部配置实现
可以缓存未映射元素
保持XML元素顺序
可以自定义类型转换器
完全支持XML命名空间
线程安全(设计用于多线程使用)
不包含任何依赖关系
整个类库大小只有80kb
采用BSD许可
二:示例
Xmappr的使用非常简单,更为详细的API和说明可以直接查看他的官方文档
http://www.theserverside.com/news/thread.tss?thread_id=59078,首先我们引入jar文件,注意你的jdk版本需要是6.0以上的才可以,如果不是6.0的需要从sun的官网下载xmlstream相关的jar。直接来代码演示:
<?xml version="1.0" encoding="UTF-8"?>
<root a="2.2">
some text
<node>123</node>
</root>
上述的是一段配置文件。
package com.neusoft.xmappr;
import org.xmappr.Attribute;
import org.xmappr.Element;
import org.xmappr.RootElement;
import org.xmappr.Text;
@RootElement
public class Root {
@Attribute
public float a;
@Element
private Integer node;
@Text
public String text;
/**
* @return the a
*/
public float getA() {
return a;
}
/**
* @param a the a to set
*/
public void setA(float a) {
this.a = a;
}
/**
* @return the node
*/
public Integer getNode() {
return node;
}
/**
* @param node the node to set
*/
public void setNode(Integer node) {
this.node = node;
}
/**
* @return the text
*/
public String getText() {
return text;
}
/**
* @param text the text to set
*/
public void setText(String text) {
this.text = text;
}
}
上述是配置文件对应的javabean,好了看一下我们的测试代码
@Test
publicvoid testAnnotaionTypeXmappr() throws FileNotFoundException {
Xmappr xmappr = new Xmappr(Root.class);
FileReader reader = new FileReader(XML_PATH);
Root root = (Root) xmappr.fromXML(reader);
Assert.assertEquals(2.2f, root.getA());
}
程序运行没有任何问题,是不是和以前的方式进行xml读取相比较方便了很多。详细的说明请直接查看官方文档,在这里只做一个引子,主要是想说明一下annotation带给程序的一些方便