今天稍稍学习了下注解,关于注解,我想大家都不陌生,目前可能在hibernate配置中可能会用的多一点,在JPA中也会用到。我想说的是学习注解可以帮助我们更好的理解注解在框架中(比如hibernate和Spirng等)的应用。
Annotation是JDK5版本以来的新特性。目前在JavaSE中的学习中可能会经常遇到集合未指定泛型、使用java.util.Date类中的过时方法,编译器给出warning时,均可以采用增加注解如(SuppressWarnings("unchecked")),Deprecated等来消除警告,这样看起来会好看些。
好,下面开始进入重点学习,首先做下说明,学习Annotation时最好先了解下JDK的反射机制,关于JDK反射机制的学习,大家可以参考我之前发表的两篇博客(1. 反射初探:http://xiaoyun34286136.iteye.com/blog/190423, 2. 使用反射对类的私有方法和私有属性进行调用和修改:http://xiaoyun34286136.iteye.com/blog/1908554)。
学习Annotation涉及到的java类包含:java.lang.annotation.Annotation。所有定义的annotation均默认实现了Annotation接口,但是要注意的是,实现Annotation接口的类并不是annotation。
java中的annotation中包含的注解类型有Documented,Inherited,Retention,Target,其中常用的有Inherited(子类可以继承父类的注解),Retention(注解类型要保留多久,可否利用反射机制调用等),Target(注解的作用对象,类,接口,方法,属性等)。定义注解的关键字为@interface,关于JDK1.6自带的注解比如SuppressWarnings("unchecked")),Deprecated等我就不介绍了,相信有一定基础的都遇到过。
相信在诸如hibernate和spring等框架中,我相信这些框架是使用反射读取的注解及注解中的参数,根据这些注解和参数通过一定的逻辑来对业务进行控制的。
下面开始自定义一个注解并讲解使用,具体步骤为:
定义注解->写测试类->进行测试
一、定义注解:
注解一:AnnotationTest
@Retention(RetentionPolicy.RUNTIME) // 将Retention的值设置为RetentionPolicy.RUNTIME,这样就可以在程序运行期间可以用反射对注解进行解析,设置为CLASS何SOURCE均不能
public @interface AnnotationTest {
String value() default "good";
String value1() default "hello";
EnumTest value2(); //枚举类型
}
/**
* 自定义枚举类
*
* @author XiaoYun 2013-07-20
*/
enum EnumTest {
HELLO, world, welcome
}
注解二:
MyAnnotation.java
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* 自定义注解
*
* @author XiaoYun 2013-07-20
*/
@Retention(RetentionPolicy.CLASS)//将Retention的值设置为RetentionPolicy.CLASS,程序运行的时候就不能通过反射解析
public @interface MyAnnotation {
String value() default "hello";
String hello();
String world();
}
测试类(这里为了少写一个类,就把测试方法和类放到了一个类中):MyTest.java
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
@AnnotationTest(value1="hello", value2=EnumTest.world)
@MyAnnotation(world="world", hello="hello")
public class MyTest {
@MyAnnotation(world="world", hello="hello")
private String field;
@AnnotationTest(value1="hello", value2=EnumTest.world)
@MyAnnotation(value="xiaoyun", hello="hello", world="world")
private void method() {
System.out.println("field的值为:" + field + "\nmethod from MyTest!");
}
@Override
public String toString() {
return "MyTest [field=" + field + "]";
}
/**
* 测试方法
* @param args
*/
public static void main(String[] args) throws Exception {
// 实例化对象
MyTest test = new MyTest();
// 获取Class类型
Class cls = MyTest.class;
// 根据方法名获取方法
Method method = cls.getDeclaredMethod("method", null);
// 获取属性
Field field = cls.getDeclaredField("field");
// 判断如果该方法中包含有MyAnnotation类型的注解,则进行如下操作
if (method.isAnnotationPresent(AnnotationTest.class)) {
// 取消Java访问性检查
field.setAccessible(true);
method.setAccessible(true);
// 给test对象上的field属性赋值,并执行method方法
field.set(test, "我是肖云,大家好!");
method.invoke(test, null);
// 根据注解类型获取特定的注解并打印到控制台
Annotation annotation = method.getAnnotation(AnnotationTest.class);
System.out.println(annotation.annotationType() + "\n" + annotation.annotationType().getName());
}
// 获取所有的注解并逐次迭代(这需要注解中Retention中的value为RetentionPolicy.Runntime)
Annotation[] annotations = method.getAnnotations();
if (annotations.length > 0) {
System.out.println("------------打印所有注解--------------");
for (Annotation annotation : annotations) {
System.out.println(annotation.annotationType().getCanonicalName());
}
}
}
}
上面一个例子主要说明的是java.lang.annotation.Annotation接口中的Retention属性(JVM是否保留注释,是否可用反射读取)TargetAnnotationDemo ,接下来第二个例子用到了剩下的三个属性(Documented(不是重点,生成API文档时用),Target,Inherited),下面开始介绍:
首先还是定义注解:
TargetAnnotationDemo.java
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;
/**
* @Target注解
*
* @author XiaoYun 2013-07-20
*/
@Target({ElementType.TYPE, ElementType.METHOD}) //程序类型的元素有类、接口、枚举类型;方法
@Retention(RetentionPolicy.RUNTIME) //运行时可用反射调用
@Inherited //子类可以继承父类的注解
@Documented //生成doc文档时显示该注解
public @interface TargetAnnotationDemo {
String value() default "hello";
String value1();
}
第二步,写测试用例:
@TargetAnnotationDemo(value1 = "good")
public class TargetTest {
@TargetAnnotationDemo(value1 = "good")
public int add(int a, int b) {
return a + b;
}
}
/**
*子类,继承自TargetTest ,同时继承父类的TargetAnnotationDemo
*@author xiaoyun 2013-07-21
*/
class TargetTestChild extends TargetTest {
@Override
public int add(int a, int b) {
// TODO Auto-generated method stub
return super.add(a, b);
}
}
第三步:
写测试类,TestTarget.java
import java.lang.annotation.Annotation;
public class TestTarget {
public static void main(String[] args) {
//实例化父类
TargetTest parent = new TargetTest();
//实例化子类
TargetTestChild child = new TargetTestChild();
//获取父类的类型
Class cls = TargetTest.class;
//获取子类的类型
Class childCls = TargetTestChild.class;
//获取父类的注解
Annotation[] pAnnotations = cls.getAnnotations();
if (pAnnotations.length > 0) {
System.out.println("父类的注解:");
for (Annotation annotation : pAnnotations) {
System.out.println(annotation.annotationType().getName());
}
}
//获取子类的注解
Annotation[] cAnnotations = childCls.getAnnotations();
if (cAnnotations.length > 0) {
System.out.println("子类的注解:");
for (Annotation annotation : cAnnotations) {
System.out.println(annotation.annotationType().getName());
}
}
}
}
该例子主要用到了java.lang.annotation.Annotation中的所有注解类型。对于Retention,Target,Inherited和Documented这是个注解类型,需要重点掌握前三个,第四个很少用到。
这上面的两个例子都能正常运行,我的环境依然是,JDK1.6。对于生成doc文档的问题,你也可以通过javadoc命令来生成,但是如果你的开发工具时myeclipse8.6的话,可以通过project->Generate Javadoc来生成doc文档,通过去掉和加上@Documented注解类型来观察在文档中是否有对应的说明(当然去掉或加上后需要重新生成下)。
这次先写到这里,关于注解在程序和框架中的应用案例,后面等我学习后会进行更新和发表,谢谢各位同行关注。
欢迎各位同行和前辈们提出问题,对程序或者我组织的语言进行批评指导。