@ConditionalOnClass和@ConditionalOnMissingClass的神秘探索-程序员宅基地

技术标签: spring  jvm  java  maven  jar  SpringBoot  

疑问

  1. 为什么打开部分字节码文件(.class)文件,看到部分类报红,找不到该类,难道真的没有该类?那编译的时候不报错吗?
  2. Spring在启动的时候为什么会可以正常启动,没有该类还能正常启动?Spring是如何加载和处理这些类的呢?
  3. 项目启动后,该类会被JVM加载初始化吗?

疑点分析

  1. 这里我随便找了一个类,里面包含一些没有的class,见下图
    在这里插入图片描述

  2. 发现字节码文件(RxReactiveStreams.class)不存在,那我在另外一个比较完整的项目,找到该类属于[rxjava-reactive-streams-1.2.1.jar]包下面的
    在这里插入图片描述

  3. 这时候我回到本项目,探讨本项目的pom中是否包含此[rxjava-reactive-streams-1.2.1.jar]

借助于IDEA插件MavenHelper在【spring-cloud-openfeign-core-2.2.5.RELEASE.pom】中找到相关引用
在这里插入图片描述

  1. 查 看 引 用 , 里 面 有 s c o p e 、 o p t i o n a l 标 签 的 引 用 , 这 是 我 们 查 看 c l a s s 不 存 在 , 但 是 又 可 以 正 常 编 译 通 过 的 关 键 \color{red}{查看引用,里面有scope、optional标签的引用,这是我们查看class不存在,但是又可以正常编译通过的关键} ,scopeoptional,class,

简单说明下:

  • 这是关键: o p t i o n a l 为 t r u e 的 话 作 用 是 让 新 项 目 依 赖 本 项 目 的 时 候 , 不 去 依 赖 此 j a r 包 , 设 置 f a l s e , 新 项 目 就 会 能 引 用 到 此 j a r 包 \color{red}{optional为true的话作用是让新项目依赖本项目的时候,不去依赖此jar包,设置false,新项目就会能引用到此jar包} optionaltruejarfalsejar
  • compile,缺省值,适用于所有阶段,会随着项目一起发布。 编译范围依赖在所有的classpath 中可用,同时它们也会被打包

optional为true:这样的话只有直接引用[rxjava-reactive-streams-1.2.1.jar]的jar包才会引到,一些通过间接引用的则不回引用到,这就导致我们看到的时候找不到该class字节码文件;
关于scope、optional详细
- scope的讲解
- optional的讲解

  1. springboot Bean加载机制和选择机制
    原文链接:https://blog.csdn.net/swordcenter/article/details/98937826

springboot Bean加载分为Bean扫描注册和Bean初始化。

  • Bean扫描

springboot 通过ConfigurationClassBeanDefinitionReader读取@Configuration注解标识的类。在扫描候选资源时,spring并没有通过Classloader#loadClass()来加载class文件,而是通过Classloader.getResource()获得class二进制文件,通过ClassReader对二进制文件进行ASM语法解析,从而得到候选类和注解的元数据。也就是说在spring扫描需要加载的Bean时,所有候选类都没有加载初始化。

  • Bean加载判断

当spring获得候选bean的元信息时,需要判断这个bean是否真的需要被加载。这个就是spring的ConditionEvaluator体系。
比如在AnnotatedBeanDefinitionReader#doRegisterBean()中有判断:

AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
    
	return;
}

在获得ConditionalOnClass注解的元数据时还有个特殊处理,我们声明@ConditionalOnClass(A.class)时,使用的是Class类。为了后续操作时不引起class加载,AnnotatedElementUtils#getMergedAnnotationAttributes(element,annotationName,classValuesAsString,nestedAnnotationsAsMap)将class类转换成了class的名称字符串。
在最终判断class是否存在时,jvm其实还是抛出了ClassNotFoundException,只是异常被吞没了。(~ ̄▽ ̄)~

//OnClassCondition$MatchType.isPresent(String, ClassLoader) line: 219	

private static boolean isPresent(String className, ClassLoader classLoader) {
    
//省略若干行
	try {
    
		forName(className, classLoader);
		return true;
	}
	catch (Throwable ex) {
    
		return false;//我就在这里,只是你看不到    O(∩_∩)O
	}
}

所以关于这个问题的正确说法应该是,class类不存在时spring条件加载能够正常执行,而不是不报异常。
至此,当Class A不存在时,Bean B被注册到spring容器中,等待初始化。

  • Bean初始化

在初始化Bean时,AbstractAutowireCapableBeanFactory#createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)直接通过MethodProxy.invokeSuper()反射调用了C#getBeanB()。C#getBeanA()将永远不会被调用,默默地在角落里死去。。。

问题总结

  1. 为什么打开部分字节码文件(.class)文件,看到部分类报红,找不到该类,难道真的没有该类?那编译的时候不报错吗?
  • 存在该类,只是我们引用jar【B】包的时候,设置optional为true,也就是说【B】只属于直接上司【A】,【C】jar不能调遣【B】jar,【C】jar也不会引用【B】jar包,想要使用需要我们另外手动引用jar包【B】;这就导致我们没有手动引用【B】时,看不到该类;
  • 但是其本项目【A】在编译打包的时候,是可以进行编译的,因为【A】是【B】的直属上司
  1. Spring在启动的时候为什么会可以正常启动,没有该类还能正常启动?Spring是如何加载和处理这些类的呢?

springboot Bean加载机制和选择机制决定了项目可以正常启动,虽然没有该类,但是我们也没有用到该类呀,这就和Spring加载class字节码文件有关,Spring采用的Classloader.getResource()获得class二进制文件,通过ClassReader对二进制文件进行ASM语法解析,从而得到候选类和注解的元数据。也就是说在spring扫描需要加载的Bean时,所有候选类都没有加载初始化。

  1. 项目启动后,该类会被JVM加载初始化吗?

当spring获得候选bean的元信息时,需要判断这个bean是否真的需要被加载。这个就是spring的ConditionEvaluator体系。
比如在AnnotatedBeanDefinitionReader#doRegisterBean()中有判断

AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
    
	return;
}

在获得ConditionalOnClass注解的元数据时还有个特殊处理,我们声明@ConditionalOnClass(A.class)时,使用的是Class类。为了后续操作时不引起class加载,AnnotatedElementUtils#getMergedAnnotationAttributes(element,annotationName,classValuesAsString,nestedAnnotationsAsMap)将class类转换成了class的名称字符串。
在最终判断class是否存在时,jvm其实还是抛出了ClassNotFoundException,只是异常被吞没了。(~ ̄▽ ̄)~

//OnClassCondition$MatchType.isPresent(String, ClassLoader) line: 219	

private static boolean isPresent(String className, ClassLoader classLoader) {
    
//省略若干行
	try {
    
		forName(className, classLoader);
		return true;
	}
	catch (Throwable ex) {
    
		return false;//我就在这里,只是你看不到    O(∩_∩)O
	}
}

本文参考链接:https://blog.csdn.net/swordcenter/article/details/98937826

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/a656678879/article/details/112897924

智能推荐

大华工业相机SDK【QT+ C++】vs2015配置环境_c++调用华睿工业相机sdk-程序员宅基地

文章浏览阅读4.8k次,点赞3次,收藏34次。安装华睿相机的驱动:http://download.huaraytech.com/pub/sdk/Ver2.2.3/Windows/2.2.3版本里面增加QT调用的例程,很详细另有Document文件夹下的相关文件文档,配置可以进行参考本文对在调试需求下配置1、环境变量:自动安装的话,环境变量会自动添加,可以不动;2、3、解决方案,添加头文件和使用命名空间..._c++调用华睿工业相机sdk

java 库存 进销存 商户 多用户管理系统 SSM springmvc 项目源码-程序员宅基地

文章浏览阅读112次。统介绍:1.系统采用主流的 SSM 框架 jsp JSTL bootstrap html5 (PC浏览器使用)2.springmvc +spring4.3.7+ mybaits3.3 SSM 普通java web(非maven, 附赠pom.xml文件) 数据库:mysql3.开发工具:myeclipse eclipse idea 均可, 没有限制. 我这边myeclipse 2014 导出..._spring库存系统

基于微信小程序的自习室预约系统的设计与实现-程序员宅基地

文章浏览阅读1.6k次,点赞26次,收藏18次。伴随着信息技术与互联网技术的不断发展,校园也进到了一个新的信息化时代,传统管理技术性没法高效率、容易地管理自习室预约信息内容。为了实现时代的发展必须,提升自习室预约高效率,各种各样自习室预约体系应时而生,自习室预约管理系统的实现是信息内容时代浪潮时代的产物之一。一切系统都要遵循系统设计的基本流程。它还需要通过市场调查、需求分析报告、汇总设计、详尽设计以及测试,根据node语言表达设计,完成实验室预约管理系统。

2024年材料员-通用基础(材料员)证考试题库及材料员-通用基础(材料员)试题解析-程序员宅基地

文章浏览阅读320次,点赞9次,收藏5次。2024年材料员-通用基础(材料员)证考试题库及材料员-通用基础(材料员)试题解析是安全生产模拟考试一点通结合(安监局)特种作业人员操作证考试大纲和(质检局)特种设备作业人员上岗证考试大纲随机出的材料员-通用基础(材料员)在线模拟考试题练习。对此,正确的处理方式应当是( )。49、【单选题】按照标准制作方法制成边长为150mm的标准立方体试件,在标准条件(温度20℃±2℃,相对湿度为95%以上)下养护( ),然后采用标准试验方法测得的极限抗压强度值,称为混凝土的立方体抗压强度。安全 生产模 拟考试一点通。

ORA-12505, TNS:listener does not currently know of SID given in connect descriptor解决方法-程序员宅基地

文章浏览阅读6.3k次,点赞3次,收藏7次。报错原因:通过报错内容 “TNS:listener does not currently know of SID given in connect descriptor”,可以分析出,SID 不正确;jdbc连接数据库的时候,使用数据库的 sid_name,而不是数据库的 services_name(服务名)原文链接:https://blog.csdn.net/weixin_45894220/article/details/129839666。//监听sid服务,表示sid为orcl。_ora-12505

QQ在线交谈代码-程序员宅基地

文章浏览阅读45次。http://www.betool.com.cn/api/qqchat.aspx?qq=862238001替换qq号码即可转载于:https://www.cnblogs.com/max-stone/p/3684688.html_与qq交谈的代码

随便推点

Java基础教程(3)-Java变量和数组-程序员宅基地

文章浏览阅读237次。定义一个整型变量num:num是标识符号,也叫变量名称,int是变量类型, 10是初始值可选变量分为两种:基本类型的变量和引用类型的变量。在Java中,所有的变量必须先声明再使用。基本的变量声明方法如下:type是Java的基本类型之一,或类及接口类型的名字。标识符(identifier)是变量的名字,指定一个等号和一个值来初始化变量。请记住初始化表达式必须产生与指定的变量类型一样(或兼容)的变量。声明指定类型的多个变量时,使用逗号将各变量分开;

yarn 安装依赖报错 error An unexpected error occurred: https://registry.yarnpkg.com“-程序员宅基地

文章浏览阅读4.1k次,点赞3次,收藏4次。yarn 安装依赖报错 error An unexpected error occurred: https://registry.yarnpkg.com“

IDEA怎么快速注释多行_idea多行注释-程序员宅基地

文章浏览阅读1.2k次。二、文档注释:输入/**后,按Enter,会自动根据参数和返回值生成注释模板。2.多行注释:Ctrl +Shift+ /一、选中内容然后按需要按快捷键。1.单行注释:Ctrl + /_idea多行注释

蝴蝶飞舞-程序员宅基地

文章浏览阅读894次。 这个哥们儿的手指蛮灵活啊,他好像叫balian fabisonghttp://www.videoplayer.es/video/indexthumbs-instructional-balisong-trick-trip-hop-beat/tVAJDI5dRFs/http://www.youtube.com/watch?v=o8_KlXPGHW8http://www.youtube.c

Grafana 提醒注意严重的认证绕过漏洞-程序员宅基地

文章浏览阅读928次。聚焦源代码安全,网罗国内外最新资讯!编译:代码卫士Grafana 为其多个应用版本发布安全修复方案,修复了一个严重漏洞,它可使攻击者绕过认证并接管使用 Azure Active Directory 用于认证的任何 Grafana 账户。该漏洞的编号是CVE-2023-3128,CVSS v3.1评分是9.4。Grafana 是一款广泛使用的开源分析和交互可视化 app,通过大量监控平台和应用程序..._grafana6.7.6版本漏洞

django 常见过滤器-程序员宅基地

文章浏览阅读52次。一、形式:小写{{ name | lower }} 二、过滤器是可以嵌套的,字符串经过三个过滤器,第一个过滤器转换为小写,第二个过滤器输出首字母,第三个过滤器将首字母转换成大写标签{{ str|lower|first|upper }} 三、过滤器的参数显示前30个字{{ bio | truncatewords:"30..._django前台{{ str|upper|lower|upper }}