Java注解编程指南
Java Annotation Tutorial
+1概念
+1.1Java语言指南解释
注解(也被称做元数据),为我们提供了程序代码的描述信息,而这些描述信息并不属于程序本身。注解并不直接
影响其注释的代码的工作。
+1.2Java编程思想解释
注解(也被称做元数据),为我们在代码中添加信息提供了一种形式化的方法,使我们可以再稍后的某个时刻非常方便
的使用这些数据。
+1.3Java语言规范解释
注解是一个包含注解类型和零或多个键-值对的Java修饰符。其中每个键-值对都绑定了一个不同的注解类型值。注解的
目标简单的说就是绑定信息到其标注的编程元素。
+2用途
注解的用途包括(但不限于)如下:
- 为编译器提供信息:注解可以被编译器(如Eclipse、IntelliJ)用来检测错误和已知警告,如Java内置的@Deprecated、@Override和@SuppressWarnnings就被经常用到。
- 编译时和部署时附加操作:软件工具可以在编译时或部署时根据注解信息生成可执行代码,XML文件等等。如生成说明文档,Jax-ws,Jax-rs使用注解生成服务描述的XML文件。
-
运行时操作:一些注解可以在程序运行时再去进行解释。如下
- Hibernate和Ibatis使用它做动态表结构和SQL语句生成等操作
- Junit从4.x版本开始使用注解简化测试代码和流程
- AspectJ使用注解提供非侵入式的面向切面编程
- Spring也使用注解做XML配置管理的一种配合或替代
- 有些工具根据特定的注解做代码分析等等
注解很多时候的确能帮助我们减少、美化代码,或为现有代码增加额外功能等。
- Hibernate和Ibatis使用它做动态表结构和SQL语句生成等操作
- Junit从4.x版本开始使用注解简化测试代码和流程
- AspectJ使用注解提供非侵入式的面向切面编程
- Spring也使用注解做XML配置管理的一种配合或替代
- 有些工具根据特定的注解做代码分析等等
+3原理
像文章开头提到的注解和类、接口一样是一种类型。注解处理程序通过Java的反射机获取程序代码中的注解,然后根据预先设定的处理规则解析处理相关注解以达到主机本身设定的功能目标。
+4语法
+4.1定义
注解的定义和接口的定义相似,又有些不同。以下是定义示例
1.无参数注解
/**java.lang.Deprecated源代码*/ @Documented //将此注解包含在JavaDoc中 @Retention(RetentionPolicy.RUNTIME)//VM运行期仍保留该注解,可通过反射获得。 @Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})//此注解适用的目标(构造方法、字段、本地变量、方法、包、参数、类型) public @interface Deprecated { //"Deprecated" 为注解名称 //注解均使用@interface声明 }2.含参数注解
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE}) @Retention(RetentionPolicy.SOURCE)//该注解仅保留在Java原文件中 public @interface SuppressWarnings { /**支持的参数列表all,deprecation,unchecked,fallthrough,path,serial,finally等*/ String[] value();//该注解可接受一个参数数组 }此注解可接受的更多参数参见此处。
3.自定义注解
/* ***************************************************************************** * This software is under the Apache License Version 2.0 * Author: Tao - mail:cn.java.river@gmail.com * Spreading Your Heart **************************************************************************** */ package atao.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 一个包含两个参数的简单测试用例注解,此注解仅适用于"方法". * * @author <a href="mailto:cn.java.river@gmail.com">Tao</a> * @since 1.0 */ @Documented @Target (ElementType.METHOD) @Retention (RetentionPolicy.RUNTIME) public @interface UseCase { /** 测试用例编号 */ public int id(); /** 测试用例描述 */ public String description() default "no description"; }4.最简单的注解
@interface Naked{}5.允许子类继承父类的注解
/**允许子类继承父类的注解*/ @Inherited public @interface TrustFather{}
总结,注解定义包含:
- 主题声明,权限修饰符 + @interface + 注解名称
-
注解描述
- 适用目标:@Target
- 保留级别:@Retention
- 是否产生JavaDoc:@Documented
- 允许子类继承父类的注解: @Inherited
- 其他注解
- 注解包含字段设定(例子2、3中)
+4.2使用
/* ***************************************************************************** * This software is under the Apache License Version 2.0 * Author: Tao - mail:cn.java.river@gmail.com * Spreading Your Heart **************************************************************************** */ package atao.annotation; import java.util.ArrayList; import java.util.List; /** * 一个使用如上注解密码工具类 * * @author <a href="mailto:cn.java.river@gmail.com">Tao</a> * @since 1.0 */ public class PasswordUtils { @UseCase (id = 47, description = "密码必须包含至少一个数字") public boolean validatePassword (String password) { return (password.matches ("\\w*\\d\\w*")); } @UseCase (id = 48) public String encryptPassword (String password) { return new StringBuilder (password).reverse ().toString (); } @UseCase (id = 49, description = "新密码不能和曾经用过的密码重复") public boolean checkForNewPassword (List <String> prevPasswords, String password) { return !prevPasswords.contains (password); } /**@deprecated 楼主决定不推荐使用此方法了,自己随便找个个其他的试试吧.*/ @Deprecated public void deprecatedMethod(){} @SuppressWarnings ({ "unused", "rawtypes" }) public void unsafeMethod () { //@SuppressWarnings ({ "unused", "rawtypes" }) //用在这里仍然可以,因为此注解支持修饰类和本地变量等类型。 List unsafeList = new ArrayList<String>(); } }
+4.3编写注解处理器
如下根据Java反射写出的一个简单的测试用例处理器
/* ***************************************************************************** * This software is under the Apache License Version 2.0 * Author: Tao - mail:cn.java.river@gmail.com * Spreading Your Heart **************************************************************************** */ package atao.annotation; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * 用例检测的注解处理器 * * @author <a href="mailto:cn.java.river@gmail.com">Tao</a> * @since 1.0 */ public class UseCaseChecker { public static void trackUseCases (List <Integer> useCases, Class <?> cl) { for (Method m : cl.getDeclaredMethods ()) { UseCase uc = m.getAnnotation (UseCase.class); if (uc != null) { System.out.println ("找到用例:" + uc.id () + " " + uc.description ()); useCases.remove (new Integer (uc.id ())); } } for (int i : useCases) { System.out.println ("警告-- 缺失用例:" + i); } } public static void main (String[] args) { List <Integer> useCases = new ArrayList <Integer> (); Collections.addAll (useCases, 1, 2, 3, 4,5); trackUseCases (useCases, PasswordUtils.class); } } /**运行输出结果为: 找到用例:1 密码必须包含至少一个数字 找到用例:4 新密码不能和曾经用过的密码重复 找到用例:3 无描述 警告-- 缺失用例:2 警告-- 缺失用例:5 */
至此,一个简单的Java注解使用流程我们已经走完,接下来我们谈谈注解的语言要素和相关Java API等.
+5语言要素
+5.1元注解
至JDk7,Java提供了3种内置标准注解(@Override,@Deprecate,@SuppressWarnings)和四种元注解(@Target,@Retention,@Documented,@Inherited)。细心的人会发现这些在上文叙述中已经提到。
- @Target,标示该注解可用在什么地方。可用参数包括(构造方法、字段、本地变量、方法、包、参数、类型(类、接口、注解、枚举))。其用ElementType枚举参数,读者可到阅读ElementType枚举产看原版解释。
- @Retention,标示注解的生命周期,其用RetentionPolicy枚举参数。包含三个(RetentionPolicy.SOURCE,RetentionPolicy.CLASS,RetentionPolicy.RUNTIME)。SOURCE,RUNTIME除前文代码中已经做出解释。至于CLASS表示注解会保存在class文件中,类加载时将被丢弃。事实上Java在其J2EE规范的API中已经广泛的使用了注解机制,但是通过文件查找工具确认JDK(j2se/j2ee)至1.7结束都没有提供可供参考的RetentionPolicy.CLASS生命周期的注解。这或许也预示着CLASSS生命周期的定义或许是一堆废柴。
- @Documented,是包含在JavaDoc中
- @Inherited,允许子类继承父类的注解
+5.2默认参数
当注解定义只包含一个字符串参数时,使用如下方式可以使用直接的字符串填充方式为参数赋值
//定义 @interface SimpleUseCase{String value();} //使用 @SimpleUseCase("就是这样么简单,可不使用键值对赋值")
+5.3裸体的注解
+5.4可重复注解
从JDK8开始java提供了一种允许自修饰的可循环注解@Repeatable,注解定义使用了此注解修饰则允许在目标上连续多次使用本注解。注意请下载JDK8,以下代码方可编译。
@Repeatable(Schedules.class) public @interface Schedule { String dayOfMonth(); String dayOfWeek(); String hour(); } @Schedule(dayOfMonth="last") @Schedule(dayOfWeek="Fri", hour="23") public void doPeriodicCleanup() { /**方法体*/ }
+6注解工具APT
APT全称 Annotation Processing Tool。注意:APT工具及com.sun.mirror包中相关的API自Java SE 7已经弃用。请使用javac工具中对应选项选项和javax.annotation.processing、 javax.lang.model包中的API。
APT工具是一个命令行实用的注解处理程序。它包括一组反射API,支持处理程序注解的常用操作(JSR 175)。这些反射API提供了一个构建时、基于源代码、只读视图的程序结构。它们被设计用来辅助Java泛型更清晰构造Java编程语言的类型系统数据模型(JSR 14)。
首先APT工具运行注解处理程序产生新的源代码和其他文件。接下来,APT会促使编译器编译原始和生成的源文件。这种方式将使开发周期更简单。
JSR 269,也被称为语言模型API,有两个基本部分:Java编程语言模型API和编写注解处理器API。可通过javac命令新选项使用这个功能;JSR 269有对应支持说明,javac现在取代了JDK5中的APT工具。
以下是相关API所在的包:
- javax.annotation.processing
- javax.lang.model
- javax.lang.model.element
- javax.lang.model.type
- javax.lang.model.util
+7参考资料
- Java编程思想第四版第20章
- Java Annotations Tutorial from Oracle
- Jenkov Java Annotation Tutorial
- Spring Annotations
- what-is-the-list-of-valid-suppresswarnings-warning-names-in-java
- Java 7 APT
- Java Language specification 7
- Java APT Getting Started.