问题前因

在一次技术升级中, 把分布式配置中心组件由百度的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运行时生成类元数据,初始化注解信息的方式的更多相关文章

  1. 在运行时生成C# .NET类

    ​本文译自​:​Generating C# .NET Classes at Runtime 作者:WedPort 在我的C#职业生涯中,有几次我不得不在运行时生成新的类型.希望把它写下来能帮助有相同应 ...

  2. 如何获取java运行时动态生成的class文件?

    查看运行时生成的文件,以更清楚运行情况. 查看动态生成的类,一般有两个方法: 1. 使用据说是jdk自带包sa-jdi.jar里的工具. 其中,不想自己搞,当然就利用下,sa-jdi.jar 里自带的 ...

  3. Javassist之使用字节码在运行时生成新的类 01

    介绍 Javassist是一个开源的分析.编辑和创建Java字节码的类库.是由东京工业大学的数学和计算机科学系的 Shigeru Chiba (千叶 滋)所创建的.它已加入了开放源代码JBoss 应用 ...

  4. Java运行时环境---内存划分

    背景:听说Java运行时环境的内存划分是挺进BAT的必经之路. 内存划分: Java程序内存的划分是交由JVM执行的,而不像C语言那样需要程序员自己买单(C语言需要程序员为每一个new操作去配对del ...

  5. 《深入理解java虚拟机》:类的初始化

    深入理解java虚拟机>:类的初始化 类从被载入到虚拟机内存中開始.到卸载出内存为止,它的整个生命周期包含:载入.验证.准备.解析.初始化.使用和卸载七个阶段.当中验证.准备.解析3个部分统称为 ...

  6. java运行时内存模式学习

    学习java运行时内存模式: 各区介绍: 方法区(线程共享):用于存放被虚拟机加载的类的元数据:静态变量,常量,以及编译和的代码(字节码),也称为永久代(所有该类的实例被回收,或者此类classLoa ...

  7. 读书笔记-浅析Java运行时数据区

    作为一个 Java 为主语言的程序员,我偶尔也需要 用 C/C++ 写程序,在使用时让我很烦恼的一件事情就是需要对 new 出来的对象进行 delete/free 操作,我老是担心忘了这件事情,从而导 ...

  8. JVM发展史和Java运行时内存区域

    目前三大主流JVM: Sun HotSpot:Sun于1997年收购Longview Technologies公司所得.Sun于2009年被Oracle收购. BEA JRockit:BEA于2002 ...

  9. 理解JVM之JAVA运行时内存区域

    java运行时内存区域划分为方法区,堆区,虚拟机栈区,本地方法栈,程序计数器.其中方法区跟堆区是线程共享的数据区,其他的是线程私有的数据区. 1.程序计数器 程序计数器(PC)是一块较小的内存,他是存 ...

  10. 【转】Sqlite 混合模式程序集是针对“v2.0.50727”版的运行时生成的,在没有配置其他信息的情况下,无法在 4.0 运行时中加载该...

    开发环境: vs2010+.net framework 4.0+ System.Data.SQLite.DLL (2.0)今天在做Sqlite数据库测试,一运行程序在一处方法调用时报出了一个异常 混合 ...

随机推荐

  1. uni-app快速导入自己需要的插件

    在uni-app中快速导入自己需要的插件 在uni-app的官网上;找到自己需要的插件: 然后点击右侧顶部的[使用HX导入]这一步的前提是你必须要用自己的账好登录: 然后选择你要导入哪一个项目 然后就 ...

  2. C/C++ Crypto密码库调用方法

    Crypto 库是C/C++的加密算法库,这个加密库很流行,基本上涵盖了市面上的各类加密解密算法,以下代码是我在学习是总结的,放到这里用于后期需要时能够快速解决问题. 项目地址:https://www ...

  3. centos离线安装chrony

    环境 CentOS Linux release 7.9.2009 (Core) 工具 chrony-2.2.1.tar.gz 下载地址:https://download.tuxfamily.org/c ...

  4. mysql删除表中重复记录

    1.创建测试表,并插入数据 create table test( id int(11) primary key auto_increment comment '用户编号', username varc ...

  5. 数学和CNN里面的卷积和互相关

    卷积和互相关 nndl上CNN这章的互相关讲的比较晦涩,简单辨析一下书上的互相关 A.1 数学意义上的卷积就是将卷积核进行翻转之后再进行我们熟悉CNN上的卷积运算 同时互相关就是不将卷积核翻转直接CN ...

  6. 深入浅出Java多线程(八):volatile

    引言 大家好,我是你们的老伙计秀才!今天带来的是[深入浅出Java多线程]系列的第八篇内容:volatile.大家觉得有用请点赞,喜欢请关注!秀才在此谢过大家了!!! 在当今的软件开发领域,多线程编程 ...

  7. ASP.NET Core分布式项目实战(oauth2 + oidc 实现 client部分)--学习笔记

    任务16:oauth2 + oidc 实现 client部分 实现 client 之前启动一下上一节的 server,启动之前需要清除一些代码 注释 Program 的 MigrateDbContex ...

  8. NC16591 [NOIP2010]关押罪犯

    题目链接 题目 题目描述 S 城现有两座监狱,一共关押着N 名罪犯,编号分别为1~N.他们之间的关系自然也极不和谐.很多罪犯之间甚至积怨已久,如果客观条件具备则随时可能爆发冲突.我们用"怨气 ...

  9. Python 中Time 模块

    python的time内置模块是一个与时间相关的内置模块,很多人喜欢用time.time()获取当前时间的时间戳,利用程序前后两个时间戳的差值计算程序的运行时间,如下: 1.使用time.time() ...

  10. 一个让java程序员有杀人的冲动的Xerces冲突问题

    History Xerces是Java生态圈使用最广泛的XML解析器,基本上所有的类库和框架都会在一定程度上使用它(即使没有直接引用,也有可能间接引用) Xerces在官网中发布的包是没有标注版本的, ...