一. 注解的本质

我们知道,在平时的开发中,注解的运用是随处可见的,所以对于注解的运用,相信大家都不陌生。但我们在代码中使用的注解对象是怎么生成的呢?这些注解的底层是什么样的呢?我们先来看一段简单的代码:

@TestAnno("hello java")
public class Test {
    public static void main(String[] args) throws Exception {
        System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        Class clazz = Test.class;
        TestAnno testAnno = (TestAnno) clazz.getAnnotation(TestAnno.class);
        String value = testAnno.value();
        System.out.println(value);
    }
}

@Retention(RetentionPolicy.RUNTIME)
@interface TestAnno{
    public String value();
}

以上代码的运行结果,相信大家都可以判断出来,那么这段代码中获取的testAnno对象到底是什么呢?我们打断点运行一下,看看获取到的TestAnno到底是什么,testAnno.value()的内部究竟是如何运作的。

java 注解排除映射 java注解底层实现原理_java

从以上图可以看出,内存中的testanno是一个proxy代理对象,我们调用testanno.value()方法时,会触发代理对象AnnotationInvocationHandler对象的invoke方法执行,然后在这个方法里再从一个map集合中将value()方法的值返回。

那么该注解的内部原理,是否真如上述描述的一致呢?其实我们可以从源码中找到答案!

二. 注解源码分析

现在让我们一切先从应用层的api调用开始:class.getAnnotation(TestAnno.class)

public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {
    if (annotationClass == null)
        throw new NullPointerException();
    initAnnotationsIfNecessary();
    return (A) annotations.get(annotationClass);
}

我们可以看到,在上述方法中,内部调用了initAnnotationsIfNecessary()方法,接下来我们看看该方法的内部实现。initAnnotationsIfNecessary()源码如下:

private synchronized void initAnnotationsIfNecessary() {
    clearCachesOnClassRedefinition();
    if (annotations != null)
        return;
    declaredAnnotations = AnnotationParser.parseAnnotations(
        getRawAnnotations(), getConstantPool(), this);
    Class<?> superClass = getSuperclass();
    if (superClass == null) {
        annotations = declaredAnnotations;
    } else {
        annotations = new HashMap<>();
        superClass.initAnnotationsIfNecessary();
        for (Map.Entry<Class<? extends Annotation>, Annotation> e : superClass.annotations.entrySet()) {
            Class<? extends Annotation> annotationClass = e.getKey();
            if (AnnotationType.getInstance(annotationClass).isInherited())
                annotations.put(annotationClass, e.getValue());
        }
        annotations.putAll(declaredAnnotations);
    }
}

我们来看看最关键的部分:

AnnotationParser.parseAnnotations(getRawAnnotations(), getConstantPool(),this)

AnnotationParser.parseAnnotations2(byte[]rawAnnotations,ConstantPoolconstPool,Class<?>container,Class<?extends Annotation[]selectAnnotationClasses)

AnnotationParser.parseAnnotation2(ByteBufferbuf,ConstantPool constPool,Class<?>container,booleanexceptionOnMissingAnnotationClass,Class<? extends Annotation>[] selectAnnotationClasses)

public static Map<Class<? extends Annotation>, Annotation> parseAnnotations(
            byte[] rawAnnotations,
            ConstantPool constPool,
            Class<?> container) {
    if (rawAnnotations == null)
        return Collections.emptyMap();
    try {
        return parseAnnotations2(rawAnnotations, constPool, container, null);
    } catch(BufferUnderflowException e) {
        throw new AnnotationFormatError("Unexpected end of annotations.");
    } catch(IllegalArgumentException e) {
        // Type mismatch in constant pool
        throw new AnnotationFormatError(e);
    }
}

我们看到,在以上方法中调用了parseAnnotations2(byte[] rawAnnotations,ConstantPool constPool,Class<?> container,Class<? extends Annotation>[] selectAnnotationClasses)

private static Map<Class<? extends Annotation>, Annotation> parseAnnotations2(
            byte[] rawAnnotations,
            ConstantPool constPool,
            Class<?> container,
            Class<? extends Annotation>[] selectAnnotationClasses) {
    Map<Class<? extends Annotation>, Annotation> result =
        new LinkedHashMap<Class<? extends Annotation>, Annotation>();
    ByteBuffer buf = ByteBuffer.wrap(rawAnnotations);
    int numAnnotations = buf.getShort() & 0xFFFF;
    for (int i = 0; i < numAnnotations; i++) {
        Annotation a = parseAnnotation2(buf, constPool, container, false, selectAnnotationClasses);
        if (a != null) {
            Class<? extends Annotation> klass = a.annotationType();
            if (AnnotationType.getInstance(klass).retention() == RetentionPolicy.RUNTIME &&
                result.put(klass, a) != null) {
                    throw new AnnotationFormatError(
                        "Duplicate annotation for class: "+klass+": " + a);
        }
    }
    }
    return result;
}

然后我们看到,在以上方法中调用了parseAnnotation2(ByteBuffer buf,ConstantPool constPool,Class<?> container,boolean exceptionOnMissingAnnotationClass,Class<? extends Annotation>[] selectAnnotationClasses),在该方法体中有一段很关键的逻辑。

private static Annotation parseAnnotation2(ByteBuffer buf,
                  ConstantPool constPool,
                  Class<?> container,
                  boolean exceptionOnMissingAnnotationClass,
                  Class<? extends Annotation>[selectAnnotationClasses) {
        ....
    Map<String, Class<?>> memberTypes = type.memberTypes();
    Map<String, Object> memberValues =
        new LinkedHashMap<String, Object>(type.memberDefaults());
    int numMembers = buf.getShort() & 0xFFFF;
    for (int i = 0; i < numMembers; i++) {
        int memberNameIndex = buf.getShort() & 0xFFFF;
        String memberName = constPool.getUTF8At(memberNameIndex);
        Class<?> memberType = memberTypes.get(memberName);
        if (memberType == null) {
            // Member is no longer present in annotation type; ignore it
            skipMemberValue(buf);
        } else {
            Object value = parseMemberValue(memberType, buf, constPool, container);
            if (value instanceof AnnotationTypeMismatchExceptionProxy)
                ((AnnotationTypeMismatchExceptionProxy) value).
                    setMember(type.members().get(memberName));
            memberValues.put(memberName, value);
        }
    }
    return annotationForMap(annotationClass, memberValues);
}

而在该方法中,有两个需要我们关注的方法:parseMemberValue和annotationForMap,其中的parseMemberValue,就是用来解析我们使用注解时配置的参数值到一个Map集合中。

public static Object parseMemberValue(Class<?> memberType,
                                      ByteBuffer buf,
                                      ConstantPool constPool,
                                      Class<?> container) {
    Object result = null;
    int tag = buf.get();
    switch(tag) {
      case 'e':
          return parseEnumValue((Class<? extends Enum<?>>)memberType, buf, constPool, container);
      case 'c':
          result = parseClassValue(buf, constPool, container);
          break;
      case '@':
          result = parseAnnotation(buf, constPool, container, true);
          break;
      case '[':
          return parseArray(memberType, buf, constPool, container);
      default:
          result = parseConst(tag, buf, constPool);
    }
    if (!(result instanceof ExceptionProxy) &&
        !memberType.isInstance(result))
        result = new AnnotationTypeMismatchExceptionProxy(
            result.getClass() + "[" + result + "]");
    return result;
}

如果注解的参数类型是基本数据类型,然后调用parseConst方法进行数据解析。

private static Object parseConst(int tag,
                                 ByteBuffer buf, ConstantPool constPool) {
    int constIndex = buf.getShort() & 0xFFFF;
    switch(tag) {
      case 'B':
        return Byte.valueOf((byte) constPool.getIntAt(constIndex));
      case 'C':
        return Character.valueOf((char) constPool.getIntAt(constIndex));
      case 'D':
        return Double.valueOf(constPool.getDoubleAt(constIndex));
      case 'F':
        return Float.valueOf(constPool.getFloatAt(constIndex));
      case 'I':
        return Integer.valueOf(constPool.getIntAt(constIndex));
      case 'J':
        return Long.valueOf(constPool.getLongAt(constIndex));
      case 'S':
        return Short.valueOf((short) constPool.getIntAt(constIndex));
      case 'Z':
        return Boolean.valueOf(constPool.getIntAt(constIndex) != 0);
      case 's':
        return constPool.getUTF8At(constIndex);
      default:
        throw new AnnotationFormatError(
            "Invalid member-value tag in annotation: " + tag);
    }
}

从上述代码中可以看出,所有基本数据类型的数据都是从类的常量池中获取,到此注解的参数值就都封装到了一个Map集合中。接下来我们来看annotationForMap方法:

public static Annotation annotationForMap(
    Class<? extends Annotation> type, Map<String, Object> memberValues){
    return (Annotation) Proxy.newProxyInstance(
        type.getClassLoader(), new Class[] { type },
        new AnnotationInvocationHandler(type, memberValues));
}

该方法非常简单,就是生成了一个动态代理对象,并在构建AnnotationInvocationHandler对象时,将之前解析到的注解参数的Map集合传入其中。那么我们在调用动态代理对象的方法时,会触发AnnotationInvocationHandler的invoke方法,接下来我们看看该方法内部是否从Map集合中将需要的值返回:

class AnnotationInvocationHandler implements InvocationHandler, Serializable {
    private static final long serialVersionUID = 6182022883658399397L;
    private final Class<? extends Annotation> type;
    private final Map<String, Object> memberValues;
    AnnotationInvocationHandler(Class<? extends Annotation> type, Map<String, Object> memberValues) {
        ...
        this.memberValues = memberValues;
    }
    public Object invoke(Object proxy, Method method, Object[] args) {
        String member = method.getName();
        ...
        // Handle annotation member accessors
        Object result = memberValues.get(member);
        if (result == null)
            throw new IncompleteAnnotationException(type, member);
        if (result instanceof ExceptionProxy)
            throw ((ExceptionProxy) result).generateException();
        if (result.getClass().isArray() && Array.getLength(result) != 0)
            result = cloneArray(result);
        return result;
    }
        ...
}

我们可以看到,在该invoke方法内部正是从Map集合中将该值获取返回。

三. 总结

最后我们再来总结一下。注解的内部原理是在解析类注解时,将类注解上配置的值存储到一个Map集合中,并且基于注解接口生成一个动态代理对象,同时在构建该动态代理对象的AnnotationInvocationHandler对象时将之前解析到的Map集合传入。接下来在调用注解对象获取属性值时,实际调用的其实是动态代理对象的获取属性值的方法,从而触发AnnotationInvocationHandler的invoke方法执行,在该方法内,从Map集合中将属性对应的值返回。