【工作技术栈】【源码解读】一次springboot注入bean失败问题的排查过程

目录

前言

对这次的过程排查如果要形容的话,我觉得更像是悬疑剧,bean not found 这种错误,已经看腻了,甚至有时候都看不起这种错误,但是似乎这个想法被springboot听见了,所以这几天他就给我来了一记大耳刮子。。。

现象

版本(抛开版本就是耍流氓~)

jdk8

sprintboot 2.3.12

现象

首先我们的项目存在一个名叫common的项目,另一个叫fusionXXX的微服务依赖了这个common的项目,并且common中存在一个beandefination注册的过程(具体可以参考mybatis的mapper),因为要实现动态代理。

ok,当前idea同时打开了common项目和fusionXXX的项目,并且在一个window下,无需install到maven本地就可以直接运行,运行成功没有任何问题。

但是,现在我们将common项目install进了自己的maven仓库之后,用idea直接打开fusionXXX项目,这个时候开始运行fusionXXX项目,使用@Autowire来获取我们自己的bean,结果发现bean注入不进来,报错 bean not found

这是什么原因???

分析原因

因为二合一的项目确实可以运行,所以我认为代码没有问题,一定是springboot出了问题!具体来说应该是我使用springboot出了问题,这个分析方向对最终问题的排查起到了关键性的作用。

没啥好说的,直接开debug模式找到bean not found的堆栈处,自己去logback.xml或者log4j.xml 文件中设置日志级别,设置之后springboot会将初始化的过程打印出来,报错之后会直接打印堆栈日志,这样直接就可以点进来看了,我这边直接运行二合一和单fusionXXX来比较哪里出现了不一致导致了bean没有注入,最终定位到这里两个项目出现了不一致的问题!

因为是Autowire注解,该注解先通过Type来获取bean,获取不到之后再使用name的方式获取bean,二合一在这里返回的String[] 存在一个bean的名称就是我给自己的bean起的名字,但是单fusionXXX就没有,所以我们继续看503行,因为cache刚开始是没有值的,与此同时我们也了解到springboot的bean查找存在一层缓存(不虚此行!)

这里我们看到如果type匹配到了才会添加这个beanName(这里的beanName是循环的临时变量,这里使用了循环的方式进行type的匹配,循环的就是spring实例化bean前的beanDefination列表,只有spring第一次实例化bean的情况才会这样循环,目前我们代码中有1222个定义,所以启动起来最耗费时间的应该就是这个过程了。),二合一的536行已经匹配到了,但是fusionXXX没有匹配到那么我么继续追踪下去。

通过层层的比对,最终发现不一致的地方在348行,单fusionXXX在这里直接进入了349行,我们看下最终返回false的条件为什么是false

终于到了重要的地方,这里我们发现单fusionXXX的两个Class不是一个对象,但是二合一的都是一个对象


上面两个图如果不注意正常来讲ClassName都是一样的,但是确实无法相互赋值,为什么呢?ClassLoader不一样。。。这种真的很难排查。。。那么现在分析一下为什么不一样

首先,如果两个类的ClassLoader不一样,那么jvm会认为两个类不是同一个类,即使两个类的reference一模一样,就如上面所看

ChatGPT说RestartClassLoader是springboot中的devtools的一个类加载器,其功能是为了热加载类文件设计的,当二合一运行时,在idea中,除了jar包以外的所有class都是可编辑的,所以他们都统一被RestartClassLoader加载,因此在二合一中的两个类的加载器都是RestartClassLoader

如果不是二合一,也就是单fusionXXX的时候,common模块以jar包形式进来,在beandefination加载的时候,jar包中的类提前手动(我自己写的)被java中的应用加载器加载(AppClassLoader ,java经典三层类加载器 :bootstrap -> ext->app),而在运行过程中,比对前spring确实找到了对应的类,但是却使用了devtools中的classloader进行了加载,从而导致了在匹配Type的时候无法匹配而最终找不到bean。

被restartclassloader加载的类可以在class文件修改的时候重新加载从而起到热修改的作用。

解决方法

将devtools注释掉即可,所有类都通过java的appclassloader加载就好了,但是不支持热修改了

思考感悟

这次的排查使用了不少的时间才排查到,一个很常见的错误本以为很快就能解决,没想到下面藏了一个冰山。。。

整个项目仅仅在本地运行不起来,打包成功后都能够运行,总的来说不会影响到业务,但是后面引入jar包的时候也应该对jar有一定的了解。

相关推荐
Lee川4 小时前
LangChain 加持:后端 AI 流式对话的优雅实现
后端
Javatutouhouduan4 小时前
2026Java面试的正确打开方式!
java·高并发·java面试·java面试题·后端开发·java编程·java八股文
JAVA面经实录9175 小时前
Java初级最终完整版学习路线图
java·spring·eclipse·maven
子兮曰5 小时前
Bun v1.3.14 深度解析:Image API、HTTP/3、全局虚拟存储与五十项变革
前端·后端·bun
Cat_Rocky6 小时前
k8s-持久化存储,粗浅学习
java·学习·kubernetes
ltl6 小时前
Self-Attention:让序列自己看自己
后端
楼兰公子6 小时前
buildroot 在编译rust时裁剪平台类型数量的方法
开发语言·后端·rust
知识领航员6 小时前
蘑兔AI音乐深度实测:功能拆解、实测表现与适用场景
java·c语言·c++·人工智能·python·算法·github
吴声子夜歌6 小时前
Go——并发编程
开发语言·后端·golang
释怀°Believe6 小时前
Spring解析
java·后端·spring