java 注解学习总结

Annotation(注解)是 Java 1.5 中添加的特性,很多 java 框架都依赖注解来实现代码级配置,相比 XML 配置更简洁。要理解框架的运行原理,注解是一个基础的概念。

注解的基本概念

网上、书本对注解的基本概念已经进行了很详细的讲解,这里不再重述,例如:

《Java核心技术 卷2 10.3,10.4,10.5》对注解进行了全面的讲解,但翻译的文字晦涩 http://www.importnew.com/10294.html 讲解了注解的基本概念,特别是介绍了注解的历史 http://www.importnew.com/11908.html 对java8中对注解的改进进行了介绍

从高层次来讲,注解是与代码紧耦合的元数据;从语法上来看,注解是一种特殊的接口。

注解的生存期与注解的处理

注解的两个元注解@Target@Retention定义了注解的使用目标和生存期。注解的生存期在java.lang.annotation.RetentionPolicy中进行了枚举,包括:

  • SOURCE 存在源码中,编译阶段被丢弃
  • CLASS 默认级别,存在类文件中,VM 加载时将丢弃
  • RUNTIME 运行时,存在于运行时,可以通过反射 API 获取

而处理注解的方式通常为以下几种情况:

  • 使用javax.annotation.processing处理器 API,在编译前对注解进行处理,所有生存期的注解都能被处理;
  • 使用字节码处理框架如 bcel 对 class 文件进行读取并处理其中的注解,此时 SOURCE 级注解已经不存在;
  • 使用java.lang.instrument设备 API,获得 class 文件并处理其中的注解,此时 SOURCE 级注解已经不存在;
  • 使用java.lang.annotationAPI,获取反射对象的注解并处理,此时 SOURCE&CLASS 级注解已经不存在。这是最常用的方式。

《Java 核心技术 卷 2》第 10 章节对上面方式都进行了介绍和基本实现。

元注解 @Inherited

如果注解类型使用了@Inherited,声明该注解的父类会将其注解继承给子类。

下面用例子说明:
首先是注解类型Country用来声明人所属国家。

@Inherited @Retention(RetentionPolicy.RUNTIME)// 因为是运行时测试,所以需要让注解在运行时保留 public @interface Country { String name(); }

然后是Chinese表示中国人,其所属国家为China

/** * 中国人 */ @Country(name = "China") public class Chinese { }

子类SiChuanese表示四川人。

/** * 四川人 */ public class SiChuanese extends Chinese { }

测试SiChuanese是否从其父类Chinese继承了注解Country

public class CountryTest { @Test public void testCountry(){ Assert.assertEquals("China",SiChuanese.class.getAnnotation(Country.class).name());// 测试通过 } }

单值注解

可能好奇使用 Spring 框架时为什么当只需要一个元素时,可以省略元素名。如在 Spring 中:

@RequestMapping("/index")

这其实是注解的语法部分,叫做单值注解,特殊的value元素,当只指定该元素值时,可以省略元素名和等号。

参考《Java 核心技术 卷 2》 744 页

其实质等于:

@RequestMapping(value="/index")

又因为 Spring 使用自定义注解@AliasFor声明了注解别名。因此等价于下面的注解:

@RequestMapping(name="/index")