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运行时生成类元数据,初始化注解信息的方式的更多相关文章
- 在运行时生成C# .NET类
本文译自:Generating C# .NET Classes at Runtime 作者:WedPort 在我的C#职业生涯中,有几次我不得不在运行时生成新的类型.希望把它写下来能帮助有相同应 ...
- 如何获取java运行时动态生成的class文件?
查看运行时生成的文件,以更清楚运行情况. 查看动态生成的类,一般有两个方法: 1. 使用据说是jdk自带包sa-jdi.jar里的工具. 其中,不想自己搞,当然就利用下,sa-jdi.jar 里自带的 ...
- Javassist之使用字节码在运行时生成新的类 01
介绍 Javassist是一个开源的分析.编辑和创建Java字节码的类库.是由东京工业大学的数学和计算机科学系的 Shigeru Chiba (千叶 滋)所创建的.它已加入了开放源代码JBoss 应用 ...
- Java运行时环境---内存划分
背景:听说Java运行时环境的内存划分是挺进BAT的必经之路. 内存划分: Java程序内存的划分是交由JVM执行的,而不像C语言那样需要程序员自己买单(C语言需要程序员为每一个new操作去配对del ...
- 《深入理解java虚拟机》:类的初始化
深入理解java虚拟机>:类的初始化 类从被载入到虚拟机内存中開始.到卸载出内存为止,它的整个生命周期包含:载入.验证.准备.解析.初始化.使用和卸载七个阶段.当中验证.准备.解析3个部分统称为 ...
- java运行时内存模式学习
学习java运行时内存模式: 各区介绍: 方法区(线程共享):用于存放被虚拟机加载的类的元数据:静态变量,常量,以及编译和的代码(字节码),也称为永久代(所有该类的实例被回收,或者此类classLoa ...
- 读书笔记-浅析Java运行时数据区
作为一个 Java 为主语言的程序员,我偶尔也需要 用 C/C++ 写程序,在使用时让我很烦恼的一件事情就是需要对 new 出来的对象进行 delete/free 操作,我老是担心忘了这件事情,从而导 ...
- JVM发展史和Java运行时内存区域
目前三大主流JVM: Sun HotSpot:Sun于1997年收购Longview Technologies公司所得.Sun于2009年被Oracle收购. BEA JRockit:BEA于2002 ...
- 理解JVM之JAVA运行时内存区域
java运行时内存区域划分为方法区,堆区,虚拟机栈区,本地方法栈,程序计数器.其中方法区跟堆区是线程共享的数据区,其他的是线程私有的数据区. 1.程序计数器 程序计数器(PC)是一块较小的内存,他是存 ...
- 【转】Sqlite 混合模式程序集是针对“v2.0.50727”版的运行时生成的,在没有配置其他信息的情况下,无法在 4.0 运行时中加载该...
开发环境: vs2010+.net framework 4.0+ System.Data.SQLite.DLL (2.0)今天在做Sqlite数据库测试,一运行程序在一处方法调用时报出了一个异常 混合 ...
随机推荐
- echarts更改x和y轴的颜色
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...
- ABP vNext系列文章10---分布式事务集成netcore.Cap
最近项目中要用到分布式事务功能,调研了DTM和Cap,最终确定用Cap来实现,Cap支持最终一致性,项目中采用MQ作为消息中间件,数据库用的mysql,集成步骤如下: 1.在需要发布消息的服务中引入如 ...
- RabbitMQ原理详解
RabbitMQ:我们通常谈到消息队列,就会联想到这其中的三者:生产者.消费者和消息队列,生产者将消息发送到消息队列,消费者从消息队列中获取消息进行处理.对于RabbitMQ,它在此基础上做了一层抽象 ...
- 从零开始配置vim(26)——LSP UI 美化
之前我们通过几个实例演示如何配置其他语言的lsp服务,相信各位小伙伴碰到其他的编程语言也能熟练的配置它对应的lsp服务.本篇讲作为一个补充,我们来优化一下LSP 相关的显示 配置 UI 原始的 lsp ...
- 【分享】从Mybatis源码中,学习到的10种设计模式
作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言:小镇卷码家 总有不少研发伙伴问小傅哥:"为什么学设计模式.看框架源码.补技 ...
- 监控Celery不一定非要使用Flower
运维平台中有许多的周期/定时/异步任务,例如证书扫描.数据备份.日志清理.线上作业等等,这些任务的执行都是借助于Celery来完成的.任务多了之后就会遇到一系列的问题,例如我之前写过的将任务分多队列来 ...
- 解决github无法进入问题,DNS加速
1.电脑的hosts文件在下面这个地址,找到hosts文件 C:\Windows\System32\Drivers\etc 2.进入文件夹目录etc 找到hosts文件,使用记事本编辑 3.进入这个网 ...
- 遥感图像处理笔记之【Сrор field boundary detection: approaches and main challenges】
遥感图像处理学习(6) 前言 遥感系列第6篇.遥感图像处理方向的学习者可以参考或者复刻 本文初编辑于2023年12月16日 2024年1月24日搬运至本人博客园平台 文章标题:Сrор field b ...
- Windows 7 Ultimate with Service Pack 1 (x64)
Windows 7 Ultimate with Service Pack 1 (x64) 链接:https://pan.baidu.com/s/1ZHODDFlJPLQ3ydxZ4rfc3g 提取码: ...
- SpringBoot实现统一异常处理
目录 前言 实现步骤 定义统一响应对象类 定义业务异常枚举接口和实现 定义业务异常基类 定义全局异常处理切面 测试和验证 总结 前言 近日心血来潮想做一个开源项目,目标是做一款可以适配多端.功能完备的 ...