`

Java自定义注解(原理和API)初探

阅读更多

    今天稍稍学习了下注解,关于注解,我想大家都不陌生,目前可能在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注解类型来观察在文档中是否有对应的说明(当然去掉或加上后需要重新生成下)。

   这次先写到这里,关于注解在程序和框架中的应用案例,后面等我学习后会进行更新和发表,谢谢各位同行关注。

    欢迎各位同行和前辈们提出问题,对程序或者我组织的语言进行批评指导。

 

 

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics