Java运行时生成类元数据,初始化注解信息的方式
2022/8/4 1:22:52
本文主要是介绍Java运行时生成类元数据,初始化注解信息的方式,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
问题前因
在一次技术升级中, 把分布式配置中心组件由百度的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的情况:
这篇关于Java运行时生成类元数据,初始化注解信息的方式的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-12-23线下车企门店如何实现线上线下融合?
- 2024-12-23鸿蒙Next ArkTS编程规范总结
- 2024-12-23物流团队冬至高效运转,哪款办公软件可助力风险评估?
- 2024-12-23优化库存,提升效率:医药企业如何借助看板软件实现仓库智能化
- 2024-12-23项目管理零负担!轻量化看板工具如何助力团队协作
- 2024-12-23电商活动复盘,为何是团队成长的核心环节?
- 2024-12-23鸿蒙Next ArkTS高性能编程实战
- 2024-12-23数据驱动:电商复盘从基础到进阶!
- 2024-12-23从数据到客户:跨境电商如何通过销售跟踪工具提升营销精准度?
- 2024-12-23汽车4S店运营效率提升的核心工具