标签归档:JAVA

Java REST框架一览

目前宣称支持REST的Java框架包括以下这些: 
Restlet(http://www.restlet.org/) 
Cetia4() 
Apache Axis2(http://http://ws.apache.org/axis2/) 
sqlREST(http://sqlrest.sourceforge.net/) 
REST-art(http://rest-art.sourceforge.net/) 

以下对这些框架进行了较为全面的分析。 

Restlet,最新版本1.0.1 
特点:完全抛弃了Servlet API,作为替代,自己实现了一套API。能够支持复杂的REST架构设计。 
缺点: 
1. 虽然也可以运行于Web容器中,但是难以利用Servlet和JSP等资源。因为需要另外学习一套API和概念,学习成本比较高。 
2. 完全不支持服务器端的HTTP Session,强制完全基于无状态服务器模型来做开发。对于基于浏览器的应用来说,开发难度较高。 
3. 自身没有包括与Spring的集成,可以使用第三方代码与Spring集成,集成难度较大。 
4. 文档不是很丰富,大多比较简短,很多时候需要自己去读代码,或者到其wiki上去查找。 
5. 没有内建的国际化支持。 
优点: 
1. 有内建的HTTP认证机制,不需要另外开发安全机制。 
2. 灵活性较高,支持更多的REST概念,支持透明的内容协商,适合于开发更加强大的REST组件(不限于服务器端应用)。 
3. 零配置文件,全部配置通过代码来完成。 

相关资源: 
功能列表:http://www.restlet.org/about/features
简介:http://www.restlet.org/about/introduction
教程:http://www.restlet.org/documentation/1.0/tutorial
FAQ:http://www.restlet.org/about/faq

Cetia4,最新版本1.0 
特点:基于Servlet API开发,可以运行于所有的Web容器中。 
优点: 
1. 可以充分利用Servlet API和JSP等资源,需要额外学习的概念较少,学习成本较低。 
2. 对于传统的Web应用,可以使用服务器端HTTP Session;对于Web服务类应用,不使用HTTP Session,基于无状态服务器模型做开发。 
3. 自身包括了对于Web MVC的支持,熟悉Web MVC框架的开发者很容易理解。还内建了参数映射、参数验证等等传统Web MVC框架所支持的功能。 
4. 内建了自己特有的导航对象栈的概念,对于支持传统的Web应用的开发(基于浏览器的导航)非常有帮助。 
5. 提供了JSP标签库,对于传统的基于HTML表单的Web开发非常有帮助。 
6. 支持与SiteMesh相配合,由SiteMesh来支持页面布局的重用。 
7. 内建有与Spring的集成,集成起来非常容易。 
8. 配置文件完全基于标准的web.xml,不需要额外的配置文件。大量使用默认配置,一般情况下足以满足常见的需求。 
9. 拥有很好的文档。 
10. 有内建的国际化支持。 
缺点: 
1. 没有内建的HTTP认证机制,需要自行开发安全机制。 
2. 对于内容协商的支持比较弱,仅支持HTML和XML格式的表现。需要加以扩展才能支持其他格式的表现。 

相关资源: 
教程:

Axis2,最新版本1.2 
特点:同时支持SOAP和REST风格的Web Service。 
缺点: 
1. 仅仅支持GET与POST方法。 
2. 仅仅是以REST风格暴露出Web服务,数据格式仍然是包含SOAP封装的XML,不能使用更加有效的格式。 
3. 只支持同步的调用方式。 
4. 仅仅提供了以SOAP方式暴露Web服务的最小化的支持,不支持全面的REST架构设计。 

相关资源: 
简介:http://ws.apache.org/axis2/1_2/rest-ws.html

sqlREST,最新版本0.3.1 
特点: 
1. 为任何可以通过JDBC访问的数据库提供Web服务访问接口,自动将REST风格的HTTP请求转换为相应的数据库SQL语句,并将数据库中的记录编码为XML格式传给客户端。是REST风格的HTTP请求到数据库中的数据的直接映射。 
2. 基于Servlet API开发。 
缺点: 
1. 因为是REST风格的HTTP请求到SQL语句的直接映射,因此强制使用以SQL和关系数据库为中心的数据建模设计方法,不支持面向对象的设计。灵活性很低,难以实现较为复杂的业务逻辑。 
2. 因为资源的定义仅限于数据库的表,难以实现更高层次的抽象,必然会导致非常细粒度的API。应用的性能和可伸缩性都难以保证。 

相关资源: 
教程:http://sqlrest.sourceforge.net/5-minutes-guide.htm

REST-art,最新版本0.2 
特点:一个旨在替换复杂的SOAP框架的REST框架,用来作为替代SOAP方便地发布Web服务的工具。不是基于Servlet API开发。 
缺点: 
1. 目前尚处于刚刚起步的阶段,功能非常少。 
2. 不是基于Servlet API,带来了额外的学习成本。 

相关资源: 

教程:http://sourceforge.net/docman/index.php?group_id=175132

来自:http://www.iteye.com/topic/85928

Annotation

Java Annotation手册 

作者:cleverpig(作者的Blog:http://blog.matrix.org.cn/page/cleverpig) 

原文:http://www.matrix.org.cn/resource/article/44/44055_Java+Annotation+Reflect.html 

关键字:java,annotation,reflect 



前言: 

在上篇文章《Java Annotation入门》中概要性的介绍了Annotation的定义、使用,范围涵盖较广,但是深度不够。所以作者在《Java Annotation入门》后,继续整理了Annotation的概念和知识点,与喜欢research的朋友们共享。 



阅读提示:文中提到的程序成员或者程序元素是一个概念,指组成程序代码的单元:如类、方法、成员变量。 



一、Annotation究竟是什么? 



Annotation 提供了一条与程序元素关联任何信息或者任何元数据(metadata)的途径。从某些方面看,annotation就像修饰符一样被使用,并应用于包、类型、构造方法、方法、成员变量、参数、本地变量的声明中。这些信息被存储在annotation的“name=value”结构对中。 annotation类型是一种接口,能够通过java反射API的方式提供对其信息的访问。 



annotation能被用来为某个程序元素(类、方法、成员变量等)关联任何的信息。需要注意的是,这里存在着一个基本的潜规则:annotaion不能影响程序代码的执行,无论增加、删除 annotation,代码都始终如一的执行。另外,尽管一些annotation通过java的反射api方法在运行时被访问,而java语言解释器在工作时忽略了这些annotation。正是由于java虚拟机忽略了annotation,导致了annotation类型在代码中是“不起作用”的;只有通过某种配套的工具才会对annotation类型中的信息进行访问和处理。本文中将涵盖标准的annotation和meta- annotation类型,陪伴这些annotation类型的工具是java编译器(当然要以某种特殊的方式处理它们)。 



由于上述原因,annotation在使用时十分简便。一个本地变量可以被一个以NonNull命名的annotation类型所标注,来作为对这个本地变量不能被赋予null值的断言。而我们可以编写与之配套的一个annotation代码分析工具,使用它来对具有前面变量的代码进行解析,并且尝试验证这个断言。当然这些代码并不必自己编写。在JDK安装后,在JDK/bin目录中可以找到名为“apt”的工具,它提供了处理annotation的框架:它启动后扫描源代码中的annotation,并调用我们定义好的annotation处理器完成我们所要完成的工作(比如验证前面例子中的断言)。说到这里, annotation的强大功能似乎可以替代XDoclet这类的工具了,随着我们的深入,大家会更加坚信这一点。 

注:详细描述请参看jsr250规范: 

http://www.jcp.org/aboutJava/communityprocess/pfd/jsr250/ 



二、Annotation的定义: 



这段文字开始介绍annotation相关技术。在此大家将看到java5.0的标准annotation类型,这种标准类型就是前文中所说的“内建”类型,它们可以直接被javac支持。可喜的是,在java6.0beta版中的javac已经加入了对自定义annotation的支持。 



1。Annotation的概念和语法: 



首先,关键的概念是理解annotation是与一个程序元素相关联信息或者元数据的标注。它从不影响java程序的执行,但是对例如编译器警告或者像文档生成器等辅助工具产生影响。 



下面是常用的annotation列表,我们应该注意在annotation和annotation类型之间的不同: 



A.annotation: 

annotation 使用了在java5.0所带来的新语法,它的行为十分类似public、final这样的修饰符。每个annotation具有一个名字和成员个数 >=0。每个annotation的成员具有被称为name=value对的名字和值(就像javabean一样),name=value装载了 annotation的信息。 



B.annotation类型: 

annotation 类型定义了annotation的名字、类型、成员默认值。一个annotation类型可以说是一个特殊的java接口,它的成员变量是受限制的,而声明annotation类型时需要使用新语法。当我们通过java反射api访问annotation时,返回值将是一个实现了该annotation类型接口的对象,通过访问这个对象我们能方便的访问到其annotation成员。后面的章节将提到在java5.0的java.lang包里包含的3个标准annotation类型。 



C.annotation成员: 

annotation 的成员在annotation类型中以无参数的方法的形式被声明。其方法名和返回值定义了该成员的名字和类型。在此有一个特定的默认语法:允许声明任何 annotation成员的默认值:一个annotation可以将name=value对作为没有定义默认值的annotation成员的值,当然也可以使用name=value对来覆盖其它成员默认值。这一点有些近似类的继承特性,父类的构造函数可以作为子类的默认构造函数,但是也可以被子类覆盖。 



D.marker annotation类型: 

一个没有成员定义的annotation类型被称为marker annotation。这种annotation类型仅使用自身的存在与否来为我们提供信息。如后面要说的Override。 



E.meta-annotation: 

meta -annotation也称为元annotation,它是被用来声明annotation类型的annotation。Java5.0提供了一些标准的元-annotation类型。下面介绍的target、retention就是meta-annotation。 



F.target: 

annotation 的target是一个被标注的程序元素。target说明了annotation所修饰的对象范围:annotation可被用于packages、 types(类、接口、枚举、annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch 参数)。在annotation类型的声明中使用了target可更加明晰其修饰的目标。 



G.retention: 

annotation 的retention定义了该annotation被保留的时间长短:某些annotation仅出现在源代码中,而被编译器丢弃;而另一些却被编译在 class文件中;编译在class文件中的annotation可能会被虚拟机忽略,而另一些在class被装载时将被读取(请注意并不影响class 的执行,因为annotation与class在使用上是被分离的)。使用这个meta-annotation可以对annotation的“生命周期” 限制。 



H.metadata: 

由于metadata被广泛使用于各种计算机开发过程中,所以当我们在这里谈论的metadata即元数据通常指被annotation装载的信息或者annotation本身。 



2。使用标准Annotation: 

java5.0在java.lang包中定义了3种标准的annotation类型: 



A.Override: 

java.lang.Override 是一个marker annotation类型,它被用作标注方法。它说明了被标注的方法重载了父类的方法,起到了断言的作用。如果我们使用了这种annotation在一个没有覆盖父类方法的方法时,java编译器将以一个编译错误来警示。 

这个annotaton常常在我们试图覆盖父类方法而确又写错了方法名时发挥威力。 



使用方法极其简单:在使用此annotation时只要在被修饰的方法前面加上@Override。 

下面的代码是一个使用@Override修饰一个企图重载父类的toString方法,而又存在拼写错误的sample: 

清单1: 







@Override 



public String toSting() {   // 注意方法名拼写错了 



    return “[” + super.toString() + “]”; 











B.Deprecated: 

同样Deprecated也是一个marker annotation。当一个类型或者类型成员使用@Deprecated修饰的话,编译器将不鼓励使用这个被标注的程序元素。而且这种修饰具有一定的 “延续性”:如果我们在代码中通过继承或者覆盖的方式使用了这个过时的类型或者成员,虽然继承或者覆盖后的类型或者成员并不是被声明为 @Deprecated,但编译器仍然要报警。 

值得注意,@Deprecated这个annotation类型和javadoc中的 @deprecated这个tag是有区别的:前者是java编译器识别的,而后者是被javadoc工具所识别用来生成文档(包含程序成员为什么已经过时、它应当如何被禁止或者替代的描述)。 

在java5.0,java编译器仍然象其从前版本那样寻找@deprecated这个javadoc tag,并使用它们产生警告信息。但是这种状况将在后续版本中改变,我们应在现在就开始使用@Deprecated来修饰过时的方法而不是 @deprecated javadoc tag。 

清单2: 







下面是一段使用@Deprecated的代码: 



/** 



* 这里是javadoc的@deprecated声明. 



* @deprecated No one has players for this format any more.  Use VHS instead. 



*/ 



@Deprecated public class Betamax { … } 







C.SuppressWarnings: 

@SuppressWarnings 被用于有选择的关闭编译器对类、方法、成员变量、变量初始化的警告。在java5.0,sun提供的javac编译器为我们提供了-Xlint选项来使编译器对合法的程序代码提出警告,此种警告从某种程度上代表了程序错误。例如当我们使用一个generic collection类而又没有提供它的类型时,编译器将提示出”unchecked warning”的警告。 



通常当这种情况发生时,我们就需要查找引起警告的代码。如果它真的表示错误,我们就需要纠正它。例如如果警告信息表明我们代码中的switch语句没有覆盖所有可能的case,那么我们就应增加一个默认的case来避免这种警告。 

相仿,有时我们无法避免这种警告,例如,我们使用必须和非generic的旧代码交互的generic collection类时,我们不能避免这个unchecked warning。此时@SuppressWarning就要派上用场了,在调用的方法前增加@SuppressWarnings修饰,告诉编译器停止对此方法的警告。 

SuppressWarning不是一个marker annotation。它有一个类型为String[]的成员,这个成员的值为被禁止的警告名。对于javac编译器来讲,被-Xlint选项有效的警告名也同样对@SuppressWarings有效,同时编译器忽略掉无法识别的警告名。 



annotation语法允许在annotation名后跟括号,括号中是使用逗号分割的name=value对用于为annotation的成员赋值: 

清单3: 







@SuppressWarnings(value={“unchecked”,”fallthrough”}) 



public void lintTrap() { /* sloppy method body omitted */ } 







在这个例子中SuppressWarnings annotation类型只定义了一个单一的成员,所以只有一个简单的value={…}作为name=value对。又由于成员值是一个数组,故使用大括号来声明数组值。 



注意:我们可以在下面的情况中缩写annotation:当annotation只有单一成员,并成员命名为”value=”。这时可以省去”value=”。比如将上面的SuppressWarnings annotation进行缩写: 

清单4: 







@SuppressWarnings({“unchecked”,”fallthrough”}) 





如果SuppressWarnings所声明的被禁止警告个数为一个时,可以省去大括号: 







@SuppressWarnings(“unchecked”) 







3。Annotation语法: 



在上一个章节中,我们看到书写marker annotation和单一成员annotation的语法。下面本人来介绍一下完整的语法:



annotation 由“@+annotation类型名称+(..逗号分割的name-value对…)”组成。其中成员可以按照任何的顺序。如果annotation 类型定义了某个成员的默认值,则这个成员可以被省略。成员值必须为编译时常量、内嵌的annotation或者数组。 



下面我们将定义一个 annotation类型名为Reviews,它有一个由@Review annotation数组构成的成员。这个@Review annotation类型有三个成员:”reviewer”是一个字符串,”comment” 是一个具有默认值的可选的字符串,”grade”是一个Review.Grade枚举类型值。 

清单5: 







@Reviews({  // Single-value annotation, so “value=” is omitted here 



    @Review(grade=Review.Grade.EXCELLENT, 



            reviewer=”df”), 



    @Review(grade=Review.Grade.UNSATISFACTORY, 



            reviewer=”eg”, 



            comment=”This method needs an @Override annotation”) 



}) 





annotation语法的另一个重要规则是没有程序成员可以有多于一个的同一annotation实例。例如在一个类中简单的放置多个@Review annotation。这也是在上面代码中定义@Reviews annotation类型数组的原因。 



4。Annotation成员类型和值: 



annotation成员必须是非空的编译时常量表达式。可用的成员类型为:primitive类型、, String, Class, enumerated类型, annotation类型, 和前面类型的数组。 



下面我们定义了一个名为UncheckedExceptions 的annotation类型,它的成员是一个扩展了RuntimeException类的类数组。 

清单6: 







@UncheckedExceptions({ 



    IllegalArgumentException.class, StringIndexOutOfBoundsException.class 



}) 







5。Annotation的目标: 



annotation通常被放在类型定义和成员定义的前面。然而它也出现在package、方法参数、本地变量的前面。下面,我们来讨论一下这些不大常用的写法: 



package annotation出现在package声明的前面。 

下面的例子package-info.java中不包含任何的公共类型定义,却包含一个可选的javadoc注释。 

清单7: 







/** 



* This package holds my custom annotation types. 



*/ 



@com.davidflanagan.annotations.Author(“David Flanagan”) 



package com.davidflanagan.annotations; 





当package -info.java文件被编译时,它将产生名为包含annotation(特殊的接口)声明的package-info.class的类。这个接口没有成员,它的名字package-info不是一个合法的java标识,所以它不能用在java源代码中。这个接口的存在只是简单的被看作一个为 package annotation准备的占位符。 



用于修饰方法参数、catch参数、本地变量的annotation只是简单的出现在这些程序成员的修饰符位置。java类文件格式没有为本地变量或者catch参数存储annotation作准备,所以这些annotation总是保留在源代码级别(source retention);方法参数annotation能够保存在类文件中,也可以在保留到运行时。 



最后,请注意,枚举类型定义中不允许任何的修饰符修饰其枚举值。 



6。Annotation和默认值: 

在Annotation 中,没有默认值的成员必须有一个成员值。而如何理解默认值是如何被处理就是一个很重要的细节:annotation类型所定义的成员默认值被存储在 class文件中,不被编译到annotation里面。如果我们修改一个annotation类型使其成员的默认值发生了改变,这个改变对于所有此类型的annotation中没有明确提供成员值的成员产生影响(即修改了该成员的成员值)。即使在annotation类型使其成员的默认值被改变后 annotation从没被重新编译过,该类型的annotation(改变前已经被编译的)也受到影响。 



三、Annotation工作原理: 



Annotation与反射 

在java5.0 中Java.lang.reflect提供的反射API被扩充了读取运行时annotation的能力。让我们回顾一下前面所讲的:一个 annotation类型被定义为runtime retention后,它才是在运行时可见,当class文件被装载时被保存在class文件中的annotation才会被虚拟机读取。那么 reflect是如何帮助我们访问class中的annotation呢? 



下文将在java.lang.reflect用于 annotation的新特性,其中java.lang.reflect.AnnotatedElement是重要的接口,它代表了提供查询 annotation能力的程序成员。这个接口被java.lang.Package、java.lang.Class实现,并间接地被Method类、 Constructor类、java.lang.reflect的Field类实现。而annotation中的方法参数可以通过Method类、 Constructor类的getParameterAnnotations()方法获得。 



下面的代码使用了AnnotatedElement类的isAnnotationPresent()方法判断某个方法是否具有@Unstable annotation,从而断言此方法是否稳定: 

清单8: 







import java.lang.reflect.*; 







Class c = WhizzBangClass.class;                           



Method m = c.getMethod(“whizzy”, int.class, int.class);  



boolean unstable = m.isAnnotationPresent(Unstable.class); 





isAnnotationPresent ()方法对于检查marker annotation是十分有用的,因为marker annotation没有成员变量,所以我们只要知道class的方法是否使用了annotation修饰就可以了。而当处理具有成员的 annotation时,我们通过使用getAnnotation()方法来获得annotation的成员信息(成员名称、成员值)。这里我们看到了一套优美的java annotation系统:如果annotation存在,那么实现了相应的annotation类型接口的对象将被getAnnotation()方法返回,接着调用定义在annotation类型中的成员方法可以方便地获得任何成员值。 



回想一下,前面介绍的@Reviews annotation,如果这个annotation类型被声明为runtime retention的话,我们通过下面的代码来访问@Reviews annotation的成员值: 

清单9: 







AnnotatedElement target = WhizzBangClass.class; //获得被查询的AnnotatedElement 



// 查询AnnotatedElement的@Reviews annotation信息 



Reviews annotation = target.getAnnotation(Reviews.class); 



// 因为@Reviews annotation类型的成员为@Review annotation类型的数组, 



// 所以下面声明了Review[] reviews保存@Reviews annotation类型的value成员值。 



Review[] reviews = annotation.value(); 



// 查询每个@Review annotation的成员信息 



for(Review r : reviews) { 



    Review.Grade grade = r.grade(); 



    String reviewer = r.reviewer(); 



    String comment = r.comment(); 



    System.out.printf(“%s assigned a grade of %s and comment ‘%s’%n”, 



                      reviewer, grade, comment); 











四、如何自定义Annotation? 



1.详解annotation与接口的异同: 

因为annotation类型是一个非凡的接口,所以两者之间存在着某些差异: 



A.Annotation类型使用关键字@interface而不是interface。 

这个关键字声明隐含了一个信息:它是继承了java.lang.annotation.Annotation接口,并非声明了一个interface。 



B.Annotation类型、方法定义是独特的、受限制的。 

Annotation 类型的方法必须声明为无参数、无异常抛出的。这些方法定义了annotation的成员:方法名成为了成员名,而方法返回值成为了成员的类型。而方法返回值类型必须为primitive类型、Class类型、枚举类型、annotation类型或者由前面类型之一作为元素的一维数组。方法的后面可以使用 default和一个默认数值来声明成员的默认值,null不能作为成员默认值,这与我们在非annotation类型中定义方法有很大不同。 

Annotation类型和它的方法不能使用annotation类型的参数、成员不能是generic。只有返回值类型是Class的方法可以在annotation类型中使用generic,因为此方法能够用类转换将各种类型转换为Class。 



C.Annotation类型又与接口有着近似之处。 

它们可以定义常量、静态成员类型(比如枚举类型定义)。Annotation类型也可以如接口一般被实现或者继承。 



2.实例: 

下面,我们将看到如何定义annotation类型的example。它展示了annotation类型声明以及@interface与interface之间的不同: 

清单10: 







package com.davidflanagan.annotations; 



import java.lang.annotation.*; 







/** 



* 使用annotation来描述那些被标注的成员是不稳定的,需要更改 



*/ 



@Retention(RetentionPolicy.RUNTIME) 



public @interface Unstable {} 







下面的另一个example只定义了一个成员。并通过将这个成员命名为value,使我们可以方便的使用这种annotation的快捷声明方式: 

清单11: 







/** 



* 使用Author这个annotation定义在程序中指出代码的作者 



*/ 



public @interface Author { 



    /** 返回作者名 */ 



    String value(); 











以下的example更加复杂。Reviews annotation类型只有一个成员,但是这个成员的类型是复杂的:由Review annotation组成的数组。Review annotation类型有3个成员:枚举类型成员grade、表示Review名称的字符串类型成员Reviewer、具有默认值的字符串类型成员 Comment。 

清单12: 







import java.lang.annotation.*; 



        



/** 



* Reviews annotation类型只有一个成员, 



* 但是这个成员的类型是复杂的:由Review annotation组成的数组 



*/ 



@Retention(RetentionPolicy.RUNTIME) 



public @interface Reviews { 



    Review[] value(); 











/** 



* Review annotation类型有3个成员: 



* 枚举类型成员grade、 



  * 表示Review名称的字符串类型成员Reviewer、 



  * 具有默认值的字符串类型成员Comment。 



*/ 



public @interface Review { 



    // 内嵌的枚举类型 



    public static enum Grade { EXCELLENT, SATISFACTORY, UNSATISFACTORY }; 







    // 下面的方法定义了annotation的成员 



    Grade grade();                



    String reviewer();          



    String comment() default “”;  











最后,我们来定义一个annotation方法用于罗列出类运行中所有的unchecked异常(上文已经提到这种情况不一定是错误)。这个 annotation类型将一个数组作为了唯一的成员。数组中的每个元素都是异常类。为了加强对未检查的异常(此类异常都是在运行时抛出)进行报告,我们可以在代码中对异常的类型进行限制: 

清单13: 







public @interface UncheckedExceptions { 



    Class<? extends RuntimeException>[] value(); 











五、Meta-Annotation 



Annotation 类型可以被它们自己所标注。Java5.0定义了4个标准的meta-annotation类型,它们被用来提供对其它annotation类型作说明。这些类型和它们所支持的类在java.lang.annotation包中可以找到。如果需要更详细的信息可以参考jdk5.0手册。 



1.再谈Target 

作为meta-annotation类型的Target,它描述了annotation所修饰的程序成员的类型。当一个annotation类型没有 Target时,它将被作为普通的annotation看待。当将它修饰一个特定的程序成员时,它将发挥其应用的作用,例如:Override用于修饰方法时,增加了@Target这个meta-annotation就使编译器对annotation作检查,从而去掉修饰错误类型的Override。 



Target meta-annotation类型有唯一的value作为成员。这个成员的类型是java.lang.annotation.ElementType[]类型的,ElementType类型是可以被标注的程序成员的枚举类型。 



2.Retention的用法 

我们在文章的开头曾经提到过Retention,但是没有详细讲解。Retention描述了annotation是否被编译器丢弃或者保留在class文件;如果保留在class文件中,是否在class文件被装载时被虚拟机读取。默认情况下,annotation被保存在class文件中,但在运行时并不能被反射访问。Retention具有三个取值:source、class、runtime,这些取值来自 java.lang.annotation.RetentionPolicy的枚举类型值。 



Retention meta-annotation类型有唯一的value作为成员,它的取值来自java.lang.annotation.RetentionPolicy的枚举类型值。 



3.Documented 

Documented是一个meta-annotation类型,用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。 



Documented是一个marker annotation,没有成员。 



4.Inherited 

@Inherited meta-annotation也是一个marker annotation,它阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。 



注意:@Inherited annotation类型是被标注过的class的子类所继承。类并不从它所实现的接口继承annotation,方法并不从它所重载的方法继承annotation。 



值得思考的是,当@Inherited annotation类型标注的annotation的Retention是RetentionPolicy.RUNTIME,则反射API增强了这种继承性。如果我们使用java.lang.reflect去查询一个@Inherited annotation类型的annotation时,反射代码检查将展开工作:检查class和其父类,直到发现指定的annotation类型被发现,或者到达类继承结构的顶层。 



六、总结: 



本文几乎覆盖了所有的Annotation的概念和知识点,从annotation的定义、语法到工作原理、如何自定义annotation,直至meta- annotation。其中也具有一些配套的代码片断可参考,虽然不是很多,但是可谓言简意赅、着其重点,本人认为用好annotation的关键还在于使用。希望本手册能够帮助大家用好annotation,这也是本人的最大快乐。 




凡是有该标志的文章,都是该blog博主Caoer(草儿)原创,凡是索引、收藏 、转载请注明来处和原文作者。非常感谢。

来自:http://mxdxm.iteye.com/blog/759607

Java Annotation详解

元数据的作用

如果要对于元数据的作用进行分类,目前还没有明确的定义,不过我们可以根据它所起的作用,大致可分为三类:

l          编写文档:通过代码里标识的元数据生成文档。

l          代码分析:通过代码里标识的元数据对代码进行分析。

l          编译检查:通过代码里标识的元数据让编译器能实现基本的编译检查。

 

基本内置注释

    @Override 注释能实现编译时检查,你可以为你的方法添加该注释,以声明该方法是用于覆盖父类中的方法。如果该方法不是覆盖父类的方法,将会在编译时报错。例如我们为某类重写toString() 方法却写成了tostring() ,并且我们为该方法添加了@Override 注释;

     @Deprecated 的作用是对不应该在使用的方法添加注释,当编程人员使用这些方法时,将会在编译时显示提示信息,它与javadoc 里的 @deprecated 标记有相同的功能,准确的说,它还不如javadoc @deprecated ,因为它不支持参数,

注意:要了解详细信息,请使用 -Xlint:deprecation 重新编译。

    @SuppressWarnings 与前两个注释有所不同,你需要添加一个参数才能正确使用,这些参数值都是已经定义好了的,我们选择性的使用就好了,参数如下:

 

deprecation   使用了过时的类或方法时的警告

unchecked  执行了未检查的转换时的警告,例如当使用集合时没有用泛型 (Generics) 来指定集合保存的类型

fallthrough    Switch 程序块直接通往下一种情况而没有 Break 时的警告

path   在类路径、源文件路径等中有不存在的路径时的警告

serial 当在可序列化的类上缺少 serialVersionUID 定义时的警告

finally    任何 finally 子句不能正常完成时的警告

all 关于以上所有情况的警告

 

注意:要了解详细信息,请使用 -Xlint:unchecked 重新编译。

 

定制注释类型

    好的,让我们创建一个自己的注释类型(annotation type )吧。它类似于新创建一个接口类文件,但为了区分,我们需要将它声明为@interface, 如下例:

public @interface NewAnnotation {

 

}

 

使用定制的注释类型

    我们已经成功地创建好一个注释类型NewAnnotation ,现在让我们来尝试使用它吧,如果你还记得本文的第一部分,那你应该知道他是一个标记注释,使用也很容易,如下例:

public class AnnotationTest {

 

    @NewAnnotation

    public static void main(String[] args) {

   

    }

}

 

添加变量

    J2SE 5.0 里,我们了解到内置注释@SuppressWarnings() 是可以使用参数的,那么自定义注释能不能定义参数个数和类型呢?答案是当然可以,但参数类型只允许为基本类型、String 、Class 、枚举类型等,并且参数不能为空。我们来扩展NewAnnotation ,为之添加一个String 类型的参数,示例代码如下:

public @interface NewAnnotation {

 

    String value();

}

    使用该注释的代码如下:正如你所看到的,该注释的使用有两种写法,这也是在之前的文章里所提到过的。如果你忘了这是怎么回事,那就再去翻翻吧。

public class AnnotationTest {

 

    @NewAnnotation(“Just a Test.”)

    public static void main(String[] args) {

        sayHello();

    }

   

    @NewAnnotation(value=”Hello NUMEN.”)

    public static void sayHello() {

        // do something

    }

}

 

为变量赋默认值

    我们对Java 自定义注释的了解正在不断的增多,不过我们还需要更过,在该条目里我们将了解到如何为变量设置默认值,我们再对NewAnnotaion 进行修改,看看它会变成什么样子,不仅参数多了几个,连类名也变了。但还是很容易理解的,我们先定义一个枚举类型,然后将参数设置为该枚举类型,并赋予默认值。

public @interface Greeting {

 

    public enum FontColor {RED, GREEN, BLUE};

 

    String name();

 

    String content();

   

    FontColor fontColor() default FontColor.BLUE;

}

 

限定注释使用范围

    当我们的自定义注释不断的增多也比较复杂时,就会导致有些开发人员使用错误,主要表现在不该使用该注释的地方使用。为此,Java 提供了一个ElementType 枚举类型来控制每个注释的使用范围,比如说某些注释只能用于普通方法,而不能用于构造函数等。下面是Java 定义的ElementType 枚举:

package java.lang.annotation;

 

public enum ElementType {

  TYPE,         // Class, interface, or enum (but not annotation)

  FIELD,        // Field (including enumerated values)

  METHOD,       // Method (does not include constructors)

  PARAMETER,        // Method parameter

  CONSTRUCTOR,      // Constructor

  LOCAL_VARIABLE,   // Local variable or catch clause

  ANNOTATION_TYPE,  // Annotation Types (meta-annotations)

  PACKAGE       // Java package

}

    下面我们来修改Greeting 注释,为之添加限定范围的语句,这里我们称它为目标(Target )使用方法也很简单,如下:

 

@Target( { ElementType.METHOD, ElementType.CONSTRUCTOR })

public @interface Greeting {

}

正如上面代码所展示的,我们只允许Greeting 注释标注在普通方法和构造函数上,使用在包申明、类名等时,会提示错误信息。

 

注释保持性策略

public enum RetentionPolicy {

  SOURCE,// Annotation is discarded by the compiler

  CLASS,// Annotation is stored in the class file, but ignored by the VM

  RUNTIME// Annotation is stored in the class file and read by the VM

}

    RetentionPolicy 的使用方法与ElementType 类似,简单代码示例如下:

@Retention(RetentionPolicy.RUNTIME)

@Target( { ElementType.METHOD, ElementType.CONSTRUCTOR })

 

文档化功能

    Java 提供的Documented 元注释跟Javadoc 的作用是差不多的,其实它存在的好处是开发人员可以定制Javadoc不支持的文档属性,并在开发中应用。它的使用跟前两个也是一样的,简单代码示例如下:

@Documented

@Retention(RetentionPolicy.RUNTIME)

@Target( { ElementType.METHOD, ElementType.CONSTRUCTOR })

public @interface Greeting {

}

 

值得大家注意的是,如果你要使用@Documented 元注释,你就得为该注释设置RetentionPolicy.RUNTIME 保持性策略。为什么这样做,应该比较容易理解,这里就不提了。

 

标注继承

继承应该是Java 提供的最复杂的一个元注释了,它的作用是控制注释是否会影响到子类,简单代码示例如下:

@Inherited

@Documented

@Retention(RetentionPolicy.RUNTIME)

@Target( { ElementType.METHOD, ElementType.CONSTRUCTOR })

public @interface Greeting {

}

 

读取注释信息

    当我们想读取某个注释信息时,我们是在运行时通过反射来实现的,如果你对元注释还有点印象,那你应该记得我们需要将保持性策略设置为RUNTIME ,也就是说只有注释标记了@Retention(RetentionPolicy.RUNTIME) 的,我们才能通过反射来获得相关信息,下面的例子我们将沿用前面几篇文章中出现的代码,并实现读取AnnotationTest 类所有方法标记的注释并打印到控制台。好了,我们来看看是如何实现的吧:

public class AnnotationIntro {

 

    public static void main(String[] args) throws Exception {

 

        Method[] methods = Class.forName(

                “com.gelc.annotation.demo.customize.AnnotationTest”)

                .getDeclaredMethods();

        Annotation[] annotations;

 

        for (Method method : methods) {

            annotations = method.getAnnotations();

            for (Annotation annotation : annotations) {

                System.out.println(method.getName() + ” : “

                        + annotation.annotationType().getName());

            }

 

********************************************************************************

 

Annotation(注解)

Annotation对于程序运行没有影响,它的目的在于对编译器或分析工具说明程序的某些信息,您可以
在包,类,方法,域成员等加上Annotation.每一个Annotation对应于一个实际的Annotation类型.

1  限定Override父类方法@Override
  java.lang.Override是J2SE5.0中标准的Annotation类型之一,它对编译器说明某个方法必须
  是重写父类中的方法.编译器得知这项信息后,在编译程序时如果发现被@Override标示的方法
  并非重写父类中的方法,就会报告错误.
  例,如果在定义新类时想要重写Object类的toString()方法,可能会写成这样:
  public class CustomClass{
    public String ToString(){
      return “customObject”;
    }
  }
  在编写toString()方法时,因为输入错误或其他的疏忽,将之写成ToString()了,编译这个类时
  并不会出现任何的错误,编译器不会知道您是想重写toString()方法,只会以为是定义了一个新
  的ToString()方法.
  可以使用java.lang.Override这个Annotation类型,在方法上加一个@Override的Annotation
  这可以告诉编译器现在定义的这个方法,必须是重写父类中的同包方法.
  public class CustomClass{
    @Override
    public String toString(){
      return “coustomObject”;
    }
  }
  java.lang.Override是一个Marker Annotation,简单地说就是用于标示的Annotation,Annotation
  名称本身表示了要给工具程序的信息。
  
  Annotation类型与Annotation实际上是有区分的,Annotation是Annotation类型的实例,例如
  @Override是个Annotation,它是java.lang.Override类型的一个实例,一个文件中可以有很多
  个@Override,但它们都是属于java.lang.Override类型。
  
2  标示方法为Deprecated @Deprecated
  java.lang.Deprecated也是J2SE5.0中标准的Annotation类型之一。它对编译器说明某个方法已经不
  建议使用。如果有开发人员试图使用或重写被@Deprecated标示的方法,编译器必须提出警告信息。
  例:
  public class Something{
    @Deprecated
    public Something getSomething(){
      return new Something();
    }
  }
  如果有人试图在继承这个类后重写getSomething()方法,或是在程序中调用getSomething()方法,
  则编译时会有警告出现。
  java.lang.Deprecated也是一个Marker Annotation简单地说就是用于标示。
  
3  抑制编译器警告 @SuppressWarnings
  java.lang.SuppressWarnings也是J2SE5.0中标准的Annotation类型之一,它对编译器说明某个方法
  中若有警告信息,则加以抑制,不用在编译完成后出现警告。
  例:
  public class SomeClass2{
    @SuppressWarnings(value={“unchecked”});
    public void doSomething(){
      Map map = new HashMap();
      map.put(“some”,”thing”);
    }
  }
  这样,编译器将忽略unchecked的警告,您也可以指定忽略多个警告:
  @SuppressWarnings(value={“unchecked”,”deprecation”});
  @SuppressWarnings是所谓的Single-Value Annotation,因为这样的Annotation只有一个成员,称为
  value成员,可在使用Annotation时作额外的信息指定。
  
  
  
自定义Annotation类型
  可以自定义Annotation类型,并使用这些自定义的Annotation类型在程序代码中使用Annotation,这些
  Annotation将提供信息给程序代码分析工具。
  首先来看看如何定义Marker Annotation,也就是Annotation名称本身即提供信息。对于程序分析工具来
  说,主要是检查是否有Marker Annotation的出现,并做出对应的动作。要定义一个Annotation所需的动作
  ,就类似于定义一个接口,只不过使用的是@interface。
  例:
  public @interface Debug{}
  
  由于是一个Marker Annotation,所以没有任何成员在Annotation定义中。编译完成后,就可以在程序代码
  中使用这个Annotation。
  public class SomeObject{
    @Debug
    public void doSomething(){
     ……
    }
  }
  稍后可以看到如何在Java程序中取得Annotation信息(因为要使用Java程序取得信息,所以还要设置
  meta-annotation,稍后会谈到)
  
  接着来看看如何定义一个Single-Value Annotation,它只有一个Value成员。
  例:
  public @interface UnitTest{
    String value();
  }
  实际上定义了value()方法,编译器在编译时会自动产生一个value的域成员,接着在使用UnitTest 
  Annotation时要指定值。如:
  public class MathTool{
   @UnitTest(“GCD”)
   public static int gcdOf(int num1,int num2){
     ……………
   }
  }
  @UnitTest(“GCD”)实际上是@UnitTest(value=”GCD”)的简便写法,value也可以是数组值。如:
  public @interface FunctionTest{
    String[] value();
  }
  在使用时,可以写成@FunctionTest({“method1″,”method2”})这样的简便形式。或是
  @FunctionTest(value={“method1″,”method2”})这样的详细形式.
  
  也可以对value成员设置默认值,使用default关键词即可。
  例:
  public @interface UnitTest2{
    String value() default “noMethod”;
  }
  这样如果使用@UnitTest2时没有指定value值,则value默认就是noMethod.
  
  
  也可以为Annotation定义额外的成员,以提供额外的信息给分析工具,如:
  public @interface Process{
    public enum Current{NONE,REQUIRE,ANALYSIS,DESIGN,SYSTEM};
    Current current() default Current.NONE;
    String tester();
    boolean ok();
  }
  运用:
  public class Application{
    @process(
      current = Process.Current.ANALYSIS,
      tester = “Justin Lin”,
      ok = true
    )
    public void doSomething(){
      ………..
    }
  }
  当使用@interface自行定义Annotation类型时,实际上是自动继承了
  java.lang.annotation接口,并由编译器自动完成其他产生的细节,并且在定义Annotation类型时,
  不能继承其他的Annotation类型或接口.
  定义Annotation类型时也可以使用包机制来管理类。由于范例所设置的包都是onlyfun.caterpillar,
  所以可以直接使用Annotation类型名称而不指定包名,但如果是在别的包下使用这些自定义的Annotation
  ,记得使用import告诉编译器类型的包们置。
  如:
  import onlyfun.caterpillar.Debug;
  public class Test{
    @Debug
    public void doTest(){
      ……
    }
  }
  或是使用完整的Annotation名称.如:
  public class Test{
    @onlyfun.caterpillar.Debug
    public void doTest(){
      ……
    }
  }
  
  
  
meta-annotation
  所谓neta-annotation就是Annotation类型的数据,也就是Annotation类型的Annotation。在定义
  Annotation类型时,为Annotation类型加上Annotation并不奇怪,这可以为处理Annotation类型
  的分析工具提供更多的信息。
  
1  告知编译器如何处理annotation @Retention
  java.lang.annotation.Retention类型可以在您定义Annotation类型时,指示编译器该如何对待自定
  义的Annotation类型,编译器默认会将Annotation信息留在.class文件中,但不被虚拟机读取,而仅用
  于编译器或工具程序运行时提供信息。
  
  在使用Retention类型时,需要提供java.lang.annotation.RetentionPolicy的枚举类型。
  RetentionPolicy的定义如下所示:
  package java.lang.annotation;
  public enum RetentionPolicy{
    SOURCE,//编译器处理完Annotation信息后就没有事了
    CLASS,//编译器将Annotation存储于class文件中,默认
    RUNTIME //编译器将Annotation存储于class文件中,可由VM读入

  }
  
  RetentionPolicy为SOURCE的例子是@SuppressWarnings,这个信息的作用仅在编译时期告知
  编译器来抑制警告,所以不必将这个信息存储在.class文件中。
  
  RetentionPolicy为RUNTIME的时机,可以像是您使用Java设计一个程序代码分析工具,您必须让VM能读出
  Annotation信息,以便在分析程序时使用,搭配反射机制,就可以达到这个目的。
  
  J2SE6.0的java.lang.reflect.AnnotatedElement接口中定义有4个方法:
  public Annotation getAnnotation(Class annotationType)
  public Annotation[] getAnnotations();
  public Annotation[] getDeclaredAnnotations()
  public boolean isAnnotationPresent(Class annotationType);
  
  Class,Constructor,field,Method,Package等类,都实现了AnnotatedElement接口,所以可以从这些
  类的实例上,分别取得标示于其上的Annotation与相关信息。由于在执行时读取Annotation信息,所以定
  义Annotation时必须设置RetentionPolicy为RUNTIME,也就是可以在VM中读取Annotation信息。
  例:
  package onlyfun.caterpillar;
  import java.lang.annotation.Retention;
  import java.lang.annotation.RetentionPllicy;
  
  @Retention(RetentionPolicy.RUNTIME)
  public @interface SomeAnnotation{
    String value();
    String name();
  }
  由于RetentionPolicy为RUNTIME,编译器在处理SomeAnnotation时,会将Annotation及给定的相关信息
  编译至.class文件中,并设置为VM可以读出Annotation信息。接下来:
  package onlyfun.caterpillar;
  public class SomeClass3{
    @SomeAnotation{
      value=”annotation value1″,
      name=”annotation name1″
    }
    public void doSomething(){
      ……
    }
  }
  
  
  现在假设要设计一个源代码分析工具来分析所设计的类,一些分析时所需的信息已经使用Annotation标示于类
  中了,可以在执行时读取这些Annotation的相关信息。例:
  
  package onlyfun.caterpillar;
  
  import java.lang.annotation.Annotation;
  import java.lang.reflect.Method;
  
  public class AnalysisApp{
    public static void main(String [] args) throws NoSuchMethodException{
      Class<SomeClass3> c = SomeClass3.class;
      //因为SomeAnnotation标示于doSomething()方法上
      //所以要取得doSomething()方法的Method实例
      Method method = c.getMethod(“doSomething”);
      //如果SomeAnnotation存在
      if(method.isAnnotationPresent(SomeAnnotation.class){
        System.out.println(“找到@SomeAnnotation”);
        //取得SomeAnnotation
        SomeAnnotation annotation = method.getAnnotation(SomeAnnotation.class);
        //取得vlaue成员值
        System.out.println(annotation.value);
        //取得name成员值
        System.out.println(annotation.name());
      }else{
        System.out.println(“找不到@SomeAnnotation”);
      }
      //取得doSomething()方法上所有的Annotation
      Annotation[] annotations = method.getAnnotations();
      //显示Annotation名称
      for(Annotation annotation : annotations){
        System.out.println(“Annotation名称:”+annotation.annotationType().getName());
      }
    }
  }
  若Annotation标示于方法上,就要取得方法的Method代表实例,同样的,如果Annotation标示于类或包上,
  就要分别取得类的Class代表的实例或是包的Package代表的实例。之后可以使用实例上的getAnnotation()
  等相关方法,以测试是否可取得Annotation或进行其他操作。
  
2  限定annotation使用对象 @Target
  在定义Annotation类型时,使用java.lang.annotation.Target可以定义其适用的时机,在定义时要指定
  java.lang.annotation.ElementType的枚举值之一。
  public enum elementType{
    TYPE,//适用class,interface,enum
    FIELD,//适用于field
    METHOD,//适用于method
    PARAMETER,//适用method上之parameter
    CONSTRUCTOR,//适用constructor
    LOCAL_VARIABLE,//适用于区域变量
    ANNOTATION_TYPE,//适用于annotation类型
    PACKAGE,//适用于package
  }
  
  举例,假设定义Annotation类型时,要限定它只能适用于构造函数与方法成员,则:
  package onlyfun.caterpillar;
  
  import java.lang.annotation.Target;
  import java.lang.annotation.ElementType;
  
  @Target({ElementType.CONSTRUCTOR,ElementType.METHOD})
  public @interface MethodAnnotation{}
  
  将MethodAnnotation标示于方法之上,如:
  
  public class SomeoneClass{
    @onlyfun.caterpillar.MethodAnnotation
    public void doSomething(){
      ……
    }
  }
  
3  要求为API文件的一部分 @Documented
 
  在制作Java Doc文件时,并不会默认将Annotation的数据加入到文件中.Annnotation用于标示程序代码以便
  分析工具使用相关信息,有时Annotation包括了重要的信息,您也许会想要在用户制作Java Doc文件的同时,
  也一并将Annotation的信息加入到API文件中。所以在定义Annotation类型时,可以使用
  java.lang.annotation.Documented.例:
  
  package onlyfun.caterpillar;
  
  import java.lang.annotation.Documented;
  import java.lang.annotation.Retention;
  import java.lang.annotation.RetentionPolicy;
  
  @Documented
  @Retention(RetentionPolicy.RUNTIME)
  public @interface TwoAnnotation{}
  
  使用java.lang.annotation.Documented为定义的Annotation类型加上Annotation时,必须同时使用Retention
  来指定编译器将信息加入.class文件,并可以由VM读取,也就是要设置RetentionPolicy为RUNTIME。接着可以使
  用这个Annotation,并产生Java Doc文件,这样可以看到文件中包括了@TwoAnnotation的信息.
  
4  子类是否可以继承父类的annotation @Inherited
  在定义Annotation类型并使用于程序代码上后,默认父类中的Annotation并不会被继承到子类中。可以在定义
  Annotation类型时加上java.lang.annotation.Inherited类型的Annotation,这让您定义的Annotation类型在
  被继承后仍可以保留至子类中。
  例:
  package onlyfun.caterpillar;
  
  import java.lang.annotation.Retention;
  import java.lang.annotation.RetentionPolicy;
  import java.lang.annotation.Inherited;
  
  @Retention(RetentionPolicy.RUNTIME)
  @Inherited
  public @interface ThreeAnnotation{
    String value();
    String name();
  }
  可以在下面的程序中使用@ThreeAnnotation:
  public class SomeoneClass{
    @onlyfun.caterpillar.ThreeAnnotation(
      value = “unit”,
      name = “debug1”
    )
    public void doSomething(){
      …..
    }
  }
  如果有一个类继承了SomeoneClass类,则@ThreeAnnotation也会被继承下来。

来自:http://djjchobits.iteye.com/blog/569000

类找不到总结java.lang.ClassNotFoundException

(1)org.apache.tomcat.dbcp.dbcp.SQLNestedException: Cannot load JDBC driver class

‘com.microsoft.sqlserver.jdbc.SQLServerDriver’

     答:sqljdbc.jar

(2)java.lang.ClassNotFoundException: org.springframework.ejb.config.JeeNamespaceHandler

    答:spring-remoting.jar

(3)java.lang.ClassNotFoundException: org.springframework.scripting.config.LangNamespaceHandler   

    答:spring-support.jar

(4)java.lang.ClassNotFoundException: org.springframework.transaction.config.TxNamespaceHandler  

    答:spring-dao.jar

(5)java.lang.ClassNotFoundException: org.springframework.aop.config.AopNamespaceHandler

  答:spring-aop.jar

(6)java.lang.NoClassDefFoundError: org/apache/commons/collections/SequencedHashMap

  答:commons-collections-2.1.1.jar

(7)java.lang.ClassNotFoundException:org.springframework.scripting.config.LangNamespaceHandler

 答:添加spring-support.jar包,具体路径为:MyEclipse 6.0myeclipseeclipsepluginscom.genuitec.eclipse.springframework_6.0.1.zmyeclipse601200710data2.0distmodules
(8)java.lang.NoClassDefFoundError: org/apache/commons/logging/LogFactory

 答:commons-logging.jar

(9)java.lang.NoClassDefFoundError: org/aopalliance/intercept/MethodInterceptor

答:aopalliance.jar

(10)java.lang.NoClassDefFoundError:org/springframework/remoting/support/RemoteInvocationTraceInterceptor

答:org.springframework.context-3.0.5.RELEASE.jar 包

(11)java.lang.NoClassDefFoundError: org/aopalliance/aop/Advice

答:com.springsource.org.aopalliance-1.0.0.jar包


本文来自:http://hi.baidu.com/jingbaobei2979/blog/item/e3874ef8d23f6b41252df2d3.html