在一次技术升级中, 把分布式配置中心组件由百度的Disconf
改成 Nacos , 在对项目进行改造时, 首先将所有Disconf客户端依赖全部移除后, 依赖的封装的jar包中, 所有依赖DIsconf 注解的配置类, 在项目启动时, 本该理所当然的报找不到类信息
, 如下:
但是, 项目却顺利启动成功, 仅仅只是没有获取到Disconf配置中心的数据而已,
后续查看了此类的Class元数据信息, 也能顺利的获取到, 说明类加载器, 在加载此class信息时, 并没有因为类注解的没有加载到,而报错(例如如果父类不存在, 则类无法成功加载), 但是在仔细查看已经加载的注解信息时, 发现不存在的注解被忽略,如下图:
分别为存在的注解信息, 不存在的注解信息,被忽略;
为了验证这个结果, 可以将断点打到类加载器加载此类的时候,并生成Class对象时(类元数据绑定在Class对象中), 如何加载注解信息, 在Class 对象中, 有这个方法,此方法初始化类的注解信息:
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); }
其中getRawAnnotations()
方法和getConstantPool()
方法, 都是native原生方法, 大概是获取类注解的符号在内存中字节码信息和地址信息
并交由AnnotationParser
对象的parseAnnotations
处理,
public static Map<Class<? extends Annotation>, Annotation> parseAnnotations(byte[] var0, ConstantPool var1, Class<?> var2) { if (var0 == null) { return Collections.emptyMap(); } else { try { return parseAnnotations2(var0, var1, var2, (Class[])null); } catch (BufferUnderflowException var4) { throw new AnnotationFormatError("Unexpected end of annotations."); } catch (IllegalArgumentException var5) { throw new AnnotationFormatError(var5); } } }
没有多余的处理过程, 再看parseAnnotations2
方法
private static Map<Class<? extends Annotation>, Annotation> parseAnnotations2(byte[] var0, ConstantPool var1, Class<?> var2, Class<? extends Annotation>[] var3) { LinkedHashMap var4 = new LinkedHashMap(); ByteBuffer var5 = ByteBuffer.wrap(var0); int var6 = var5.getShort() & '\uffff'; // 遍历类注解上声明的注解信息 for(int var7 = 0; var7 < var6; ++var7) { // 获取实际注解对象 Annotation var8 = parseAnnotation2(var5, var1, var2, false, var3); // 如果获取不为空,则返回, 但是不为空, 则没有进行抛错, 而是跳过 if (var8 != null) { Class var9 = var8.annotationType(); if (AnnotationType.getInstance(var9).retention() == RetentionPolicy.RUNTIME && var4.put(var9, var8) != null) { throw new AnnotationFormatError("Duplicate annotation for class: " + var9 + ": " + var8); } } } return var4; }
根据上面的代码可以看到, 如果注解信息没有找到, 并没有抛错处理, 看下debug的情况: