一. 注解的本质
我们知道,在平时的开发中,注解的运用是随处可见的,所以对于注解的运用,相信大家都不陌生。但我们在代码中使用的注解对象是怎么生成的呢?这些注解的底层是什么样的呢?我们先来看一段简单的代码:
@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()的内部究竟是如何运作的。
从以上图可以看出,内存中的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集合中将属性对应的值返回。