spring启动一直Pre-instantiating singletons原因及解决方案

朴灿烈づ我的快乐病毒、 2023-06-30 01:41 48阅读 0赞

今天帮同事排查一个问题,他开发完并未经过本地测试,便提交代码,发布到预生产测试环境,启动 server 过程中一直卡在 Pre-instantiating singletons 无法成功启动项目,我在本地调试后发现并成功定位此问题。

项目环境:

spring 3.x
mybatis 3.x
logback

问题:

应用服务器(tomcat、jetty、jboss等)启动项目一直卡在 Pre-instantiating singletons,详细日志如下:

Pre-instantiating singletons in
org.springframework.beans.factory.support.DefaultListableBeanFactory@36bc55de:
defining beans [XXX;XXY]; root of factory hierarchy

从上面日志信息可以看到,应用启动一直卡在预加载单例对象的过程中。所以从跟踪 DefaultListableBeanFactory 中的方法定位问题,首先了解spring的都知道该类是IOC容器类,其中的 beanDefinitionMap 用于存放对象的抽象映射,应用启动必然会通过此类来加载应用中的单例对象(如spring.xml扫描包路径的类、注解标注的类等)。
在这里插入图片描述
从DefaultListableBeanFactory中发现 preInstantiateSingletons 方法和上面日志的信息很像,在此方法上打上断点进行单步调试跟踪:

  1. public void preInstantiateSingletons() throws BeansException {
  2. if (this.logger.isInfoEnabled()) {
  3. this.logger.info("Pre-instantiating singletons in " + this);
  4. }
  5. //以下省略
  6. ......
  7. }

从方法中打印的日志可以看出,预加载线程在打印完 Pre-instantiating singletons in XXX 便进入阻塞状态,继续往下debug,在创建单例对象的时候会进入 AbstractBeanFactory 中的 getTypeForFactoryBean 方法:

  1. protected Class<?> getTypeForFactoryBean(String beanName, RootBeanDefinition mbd) {
  2. if (!mbd.isSingleton()) {
  3. return null;
  4. }
  5. try {
  6. FactoryBean<?> factoryBean = doGetBean(FACTORY_BEAN_PREFIX + beanName, FactoryBean.class, null, true);
  7. return getTypeForFactoryBean(factoryBean);
  8. }
  9. catch (BeanCreationException ex) {
  10. // Can only happen when getting a FactoryBean.
  11. if (logger.isDebugEnabled()) {
  12. logger.debug("Ignoring bean creation exception on FactoryBean type check: " + ex);
  13. }
  14. onSuppressedException(ex);
  15. return null;
  16. }
  17. }

beanName:bean的别名。
mbd:bean的抽象定义对象,如 beanDefinitionMap 的中的 value。

由于在 catch 块中只有 debug 级别才打印 “Ignoring bean creation exception on FactoryBean type check: “ 日志,但由于项目中使用的logback,spring源码中logger对象是log4j,并且默认日志级别是 info,所以此处日志未打印。
在 onSuppressedException 这里打入断点,可以看到在 doGetBean 时会抛出异常会进入此方法:

  1. protected void onSuppressedException(Exception ex) {
  2. synchronized (this.singletonObjects) {
  3. if (this.suppressedExceptions != null) {
  4. this.suppressedExceptions.add(ex);
  5. }
  6. }
  7. }

suppressedExceptions:一个 Set 集合,从名称上也可以看出用于存放被抑制的异常,也就是说此异常被放入到了列表中并未抛出,这也是为何我们无法从控制台看到的原因。
分析:spring 这样定义的原因主要是因为,如果创建一个单例bean发生异常,那么临时循环引用解析会导致异常无限增多,所以预先用一个列表来注册异常对象。

解决方案:

在debug模式下,通过观察 onSuppressedException 方法中异常变量 ex 的内容,我们就可以看到具体的异常内容了,如下所示:
异常栈

总结:

经过此分析,可以看到,大部分情况都是因为 mybatis 的 xml 文件不符合 mybatis 语法造成的,如整数类型使用 int 而未使用INTEGER。

发表评论

表情:
评论列表 (有 0 条评论,48人围观)

还没有评论,来说两句吧...

相关阅读