如果用最简单的话描述注解,那就是元数据,即一种描述数据的数据。所以,可以说注解就是源代码的元数据。
在Java中叫Annotation,Annotation是一种应用于类、方法、参数、变量、构造器及包声明中的特殊修饰符。
Java自带的注解有@Override,@Supperwarning,@Deprecated
1.生成文档,比如@see @param @return
2.在编译时进行格式检查。如@override 放在方法前,如果你这个方法并不是覆盖了超类方法,则编译时就能检查出
3.追踪代码依赖性,实现替代配置文件,比如Spring的各种注解等
我们先写一个最简单的注解:
@Retention(RetentionPolicy.RUNTIME)
public @interface FunctionDesc {
}
上面这个注解没有任何的属性定义,我们看下定义一个注解所需的元素。首先使用关键字“@interface” 表明这类是注解类;然后这类上加上注解“@Retention”,这是定义注解所必须的。
@Retention是注解的注解,称为元注解。我们看一下@Retention的内部:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
/**
* Returns the retention policy.
* @return the retention policy
*/
RetentionPolicy value();
}
我们看到Retention本身也被@Retention注解,还有@Documented和@Target(ElementType.ANNOTATION_TYPE),ANNOTATION_TYPE表明这个注解应应用在注解上。
Retention有一个属性value,类型为枚举的RetentionPolicy,它没有默认值,所以不写这个值就会报错,我们继续看一下RetentionPolicy:
public enum RetentionPolicy {
/**
* Annotations are to be discarded by the compiler.
*/
SOURCE,
/**
* Annotations are to be recorded in the class file by the compiler
* but need not be retained by the VM at run time. This is the default
* behavior.
*/
CLASS,
/**
* Annotations are to be recorded in the class file by the compiler and
* retained by the VM at run time, so they may be read reflectively.
*
* @see java.lang.reflect.AnnotatedElement
*/
RUNTIME
}
这个枚举定义了三个值,SOURCE,CLASS,RUNTIME。分别定义我们写的注解保持到哪个生命阶段。把上面的英文注释翻译过来:
如此,我们实现并解释了最简单的注解,那么注解怎么起作用呢,我们借测试来说明下:
public class AnnotationTest {
@FunctionDesc
public void work(){}
public static void main(String[] args) {
try {
Method workMethod = AnnotationTest.class.getMethod("work",null);
if(workMethod.isAnnotationPresent(FunctionDesc.class)){
System.out.println(workMethod.getAnnotation(FunctionDesc.class));
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
运行上面的代码,会输出下面这句:
@com.wy.core.annotation.FunctionDesc()
这表明我们的注解起作用了。细心的你会发现,注解的真正起作用是借助于“反射”完成的,这个我们待会深讲。
上面我们定义的@FunctionDesc是没有任何功能的。实际上注解的功能实现是通过定义属性完成的。我们给FunctionDesc加上属性:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface FunctionDesc {
String desc();
int paramCount() default 0;
String returnDesc() default "";
}
public class AnnotationTest {
@FunctionDesc(desc = "干活")
public void work(){}
public static void main(String[] args) {
try {
Method workMethod = AnnotationTest.class.getMethod("work",null);
if(workMethod.isAnnotationPresent(FunctionDesc.class)){
FunctionDesc fd = workMethod.getAnnotation(FunctionDesc.class);
System.out.println(fd);
System.out.println("desc="+fd.desc());
System.out.println("paramCount="+fd.paramCount());
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
运行代码后输出:
@com.wy.core.annotation.FunctionDesc(paramCount=0, returnDesc=, desc=干活)
desc=干活
paramCount=0
我们添加了@Target({ElementType.METHOD}),表明这个注解适用在方法上。内部定义了3个属性,desc是用作方法描述 ,paramCount表明参数个数,returnDesc是返回值描述。
paramCount和returnDesc有默认值,注解时可以不填值,但是desc必须写值。
上面这个测试的例子,我们从AnnotationTest.class 反射拿到了work方法的Method定义,从Method获取了它上面的注解信息。
这就是注解功能处理实现的大致思路。
我们知道反射的相关类有Class,Method,Field,Constructor等,它们都实现了AnnotatedElement接口。AnnotatedElement是注解处理器类库的核心元素。
我们看下AnnotatedElement的定义
public interface AnnotatedElement {
default boolean isAnnotationPresent(Class extends Annotation> annotationClass) {
return getAnnotation(annotationClass) != null;
}
T getAnnotation(Class annotationClass);
Annotation[] getAnnotations();
default T[] getAnnotationsByType(Class annotationClass) {
/*
* Definition of associated: directly or indirectly present OR
* neither directly nor indirectly present AND the element is
* a Class, the annotation type is inheritable, and the
* annotation type is associated with the superclass of the
* element.
*/
T[] result = getDeclaredAnnotationsByType(annotationClass);
if (result.length == 0 && // Neither directly nor indirectly present
this instanceof Class && // the element is a class
AnnotationType.getInstance(annotationClass).isInherited()) { // Inheritable
Class> superClass = ((Class>) this).getSuperclass();
if (superClass != null) {
// Determine if the annotation is associated with the
// superclass
result = superClass.getAnnotationsByType(annotationClass);
}
}
return result;
}
default T getDeclaredAnnotation(Class annotationClass) {
Objects.requireNonNull(annotationClass);
// Loop over all directly-present annotations looking for a matching one
for (Annotation annotation : getDeclaredAnnotations()) {
if (annotationClass.equals(annotation.annotationType())) {
// More robust to do a dynamic cast at runtime instead
// of compile-time only.
return annotationClass.cast(annotation);
}
}
return null;
}
default T[] getDeclaredAnnotationsByType(Class annotationClass) {
Objects.requireNonNull(annotationClass);
return AnnotationSupport.
getDirectlyAndIndirectlyPresent(Arrays.stream(getDeclaredAnnotations()).
collect(Collectors.toMap(Annotation::annotationType,
Function.identity(),
((first,second) -> first),
LinkedHashMap::new)),
annotationClass);
}
Annotation[] getDeclaredAnnotations();
}
所以我们通过反射得到Class,Method,Field,Constructor之后,就可以通过以上方法获取注解信息了,最常用的以下几个方法
1.getAnnotation(Class annotationClass): 返回改程序元素上存在的、指定类型的注解,如果该类型注解不存在,则返回null。
2.getAnnotations():返回该程序元素上存在的所有注解。
3.isAnnotationPresent(Class annotationClass):判断该程序元素上是否包含指定类型的注解,存在则返回true,否则返回false.
4.Annotation[] getDeclaredAnnotations():返回直接存在于此元素上的所有注解。与此接口中的其他方法不同,该方法将忽略继承的注解。该方法的调用者可以随意修改返回的数组;这不会对其他调用者返回的数组产生任何影响。