Java注解的用处和使用

Java 注解(Annotation)又称为 Java 标注,是 JDK5.0 引入的一种注释机制。
Java 语言中的类、方法、变量、参数和包等都可以被标注。和 Javadoc 不同,Java 标注可以通过反射获取标注内容。在编译器生成类文件时,标注可以被嵌入到字节码中。Java 虚拟机可以保留标注内容,在运行时可以获取到标注内容 (通过 解析注解 来使用这些数据)。 它也支持自定义 Java 标注。

Annotation 的作用

Annotation 是一个辅助类,它在 Junit、Struts、Spring 等工具框架中被广泛使用。

常见的作用有以下几种:

  1. 生成文档。这是最常见的,也是java 最早提供的注解。常用的有@see @param @return 等;
  2. 在编译时进行格式检查。如@Override放在方法前,如果你这个方法并不是覆盖了超类方法,则编译时就能检查出;
  3. 跟踪代码依赖性,实现替代配置文件功能。比较常见的是spring 2.5 开始的基于注解配置。作用就是减少配置。现在的框架基本都使用了这种配置来减少配置文件的数量;
  4. 在反射的 Class, Method, Field 等函数中,有许多于 Annotation 相关的接口。可以在反射中解析并使用 Annotation。

内置的注解

Java 定义了一套注解,共有 7 个,3 个在 java.lang 中,剩下 4 个在 java.lang.annotation 中。

作用在代码的注解是:

  • @Override - 检查该方法是否是重载方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。
  • @Deprecated - 标记过时方法。如果使用该方法,会报编译警告。
  • @SuppressWarnings - 指示编译器去忽略注解中声明的警告。

元注解是java API提供的,是用于修饰注解的注解,通常用在注解的定义上。四个元注解:

  • @Target:注解的作用目标;
  • @Retention:注解的生命周期;
  • @Documented:将此注解包含在 javadoc 中 ,它代表着此注解会被javadoc工具提取成文档。在doc文档中的内容会因为此注解的信息内容不同而不同;
  • @Inherited:是否允许子类继承该注解。

@Target 用于指明被修饰的注解最终可以作用的目标是谁,也就是指明,你的注解到底是用来修饰方法的?修饰类的?还是用来修饰字段属性的。
可能的值在枚举类 ElemenetType 中,包括:

  • ElementType.TYPE:允许被修饰的注解作用在类、接口和枚举上;
  • ElementType.FIELD:允许作用在属性字段上;
  • ElementType.METHOD:允许作用在方法上;
  • ElementType.PARAMETER:允许作用在方法参数上;
  • ElementType.CONSTRUCTOR:允许作用在构造器上;
  • ElementType.LOCAL_VARIABLE:允许作用在本地局部变量上;
  • ElementType.ANNOTATION_TYPE:允许作用在注解上;
  • ElementType.PACKAGE:允许作用在包上。

@Retention 表示在什么级别保存该注解信息。可选的参数值在枚举类型 RetentionPolicy 中,包括:

  • RetentionPolicy.SOURCE:当前注解编译期可见,不会写入 class 文件;
  • RetentionPolicy.CLASS:类加载阶段丢弃,会写入 class 文件;
  • RetentionPolicy.RUNTIME:将在运行期也保留,可以反射获取。

使用Annotation

要获取类、方法和字段的注解信息,必须通过类的Class对象来获取 Annotation对象,除此之外没有别的获取注解对象的方法。

获取Class对象有三种方式:

  1. 通过类的静态成员表示。每个类都有一个隐含的静态成员class,如下:
Class c1 = MyClass.class;

2.通过类对象的 getClass() 方法。由方法1不难理解,既然存在静态变量,那么通过对象的 getter 方法,就可以获取静态成员class:

Class c2 = myClass1.getClass();

3.通过 Class 类的静态方法 forName() 方法获取 Class 的对象。区别于通过 new 创建对象(编译时静态加载),在开发时如果我们需要动态加载我们的功能模块,该方法可以帮助我们实现在程序运行时类的动态加载。

try {
    //注意,forName()需要传入类的全路径
    //如果当前类与参数类在同一包下即可省略包名
    Class c3 = Class.forName("custom.MyClass");
} catch (ClassNotFoundException e) {
    e.printStackTrace();
}

得到类的Class对象后可以通过getAnnotation方法得到注解:

 MyClassAnnotation myClassAnnotation = clazz.getAnnotation(MyClassAnnotation.class);

Spring动态加载类

在Spring中可通过ApplicationContext容器来获取Bean。调用getBeansOfType方法可动态加载,获得指定类型的所有Bean对象。该方法返回一个Map类型的实例,Map中的key为Bean的名,key对应的内容为Bean的实例。

Map beans = context.getBeansOfType(MyClass.class);

这种方式可通过公共接口实现动态代理。得到的Bean是代理对象,它和Bean里写的实现类没有关系, 只是把代理类接口中方法指向了实现类中的方法。这时如果通过代理对象的getClass()方法取Class对象将不会得到注解数据。

解决方法是通过toString()方法获得实现类的类名,然后用Class.forName()得到实现类的Class对象。

Class clazz = Class.forName(bean.toString().substring(0,bean.toString().indexOf("@")));

toString()方法是Object类里的一个实例方法,所有java类都是Object类的子类,因此所有java对象都具有toString方法。代理类继承了Proxy类,然后实现了Object里面的toString方法,所以输出的是实现类的类名。

toString方法是一个”自我描述“方法,该方法默认返回该对象实现类的类名+@+hashCode值。可以通过字符串截取得到实现类的类名。

你可能感兴趣的:(Java注解的用处和使用)