YON Blog

BeanCreationException Related cause

昨天帮同事查一个诡异的 Spring 启动问题,应用里只要引用一个公司中间件的 Spring 配置就启动不起来,但是这个引用的配置里就配置了两个 bean,bean 的功能也非常简单,完全不像是会影响启动的样子。排查大半天后发现,原来是应用里写了一个拦截器,只要 bean 名字以 Processor 结尾就会被拦截,引用的 Spring 配置里有一个 BeanPostProcessor,所以很自然就被拦截到了。

其实报错日志里是有相关的报错的,报错的点就在拦截器里。之所以我们都没有注意是因为看报错日志习惯性的看最后的报错,而这次最后的报错其实并不是导致失败的报错日志,这些报错日志是 Spring 的 related cause。啥是 related cause 呢?Spring 在 Bean 初始化过程中是允许一些报错的,例如一些 Bean 有循环依赖问题,这时候抛出的异常就会被记录下来(参考 org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#suppressedExceptions),然后最终 BeanCreationException 抛出时就会打印真正的异常堆栈同时把 suppressedExceptions 作为 related cause 打印出来。这次就是被这个 related cause 给误导了排查方向。

如果我们对 Spring 非常了解,那么这个问题应该是可以很快定位的,但是我们很多时候排查问题都是对底层没那么了解的,那是不是就没有什么办法了呢?不是的,查问题也是有方法论的。第一步肯定是找到问题的一些特征,去网上搜,看看是不是别人已经遇到了类似的问题。如果第一步失败了,那么我们首先要做的就是要能复现问题,而且是要能快速的复现,像这次排查问题一开始是在远端机器复现,一次修改代码部署就要十来分钟也不方便 debug,所以我就花了不少时间(这个是值得的)把应用在本地启动并复现。第二步完成后就是控制变量进行实验,把导致问题的点圈到最小,这次就是先定位到是配置文件里一个 Bean 的定义,然后再定位到是 Bean 的名字会导致问题,最后才怀疑到了 AOP 上。大胆猜想,小心求证。