在使用注解之前,需要了解:一、什么是注解?二、注解是如何产生作用的?而元注解是一个很好的切入点
元注解的源码结构
从java.lang.annotation包截图看,一共定义了6个注解:
其中前4个是元注解
元注解的定义
元注解负责注解自定义注解,你可以看到许多自定义的注解上面都有这些元注解:
例如spring mvc的注解@RequestMapping
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping{
......
}
元注解的用途
@Target
标识注解的使用范围,可以赋值为ElementType
类型,ElementType
定义如下:
public enum ElementType {
/** Class, interface (including annotation type), or enum declaration */
TYPE,
/** Field declaration (includes enum constants) */
FIELD,
/** Method declaration */
METHOD,
/** Formal parameter declaration */
PARAMETER,
/** Constructor declaration */
CONSTRUCTOR,
/** Local variable declaration */
LOCAL_VARIABLE,
/** Annotation type declaration */
ANNOTATION_TYPE,
/** Package declaration */
PACKAGE,
/**
* Type parameter declaration
*
* @since 1.8
*/
TYPE_PARAMETER,
/**
* Use of a type
*
* @since 1.8
*/
TYPE_USE
}
仍然以@RequestMapping
为例,它的@Target
赋值为{ElementType.METHOD, ElementType.TYPE},参考Element
注释可以理解@RequestMapping
可以用于java的方法定义及Class/Interfac/enum的定义上
@Retention
可以赋值 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
}
RetentionPolicy.SOURCE
:表明注解会被编译器丢弃,字节码中不会带有注解信息RetentionPolicy.CLASS
:表明注解会被写入字节码文件,且是@Retention
的默认值RetentionPolicy.RUNTIME
:表明注解会被写入字节码文件,并且能够被JVM 在运行时获取到,可以通过反射的方式解析到
@Documented
/**
* Indicates that annotations with a type are to be documented by javadoc
* and similar tools by default. This type should be used to annotate the
* declarations of types whose annotations affect the use of annotated
* elements by their clients. If a type declaration is annotated with
* Documented, its annotations become part of the public API
* of the annotated elements.
*
* @author Joshua Bloch
* @since 1.5
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}
从注释可以看到 @Document
注解用途主要是标识类型是否要被收入javadoc
如何处理注解?
jdk中是通过AnnotatedElement
(package java.lang.reflect)接口实现对注解的解析,我们的Class类实现了AnnotatedElement
接口
public final class Class implements java.io.Serializable,
GenericDeclaration,
Type,
AnnotatedElement {
......
}
AnnotatedElement
代码:
AnnotatedElement
的注释:
Represents an annotated element of the program currently running in this VM. This interface allows annotations to be read reflectively翻译过来就是:AnnotatedElement代表了jvm中一个正在运行的被注解元素,这个接口允许通过反射的方式读取注解
可以看下Class类中对于AnnotatedElement
接口都是如何实现的:
/**
* @throws NullPointerException {@inheritDoc}
* @since 1.5
*/
@SuppressWarnings("unchecked")
public A getAnnotation(Class annotationClass) {
Objects.requireNonNull(annotationClass);
return (A) annotationData().annotations.get(annotationClass);
}
/**
* {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
* @since 1.5
*/
@Override
public boolean isAnnotationPresent(Class extends Annotation> annotationClass) {
return GenericDeclaration.super.isAnnotationPresent(annotationClass);
}
/**
* @throws NullPointerException {@inheritDoc}
* @since 1.8
*/
@Override
public A[] getAnnotationsByType(Class annotationClass) {
Objects.requireNonNull(annotationClass);
AnnotationData annotationData = annotationData();
return AnnotationSupport.getAssociatedAnnotations(annotationData.declaredAnnotations,
this,
annotationClass);
}
/**
* @since 1.5
*/
public Annotation[] getAnnotations() {
return AnnotationParser.toArray(annotationData().annotations);
}
/**
* @throws NullPointerException {@inheritDoc}
* @since 1.8
*/
@Override
@SuppressWarnings("unchecked")
public A getDeclaredAnnotation(Class annotationClass) {
Objects.requireNonNull(annotationClass);
return (A) annotationData().declaredAnnotations.get(annotationClass);
}
/**
* @throws NullPointerException {@inheritDoc}
* @since 1.8
*/
@Override
public A[] getDeclaredAnnotationsByType(Class annotationClass) {
Objects.requireNonNull(annotationClass);
return AnnotationSupport.getDirectlyAndIndirectlyPresent(annotationData().declaredAnnotations,
annotationClass);
}
/**
* @since 1.5
*/
public Annotation[] getDeclaredAnnotations() {
return AnnotationParser.toArray(annotationData().declaredAnnotations);
}
上面的接口实现中,大致的原理都是一致的,我们挑选其中的getAnnotation
方法来讲解:
Controller是注解类型,Controller.getClass()获取到的就是Class实例
annotationData().annotations
是一个Map
(key为注解的Class实例,value为注解类型),源码为: // annotation data that might get invalidated when JVM TI RedefineClasses() is called
private static class AnnotationData {
final Map, Annotation> annotations;
final Map, Annotation> declaredAnnotations;
// Value of classRedefinedCount when we created this AnnotationData instance
final int redefinedCount;
AnnotationData(Map, Annotation> annotations,
Map, Annotation> declaredAnnotations,
int redefinedCount) {
this.annotations = annotations;
this.declaredAnnotations = declaredAnnotations;
this.redefinedCount = redefinedCount;
}
}
2、annotationData()
的源码是:
private AnnotationData annotationData() {
while (true) { // retry loop
AnnotationData annotationData = this.annotationData;
int classRedefinedCount = this.classRedefinedCount;
if (annotationData != null &&
annotationData.redefinedCount == classRedefinedCount) {
return annotationData;
}
// null or stale annotationData -> optimistically create new instance
AnnotationData newAnnotationData = createAnnotationData(classRedefinedCount);
// try to install it
if (Atomic.casAnnotationData(this, annotationData, newAnnotationData)) {
// successfully installed new AnnotationData
return newAnnotationData;
}
}
}
核心的逻辑是:当this.annotationData
为空,解析类中的annotationData
并写入this.annotationData
,最后都会返回this.annotationData
3、其中Atomic.casAnnotationData(this, annotationData, newAnnotationData)
的作用便是将解析到的annotationData
写入this.annotationData
:
static boolean casAnnotationData(Class> clazz,
AnnotationData oldData,
AnnotationData newData) {
return unsafe.compareAndSwapObject(clazz, annotationDataOffset, oldData, newData);
}
其中unsafe.compareAndSwapObject是一个native方法
4、而createAnnotationData(classRedefinedCount)
的作用是解析类中用到的annotationData
private AnnotationData createAnnotationData(int classRedefinedCount) {
Map, Annotation> declaredAnnotations =
AnnotationParser.parseAnnotations(getRawAnnotations(), getConstantPool(), this);
Class> superClass = getSuperclass();
Map, Annotation> annotations = null;
if (superClass != null) {
Map, Annotation> superAnnotations =
superClass.annotationData().annotations;
for (Map.Entry, Annotation> e : superAnnotations.entrySet()) {
Class extends Annotation> annotationClass = e.getKey();
if (AnnotationType.getInstance(annotationClass).isInherited()) {
if (annotations == null) { // lazy construction
annotations = new LinkedHashMap<>((Math.max(
declaredAnnotations.size(),
Math.min(12, declaredAnnotations.size() + superAnnotations.size())
) * 4 + 2) / 3
);
}
annotations.put(annotationClass, e.getValue());
}
}
}
if (annotations == null) {
// no inherited annotations -> share the Map with declaredAnnotations
annotations = declaredAnnotations;
} else {
// at least one inherited annotation -> declared may override inherited
annotations.putAll(declaredAnnotations);
}
return new AnnotationData(annotations, declaredAnnotations, classRedefinedCount);
}
整个的处理逻辑是:
1、获取类本身的declaredAnnotations
2、获取父类的annotations
3、将declaredAnnotations+annotations
整合,返回
Annotation
解析的范例代码:
@Component
public class SSHClient {
......
}
public class AnnotationHelper {
public static void main(String[] args) {
Annotation[] annotations = new Annotation[0];
annotations = SSHClient.class.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation.toString());
System.out.println(annotation.annotationType());
System.out.println(annotation.getClass().getName());
System.out.println(annotation.getClass().getTypeName());
System.out.println(annotation.getClass().toString());
}
if (SSHClient.class.isAnnotationPresent(Component.class)) {
System.out.println("find Component annotation");
}
Annotation annotation = SSHClient.class.getAnnotation(Component.class);
System.out.println(annotation.toString());
System.out.println(annotation.annotationType());
System.out.println(annotation.getClass().getName());
System.out.println(annotation.getClass().getTypeName());
System.out.println(annotation.getClass().toString());
}
}
执行结果:
可以看到,通过Annotation接口中定义的annotationType()可以获取Annotation的类型
实际应用中,比如spring框架中对注解的解析有专门的工具类,但是都是基于AnnotatedElement
中定义的方法来实现的
以上,就是整个元注解和注解解析相关的讲解。
作者:测试你个头
链接:https://www.jianshu.com/p/e9329c8a59c2
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。