zoukankan      html  css  js  c++  java
  • java 元注解

    1.概念讲解

    (转载自:https://www.cnblogs.com/lyy-2016/p/6288535.html)

    元注解是指注解的注解,包括@Retention @Target @Document @Inherited四种。

    1.@Retention: 定义注解的保留策略
    @Retention(RetentionPolicy.SOURCE)   //注解仅存在于源码中,在class字节码文件中不包含
    @Retention(RetentionPolicy.CLASS)     // 默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得,
    @Retention(RetentionPolicy.RUNTIME)  // 注解会在class字节码文件中存在,在运行时可以通过反射获取到
    首 先要明确生命周期长度 SOURCE < CLASS < RUNTIME ,所以前者能作用的地方后者一定也能作用。一般如果需要在运行时去动态获取注解信息,那只能用 RUNTIME 注解;如果要在编译时进行一些预处理操作,比如生成一些辅助代码(如 ButterKnife),就用 CLASS注解;如果只是做一些检查性的操作,比如 @Override 和 @SuppressWarnings,则可选用 SOURCE 注解。


    2.@Target:定义注解的作用目标
    源码为:
    @Documented  
    @Retention(RetentionPolicy.RUNTIME)  
    @Target(ElementType.ANNOTATION_TYPE)  
    public @interface Target {  
        ElementType[] value();  
    }  
    @Target(ElementType.TYPE)   //接口、类、枚举、注解
    @Target(ElementType.FIELD) //字段、枚举的常量
    @Target(ElementType.METHOD) //方法
    @Target(ElementType.PARAMETER) //方法参数
    @Target(ElementType.CONSTRUCTOR)  //构造函数
    @Target(ElementType.LOCAL_VARIABLE)//局部变量
    @Target(ElementType.ANNOTATION_TYPE)//注解
    @Target(ElementType.PACKAGE) ///包    
    3.@Document:说明该注解将被包含在javadoc中
    4.@Inherited:说明子类可以继承父类中的该注解

    2.反射获取注解

    举例:

    复制代码
    // 适用类、接口(包括注解类型)或枚举  
    @Retention(RetentionPolicy.RUNTIME)  
    @Target(ElementType.TYPE)  
    public @interface ClassInfo {  
        String value();  
    }  
        
    // 适用field属性,也包括enum常量  
    @Retention(RetentionPolicy.RUNTIME)  
    @Target(ElementType.FIELD)  
    public @interface FieldInfo {  
        int[] value();  
    }  
    // 适用方法  
    @Retention(RetentionPolicy.RUNTIME)  
    @Target(ElementType.METHOD)  
    public @interface MethodInfo {  
        String name() default "long";  
        String data();  
        int age() default 27;  
    }  
    复制代码

    这3个注解分别适用于不同的元素,并都带有不同的属性,在使用注解是需要设置这些属性值。
    再定义一个测试类来使用这些注解:

    复制代码
    @ClassInfo("Test Class")  
    public class TestRuntimeAnnotation {  
     
        @FieldInfo(value = {1, 2})  
        public String fieldInfo = "FiledInfo";  
     
        @FieldInfo(value = {10086})  
        public int i = 100;  
     
        @MethodInfo(name = "BlueBird", data = "Big")  
        public static String getMethodInfo() {  
            return TestRuntimeAnnotation.class.getSimpleName();  
        }  
    }  
    在代码中获取注解信息:
    private void _testRuntimeAnnotation() {  
        StringBuffer sb = new StringBuffer();  
        Class<?> cls = TestRuntimeAnnotation.class;  
        Constructor<?>[] constructors = cls.getConstructors();  
        // 获取指定类型的注解  
        sb.append("Class注解:").append("\n");  
        ClassInfo classInfo = cls.getAnnotation(ClassInfo.class);  
        if (classInfo != null) {  
            sb.append(Modifier.toString(cls.getModifiers())).append(" ")  
                    .append(cls.getSimpleName()).append("\n");  
            sb.append("注解值: ").append(classInfo.value()).append("\n\n");  
        }  
     
        sb.append("Field注解:").append("\n");  
        Field[] fields = cls.getDeclaredFields();  
        for (Field field : fields) {  
            FieldInfo fieldInfo = field.getAnnotation(FieldInfo.class);  
            if (fieldInfo != null) {  
                sb.append(Modifier.toString(field.getModifiers())).append(" ")  
                        .append(field.getType().getSimpleName()).append(" ")  
                        .append(field.getName()).append("\n");  
                sb.append("注解值: ").append(Arrays.toString(fieldInfo.value())).append("\n\n");  
            }  
        }  
     
        sb.append("Method注解:").append("\n");  
        Method[] methods = cls.getDeclaredMethods();  
        for (Method method : methods) {  
            MethodInfo methodInfo = method.getAnnotation(MethodInfo.class);  
            if (methodInfo != null) {  
                sb.append(Modifier.toString(method.getModifiers())).append(" ")  
                        .append(method.getReturnType().getSimpleName()).append(" ")  
                        .append(method.getName()).append("\n");  
                sb.append("注解值: ").append("\n");  
                sb.append("name: ").append(methodInfo.name()).append("\n");  
                sb.append("data: ").append(methodInfo.data()).append("\n");  
                sb.append("age: ").append(methodInfo.age()).append("\n");  
            }  
        }  
     
        System.out.print(sb.toString());  
    }  
    复制代码
     

    3.Java源代码如何处理注解

    (转载自:https://www.jianshu.com/p/e9329c8a59c2  作者:测试你个头)

    jdk中是通过AnnotatedElement(package java.lang.reflect)接口实现对注解的解析,我们的Class类实现了AnnotatedElement接口

    public final class Class<T> 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 extends Annotation> A getAnnotation(Class<A> 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 extends Annotation> A[] getAnnotationsByType(Class<A> 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 extends Annotation> A getDeclaredAnnotation(Class<A> annotationClass) {
            Objects.requireNonNull(annotationClass);
    
            return (A) annotationData().declaredAnnotations.get(annotationClass);
        }
    
        /**
         * @throws NullPointerException {@inheritDoc}
         * @since 1.8
         */
        @Override
        public <A extends Annotation> A[] getDeclaredAnnotationsByType(Class<A> annotationClass) {
            Objects.requireNonNull(annotationClass);
    
            return AnnotationSupport.getDirectlyAndIndirectlyPresent(annotationData().declaredAnnotations,
                                                                     annotationClass);
        }
    
        /**
         * @since 1.5
         */
        public Annotation[] getDeclaredAnnotations()  {
            return AnnotationParser.toArray(annotationData().declaredAnnotations);
        }
    

    上面的接口实现中,大致的原理都是一致的,我们挑选其中的getAnnotation方法来讲解:

    • getAnnotation
      根据注解的class实例从类的注解缓存数据中获取匹配的注解类型
      Controller是注解类型,Controller.getClass()获取到的就是Class实例
      1、代码中annotationData().annotations是一个Map(key为注解的Class实例,value为注解类型),源码为:
        // annotation data that might get invalidated when JVM TI RedefineClasses() is called
        private static class AnnotationData {
            final Map<Class<? extends Annotation>, Annotation> annotations;
            final Map<Class<? extends Annotation>, Annotation> declaredAnnotations;
    
            // Value of classRedefinedCount when we created this AnnotationData instance
            final int redefinedCount;
    
            AnnotationData(Map<Class<? extends Annotation>, Annotation> annotations,
                           Map<Class<? extends Annotation>, 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 <T> 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<Class<? extends Annotation>, Annotation> declaredAnnotations =
                AnnotationParser.parseAnnotations(getRawAnnotations(), getConstantPool(), this);
            Class<?> superClass = getSuperclass();
            Map<Class<? extends Annotation>, Annotation> annotations = null;
            if (superClass != null) {
                Map<Class<? extends Annotation>, Annotation> superAnnotations =
                    superClass.annotationData().annotations;
                for (Map.Entry<Class<? extends Annotation>, 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中定义的方法来实现的

    以上,就是整个元注解和注解解析相关的讲解。

  • 相关阅读:
    python中列表排序的方法
    pyrhon3中字符串方法
    python数据探索与数据与清洗概述
    2020年日期表-python实现
    python中字符串离散化的例子
    python中常见的日期处理方法
    如何简单地理解Python中的if __name__ == '__main__'
    我的老爸老了
    关于
    关于
  • 原文地址:https://www.cnblogs.com/frankcui/p/10226729.html
Copyright © 2011-2022 走看看