zoukankan      html  css  js  c++  java
  • java注解的实现原理

    使用

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Log {
      String value();
    }
    

    Target是java中的源注解,标识注解的使用位置,如类,方法,参数,变量等。
    Retention也是源注解,标识注解的作用范围,编译期,运行期。

    @Log("hello")
    public class User {
    }
    
    public class Client {
      public static void main(String[] args) {
        Log log = User.class.getAnnotation(Log.class);
        System.out.println(log.value());
      }
    }
    

    结果为

    hello
    

    原理

    注解本质上是一个接口,

    public interface Log
        extends Annotation{
        public abstract String value();
    }
    

    接下来我们跟一下getAnnotation方法的实现原理,

    public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {
            Objects.requireNonNull(annotationClass);
    	// 关键就是annotationData()方法的实现
            return (A) annotationData().annotations.get(annotationClass);
        }
    

    从类上获取注解对象

    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
    // 看一下java是如何创建注解的数据的
                AnnotationData newAnnotationData = createAnnotationData(classRedefinedCount);
                // try to install it
                if (Atomic.casAnnotationData(this, annotationData, newAnnotationData)) {
                    // successfully installed new AnnotationData
                    return newAnnotationData;
                }
            }
        }
    

    Class内部对注解数据是使用了缓存的,只会解析一次。

    private AnnotationData createAnnotationData(int classRedefinedCount) {
    // 这两个方法都是native方法
            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);
        }
    

    IDE在debug过程中,可以看到

    declaredAnnotations变量是一个map,key是注解接口的class,value是接口的一个动态代理实现,具体的处理是AnnotationInvocationHandler,

    package sun.reflect.annotation;
    
    class AnnotationInvocationHandler implements InvocationHandler, Serializable {
      private static final long serialVersionUID = 6182022883658399397L;
      private final Class<? extends Annotation> type;
      private final Map<String, Object> memberValues;
      private transient volatile Method[] memberMethods = null;
    
      AnnotationInvocationHandler(Class<? extends Annotation> var1, Map<String, Object> var2) {
        Class[] var3 = var1.getInterfaces();
        if (var1.isAnnotation() && var3.length == 1 && var3[0] == Annotation.class) {
          this.type = var1;
          this.memberValues = var2;
        } else {
          throw new AnnotationFormatError("Attempt to create proxy for a non-annotation type.");
        }
      }
    public Object invoke(Object var1, Method var2, Object[] var3) {
        String var4 = var2.getName();
        Class[] var5 = var2.getParameterTypes();
        if (var4.equals("equals") && var5.length == 1 && var5[0] == Object.class) {
          return this.equalsImpl(var3[0]);
        } else if (var5.length != 0) {
          throw new AssertionError("Too many parameters for an annotation method");
        } else {
          byte var7 = -1;
          switch(var4.hashCode()) {
          case -1776922004:
            if (var4.equals("toString")) {
              var7 = 0;
            }
            break;
          case 147696667:
            if (var4.equals("hashCode")) {
              var7 = 1;
            }
            break;
          case 1444986633:
            if (var4.equals("annotationType")) {
              var7 = 2;
            }
          }
    
          switch(var7) {
          case 0:
            return this.toStringImpl();
          case 1:
            return this.hashCodeImpl();
          case 2:
            return this.type;
          default:
    // 核心逻辑
            Object var6 = this.memberValues.get(var4);
            if (var6 == null) {
              throw new IncompleteAnnotationException(this.type, var4);
            } else if (var6 instanceof ExceptionProxy) {
              throw ((ExceptionProxy)var6).generateException();
            } else {
              if (var6.getClass().isArray() && Array.getLength(var6) != 0) {
                var6 = this.cloneArray(var6);
              }
    
              return var6;
            }
          }
        }
      }
    }
    

    通过debug信息可以看到,注解的信息是保存在AnnotationInvocationHandler的memberValues变量中的,调用value方法是,会代理到invoke方法中,数据从memberValues变量中取。

  • 相关阅读:
    Response.AppendHeader使用大全
    JS获取父框架的内容:获取子框架的内容:js框架应用
    各种好用的代码生成器
    Centos 64位上搭建Android
    WinForm 多线程
    GAC及其作用
    WPF 详解模板
    WPF控件开发基础(1)
    WPF:从WPF Diagram Designer Part 1学习控件模板、移动、改变大小和旋转
    告诫
  • 原文地址:https://www.cnblogs.com/strongmore/p/13282691.html
Copyright © 2011-2022 走看看