注解(元数据)为我们在代码中添加信息提供了一种形式化的方法,使我们可以在稍后某个时刻方便使用这些数据
从JDK 1.6开始提供了一组插入式注解处理器的标准API在编译期间对注解进行处理,有了这些API之后,我们的代码就可能干涉编译器的行为,对于语法树中的任意元素,甚至包括代码注释都可以在插件中访问到
注解使得我们能
- 以将由编译器来测试和验证的格式,存储有关程序的额外信息
- 可以生成描述符文件,甚至或是新的类定义,有助于减轻编写样板的负担
- 将元数据保存在Java源代码中,并构造处理工具
- 简单易读
Java内置了三种注解
- @Override:定义覆盖超类的方法,如果签名对不上,就会出错
- @Deprecated:如果使用了注解为它的元素,就会发出警告
- @SuppressWarnings:关闭不当的编译器警告信息
基本语法
与其他任何Java接口一样,注解也会编译成class文件1
2
3 (ElementType.METHOD)
(RetentionPolicy.RUNTIME)
public Test {}
上面就是最简单的标记注解(没有元素),@Target
@Retention
是两个元注解,第一个定义了注解应用在什么地方(构造器、域、局部变量、方法、包、参数、类、接口等),第二个定义注解在哪个级别可用(SOURCE、CLASS、RUNTIME)
1 | (ElementType.METHOD) |
上面的注解可以用来跟踪一个项目的用例,如果一个方法实现了某个需求,程序员就可以为该方法加上注解,如下所示1
2
3
4
5
6
7public class PasswordUtils {
47,description = (id =
"Password must contain at least one numeric")
public boolean validatePassword(String password) {
return password.matches("\\w*\\d\\w*");
}
}
编写注解处理器
如果没有读取注解的工具,那注解和注释并没与两样,因此使用注解的很大一部分工作就是创建和使用注解处理器,底层实现是反射机制的API,同时还提供apt
帮助解析带有注解的源代码
1 | public class UseCaseTracker { |
上面就是简单的注解处理器,将用它来读取PasswordUtils
类,先提供一组id
,它会列出类中找到的用例和缺失的用例
这个程序用到了两个反射方法:getDeclaredMethods()
和getAnnotation()
,它们都属于AnnotatedElement
接口(Class、Method和Field等类都实现了该接口??),getAnnotation()
就是返回指定类型的注解对象,然后通过调用注解对象的方法来提取元素的值
注解元素可用类型有所有基本类型、String、Class、enum、Annotation、以上类型数组。由此可见注解可以嵌套,这将非常有用
编译器对于注解元素的默认值有很大的限制,要么元素具有默认值,要么在使用注解时提供值,不能用null作为值,这使得处理器很难表现一个元素的存在或缺失的状态,为了绕开这个限制,只能使用空字符串或负数来表示元素不存在
生成外部文件(ORM)
有些framework需要一些额外的信息来能和源代码协同工作,如String中的ORM(Mybatis、Hibernate),这就是注解的价值所在!
假如希望提供一些基本的对象/关系映射功能,能够自动生成数据库表,用以存储Javabean对象,可以选择XML描述文件,指明类的名字,每个成员以及数据库映射的相关信息,但是如果使用注解,所有信息都保存在Javabean源文件中,为此只要一些新的注解,用来定义与Javabean关联的数据库表的名字,以及与Javabean属性关联的列的名字和SQL类型
1 | (ElementType.TYPE) |
该注解通过该元素为处理器创建数据库表提供表的名字1
2
3
4
5
6
7 (ElementType.FIELD)
(RetentionPolicy.RUNTIME)
public Constraints {
boolean primaryKey() default false;
boolean allowNULL() default true;
boolean unique() default false;
}
注解处理器通过Constraints
提取出数据库表的元数据,虽然相对于数据库能提供的属性来说,只是一部分
1 | (ElementType.FIELD) |
1 | (ElementType.FIELD) |
上面两个注解定义的是SQL类型,Constraints constraints() default @Constraints;
这里即采取了嵌套注解,默认值是@Constraints
,当然也可以这样定义Constraints constraints() default @Constraints(unique=true);
1 | "MEMBER") (name = |
上述代码则应用了之前定义的注解,当然也有不同的方式来实现上面的功能,那种灵活且简洁就选那种
注解目前不支持继承
下面的代码实现了上面的定义的注解的处理器
1 | import java.util.List; |
虽然上面的代码对现在的自己来说大开眼界!但是对于真正的对象/关系映射框架来说,还是太幼稚!例如使用@DBTable
注解给出表的名字,如果要修改表的名字,就要重新编译Java代码,这显然不是希望看到的!
详细的对象/关系映射框架参见《Spring-数据访问》