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

今天帮同事排查一个问题,他开发完并未经过本地测试,便提交代码,发布到预生产测试环境,启动 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 方法和上面日志的信息很像,在此方法上打上断点进行单步调试跟踪:

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

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

protected Class<?> getTypeForFactoryBean(String beanName, RootBeanDefinition mbd) {
	if (!mbd.isSingleton()) {
		return null;
	}
	try {
		FactoryBean<?> factoryBean = doGetBean(FACTORY_BEAN_PREFIX + beanName, FactoryBean.class, null, true);
		return getTypeForFactoryBean(factoryBean);
	}
	catch (BeanCreationException ex) {
		// Can only happen when getting a FactoryBean.
		if (logger.isDebugEnabled()) {
			logger.debug("Ignoring bean creation exception on FactoryBean type check: " + ex);
		}
		onSuppressedException(ex);
		return null;
	}
}

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

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

protected void onSuppressedException(Exception ex) {
	synchronized (this.singletonObjects) {
		if (this.suppressedExceptions != null) {
			this.suppressedExceptions.add(ex);
		}
	}
}

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

解决方案:

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

总结:

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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值