什么是注解?
注解是JDK1.5引入的一个语法糖,它主要用来当作元数据,简单的说就是用于解释数据的数据。在Java中,类、方法、变量、参数、包都可以被注解。很多开源框架都使用了注解,例如Spring、MyBatis、Junit。我们平常最常见的注解可能就是@Override了,该注解用来标识一个重写的函数。
注解的作用:
- 配置文件:替代xml等文本文件格式的配置文件。使用注解作为配置文件可以在代码中实现动态配置,相比外部配置文件,注解的方式会减少很多文本量。但缺点也很明显,更改配置需要对代码进行重新编译,无法像外部配置文件一样进行集中管理(所以现在基本都是外部配置文件+注解混合使用)。
- 数据的标记:注解可以作为一个标记(例如:被@Override标记的方法代表被重写的方法)。
- 减少重复代码:注解可以减少重复且乏味的代码。比如我们定义一个@ValidateInt,然后通过反射来获得类中所有成员变量,只要是含有@ValidateInt注解的成员变量,我们就可以对其进行数据的规则校验。
定义一个注解非常简单,只需要遵循以下的语法规则:
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) @Documented public @interface ValidateInt { // 它们看起来像是定义一个函数,但其实这是注解中的属性 int maxLength(); int minLength(); }
我们发现上面的代码在定义注解时也使用了注解,这些注解被称为元注解。作用于注解上的注解称为元注解(元注解其实就是注解的元数据),Java中一共有以下元注解。
@Target:用于描述注解的使用范围(注解可以用在什么地方)。
- ElementType.CONSTRUCTOR:构造器。
- ElementType.FIELD:成员变量。
- ElementType.LOCAL_VARIABLE:局部变量。
- ElementType.PACKAGE:包。
- ElementType.PARAMETER:参数。
- ElementType.METHOD:方法。
- ElementType.TYPE:类、接口(包括注解类型) 或enum声明。
@Retention:注解的生命周期,用于表示该注解会在什么时期保留。
- RetentionPolicy.RUNTIME:运行时保留,这样就可以通过反射获得了。
- RetentionPolicy.CLASS:在class文件中保留。
- RetentionPolicy.SOURCE:在源文件中保留。
@Documented:表示该注解会被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。
@Inherited:表示该注解是可被继承的(如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类)。
了解了这些基础知识之后,接着完成上述定义的@ValidateInt,我们定义一个Cat类然后在它的成员变量中使用@ValidateInt,并通过反射进行数据校验。
public class Cat {private String name;
@ValidateInt(minLength = 0, maxLength = 10)
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public static void main(String[] args) throws IllegalAccessException {
Cat cat = new Cat();
cat.setName("楼楼");
cat.setAge(11);
Class<? extends Cat> clazz = cat.getClass();
Field[] fields = clazz.getDeclaredFields();
if (fields != null) {
for (Field field : fields) {
ValidateInt annotation = field.getDeclaredAnnotation(ValidateInt.class);
if (annotation != null) {
field.setAccessible(true);
int value = field.getInt(cat);
if (value < annotation.minLength()) {
// ....
} else if (value > annotation.maxLength()) {
// ....
}
}
}
}
}
}
注解的实现
注解其实只是Java的一颗语法糖(语法糖是一种方便程序员使用的语法规则,但它其实并没有表面上那么神奇的功能,只不过是由编译器帮程序员生成那些繁琐的代码)。在Java中这样的语法糖还有很多,例如enum、泛型、forEach等。
通过阅读JLS(Java Language Specification(当你想了解一个语言特性的实现时,最好的方法就是阅读官方规范)发现,注解是一个继承自java.lang.annotation.Annotation接口的特殊接口,原文如下:
An annotation type declaration specifies a new annotation type, a special kind of interface type. To distinguish an annotation type declaration from a normal interface declaration, the keyword interface is preceded by an at-sign (@).
Note that the at-sign (@) and the keyword interface are distinct tokens. It is possible to separate them with whitespace, but this is discouraged as a matter of style.
The rules for annotation modifiers on an annotation type declaration are specified in §9.7.4 and §9.7.5.
The Identifier in an annotation type declaration specifies the name of the annotation type.
It is a compile-time error if an annotation type has the same simple name as any of its enclosing classes or interfaces.
The direct superinterface of every annotation type is java.lang.annotation.Annotation.
package java.lang.annotation;
/**
- The common interface extended by all annotation types. Note that an
- interface that manually extends this one does not define
- an annotation type. Also note that this interface does not itself
- define an annotation type.
- More information about annotation types can be found in section 9.6 of
- The Java™ Language Specification.
- The {@link java.lang.reflect.AnnotatedElement} interface discusses
- compatibility concerns when evolving an annotation type from being
- non-repeatable to being repeatable.
- @author Josh Bloch
- @since 1.5
*/
public interface Annotation {
…
}
我们将上节定义的@ValidateInt注解进行反编译来验证这个说法。
Last modified Oct 14, 2017; size 479 bytes MD5 checksum 2d9dd2c169fe854db608c7950af3eca7 Compiled from "ValidateInt.java" public interface com.sun.annotation.ValidateInt extends java.lang.annotation.Annotation minor version: 0 major version: 52 flags: ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION Constant pool: #1 = Class #18 // com/sun/annotation/ValidateInt #2 = Class #19 // java/lang/Object #3 = Class #20 // java/lang/annotation/Annotation #4 = Utf8 maxLength #5 = Utf8 ()I #6 = Utf8 minLength #7 = Utf8 SourceFile #8 = Utf8 ValidateInt.java #9 = Utf8 RuntimeVisibleAnnotations #10 = Utf8 Ljava/lang/annotation/Retention; #11 = Utf8 value #12 = Utf8 Ljava/lang/annotation/RetentionPolicy; #13 = Utf8 RUNTIME #14 = Utf8 Ljava/lang/annotation/Target; #15 = Utf8 Ljava/lang/annotation/ElementType; #16 = Utf8 FIELD #17 = Utf8 Ljava/lang/annotation/Documented; #18 = Utf8 com/sun/annotation/ValidateInt #19 = Utf8 java/lang/Object #20 = Utf8 java/lang/annotation/Annotation { public abstract int maxLength(); descriptor: ()I flags: ACC_PUBLIC, ACC_ABSTRACT
public abstract int minLength();
descriptor: ()I
flags: ACC_PUBLIC, ACC_ABSTRACT
}
SourceFile: “ValidateInt.java”
RuntimeVisibleAnnotations:
0: #10(#11=e#12.#13)
1: #14(#11=[e#15.#16])
2: #17()
public interface com.sun.annotation.ValidateInt extends java.lang.annotation.Annotation,很明显ValidateInt继承自java.lang.annotation.Annotation。