Java Annotation特性与使用方式
一、为什么使用Annotation
在JAVA应用中,我们常遇到一些需要使用模版代码的情况。例如,为了编写一个 web service,我们必须提供一对接口和实现作为模版代码。如果使用annotation对远
程访问的方法代码进行修饰的话,这个模版就能够使用工具自动生成。
另外,一些API需要使用与程序代码同时维护的附属文件。例如EJB需要一个部署描述符。此时在程序中使用annotation来维护这些附属文件的信息将十分便利而且减少
了错误。
二、Annotation工作方式(应知道如何定义,如何使用)
从Java5.0版发布以来,5.0平台提供了一个正式的annotation功能:允许开发者定义、使用自己的annotation类型。此功能由一个定义annotation类型的语法和一个描
述annotation声明的语法,读取annotation的API,一个使用annotation修饰的class文件,一个annotation处理工具(apt)组成。
annotation并不直接影响代码语义,但是它能够工作的方式被看作类似程序的工具或者类库,它会反过来对正在运行的程序语义有所影响。annotation可以从源文件、
class文件或者以在运行时反射的多种方式被读取。
当然annotation在某种程度上使javadoc tag更加完整。一般情况下,如果这个标记对java文档产生影响或者用于生成java文档的话,它应该作为一个javadoc tag;否
则将作为一个annotation。
三、使用JDK5内建Annotation
参见程序实例
Override
Deprecated
SuppressWarnings
1、Override重写(子类、父类间关系)而overload(重载,方法间)
Override强制的保障已经覆盖了父类的方法, 如此处toString()大写成ToString(),它就会提示你出错了。
public class OverrideTest
{
@Override
public String toString()
{
return "This is override";
}
public static void main(String[] args)
{
OverrideTest test = new OverrideTest();
System.out.println(test.toString());
}
}
2、Deprecated
被@Deprecated标示的方法是一个不建议被使用的方法
DeprecatedTest.java,申明作者以前定义的方法doSomething可能会有问题,不介意使用了!
当你再去使用或者再从该类去继承都会出现警告信息!
public class DeprecatedTest
{
@Deprecated
public void doSomething()
{
System.out.println("do something");
}
public static void main(String[] args)
{
DeprecatedTest test = new DeprecatedTest();
test.doSomething();
}
}
3、SuppressWarnings
import java.util.Date;
import java.util.Map;
import java.util.TreeMap;
public class SuppressWarningsTest
{//仅去掉集合的警告如下:@SuppressWarnings("unchecked")
//以下表示压制住两个类型的警告
@SuppressWarnings({"unchecked","deprecation"})
public static void main(String[] args)
{//没有用泛型,在jdk1.5,会出现警告!
Map map = new TreeMap();
map.put("hello",new Date());
System.out.println(map.get("hello"));
DeprecatedTest test = new DeprecatedTest();
test.doSomething();
}
}
四、自定义Annotation类型
使用@interface自行定义Annotation型态时,实际上是自动继承了java.lang.annotation.Annotation接口;;如果我们手工地显示地区定义一个接口public interface test
extents Annotation,这个test接口不是Annotation。
它要隐式地为你实现,由编译程序自动为您完成其它产生的细节
在定义Annotation型态时,不能继承其它的Annotation型态或是接口
public @interface AnnotationTest//这个后面不能显示地再去继承别的接口
{
//在Annotation类中定义属性方法,需要多一个小括号
//例如:String value();
//然后在使用的地方需要对这个value赋值,如在别的 类中需要@AnnotationTest(value="hello world")
//value是默认表述,可以省略@AnnotationTest("hello world"),但如果改成value1必须显示申明
String value();
}
@AnnotationTest("hello world")
public class AnnotationUsage
{
public void method()
{
System.out.println("usage of annotation");
}
public static void main(String[] args)
{
AnnotationUsage usage = new AnnotationUsage();
usage.method();
}
}
扩展以上两个例子
package com.test;
public @interface AnnotationTest
{
//在Annotation类中定义属性方法,需要多一个小括号
//例如:String value();
//然后在使用的地方需要对这个value赋值,如在别的 类中需要@AnnotationTest(value="hello world")
//value是默认表述,可以省略@AnnotationTest("hello world"),但如果改成value1必须显示申明
String value() default "ok";//用defaulf关键字可以定义默认值
//枚举类型的value1,默认值是hello
//EnumTest value1() default EnumTest.Hello;
//String[] value2();
}
enum EnumTest
{
Hello, World, Welcome;
}
package com.langsin.test;
@com.test.AnnotationTest(value="hello world")//或者可以导入包import com.test然后写成AnnotationTest(value="hello world")//@AnnotationTest(value1="EnumTest.World")
//@AnnotationTest(value2={"hello","world"})
public class AnnotationUsage
{
public void method()
{
System.out.println("usage of annotation");
}
public static void main(String[] args)
{
AnnotationUsage usage = new AnnotationUsage();
usage.method();
}
}
五、告知编译程序如何处理@Retention
java.lang.annotation.Retention型态可以在您定义Annotation型态时,指示编译程序该如何对待您的自定义的Annotation型态
预设上编译程序会将Annotation信息留在.class档案中,但不被虚拟机读取,而仅用于编译程序或工具程序运行时提供信息
在使用Retention型态时,需要提供java.lang.annotation.RetentionPolicy的枚举型态{}中为它的三个级别!
package java.lang.annotation;
public enum RetentionPolicy
{
SOURCE, //编译程序处理完Annotation信息后就完成任务(被编译程序废弃)
CLASS, //编译程序将Annotation储存于class档中,缺省
RUNTIME //编译程序将Annotation储存于class檔中,可由jVM读入(可以通过反射方式读取注解相关信息)
}
RetentionPolicy为SOURCE的例子是@SuppressWarnings
仅在编译时期告知编译程序来抑制警告,所以不必将这个信息储存于.class档案
RetentionPolicy为RUNTIME的时机,可以像是您使用Java设计一个程序代码分析工具,您必须让VM能读出Annotation信息,以便在分析程序时使用
搭配反射(Reflection)机制,就可以达到这个目的
如何使用?以下程序为演示程序
演示程序
1、Annotation类的定义中不能定义日期类变量、也不能定义二维数组。
2、AnnotatedElement接口介绍
<T extends Annotation> T getAnnotation(Class<T> annotationType)
Returns this element's annotation for the specified type if such an annotation is present, else null.
Annotation[] getAnnotations() Returns all annotations present on this element.
Annotation[] getDeclaredAnnotations()
Returns all annotations that are directly present on this element.(即不包括父类)
boolean isAnnotationPresent(Class<? extends Annotation> annotationType)
Returns true if an annotation for the specified type is present on this element, else false.
3、程序介绍
package com.langsin.annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation
{
String hello() default "langsin";
String world();
}
package com.langsin.annotation;
@MyAnnotation(hello = "beijing", world = "shanghai")
public class MyTest
{
@MyAnnotation(hello = "beijing", world = "shanghai")
@Deprecated
@SuppressWarnings("unchecked")
public void output()
{
System.out.println("output something");
}
}
package com.langsin.annotation;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
public class MyReflection
{
public static void main(String[] args) throws Exception
{
MyTest myTest = new MyTest();
Class<MyTest> c = MyTest.class;
Method method = c.getMethod("output",new Class[]{});
if(method.isAnnotationPresent(MyAnnotation.class))
{
method.invoke(myTest,new Object[]{});
//以下方法的具体含义可以参见AnnotatedElement接口介绍的具体介绍!
MyAnnotation myAnnotation = method.getAnnotation(MyAnnotation.class);
//myAnnotation.hello();这句话的意思是此时的MyAnnotation的属性需要按按类似方法的式样进行调用。
String hello = myAnnotation.hello();
String world = myAnnotation.world();
System.out.println(hello);
System.out.println(world);
}
Annotation[] annotations = method.getAnnotations();
for(Annotation annotation : annotations)
{
System.out.println(annotation.annotationType().getName());
}
}
}
/*
* 运行结果
output something
beijing
shanghai
com.langsin.annotation.MyAnnotation
java.lang.Deprecated
说明:为什么运行结果中没有java.lang.SuppressWarnings。因为在jdk文档中我们可以发现SuppressWarnings的定义中为@Retention(value=SOURCE)
而 SOURCE, //编译程序处理完Annotation信息后就完成任务(被编译程序废弃)
CLASS, //编译程序将Annotation储存于class档中,缺省
RUNTIME //编译程序将Annotation储存于class檔中,可由jVM读入(可以通过反射方式读取注解相关信息)
也就是说只有当@Retention的value值为RUNTIME时才会被虚拟机运行时加载!
*/
六、限定annotation使用对象@Target
1、使用java.lang.annotation.Target可以定义其使用之时机
在定义时要指定java.lang.annotation.ElementType的枚举值之一
package java.lang.annotation;
public enum ElementType
{
TYPE, //适用class, interface, enum
FIELD, //适用field
METHOD, //适用method
PARAMETER, //适用method上之parameter
CONSTRUCTOR, //适用constructor
LOCAL_VARIABLE, //适用局部变量
ANNOTATION_TYPE, //适用annotation型态
PACKAGE //适用package
}
2、程序举例
package com.langsin.annotation;
import java.lang.annotation.*;
@Target(ElementType.METHOD)
//定义完以上@Target(ElementType.METHOD),以后MyTarget这个Annotation只能用在方法上了!
public @interface MyTarget
{
String value();
}
package com.langsin.annotation;
public class MyTargetTest
{
@MyTarget("xyz")
public void doSomething()
{
System.out.println("hello world");
}
}
七、要求为API文件@Documented
1、想要在使用者制作JavaDoc文件的同时,也一并将Annotation的讯息加入至API文件中
使用java.lang.annotation.Documented
2、程序举例
package com.langsin.annotation;
import java.lang.annotation.Documented;
@Documented//这句话的意思是生成doc文档时,包括annotation的信息。
public @interface DocumentedAnnotation
{
String hello();
}
package com.langsin.annotation;
public class DocumentedTest
{
/**
* This is the comments that I have added
*/
@DocumentedAnnotation(hello = "welcome")
public void method()
{
System.out.println("hello world");
}
}
八、子类是否继承父类@Inherited
1、预设上父类别中的Annotation并不会被继承至子类别中
可以在定义Annotation型态时加上java.lang.annotation.Inherited型态的Annotation
2、程序举例
package com.langsin.annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
@Inherited//这个注解决定了使用这个Annotation的类,子类可以继承他的Annotation!
//但是这个注解只能在类的级别上才能起作用,若是实现接口层次上,不会被继承
public @interface InheritedTest
{
String value();
}
package com.langsin.annotation;
@InheritedTest("langsin")
public class Parent
{
public void doSomething()
{
System.out.println("hello");
}
}
package com.langsin.annotation;
public class Child extends Parent
{
}
package com.langsin.annotation;
public class Test
{
public static void main(String[] args)
{
Class<Child> c = Child.class;
if(c.isAnnotationPresent(InheritedTest.class))
{
InheritedTest inheritedTest = c.getAnnotation(InheritedTest.class);
String value = inheritedTest.value();
System.out.println(value);
}
}
}
附:项目组总结的annotation的应用。 Annotation -Junit,Spring,Hibernate应用.rar