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

本文分析了一位同事在预生产环境中遇到的应用服务器启动卡在预加载单例对象的问题,详细介绍了如何通过debug定位到mybatis的xml文件语法错误,并给出了具体的解决方案。

今天帮同事排查一个问题,他开发完并未经过本地测试,便提交代码,发布到预生产测试环境,启动 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。

2025-09-16 15:00:23,186 [main] INFO [com.opensymphony.xwork2.spring.SpringObjectFactory] - Setting autowire strategy to name 2025-09-16 15:00:23,186 [main] INFO [org.apache.struts2.spring.StrutsSpringObjectFactory] - ... initialized Struts-Spring integration successfully 九月 16, 2025 3:00:35 下午 org.apache.cxf.transport.servlet.CXFServlet updateContext 信息: Load the bus with application context 九月 16, 2025 3:00:35 下午 org.apache.cxf.transport.servlet.AbstractCXFServlet replaceDestinationFactory 信息: Servlet transport factory already registered 2025-09-16 15:00:35,381 [main] INFO [org.apache.cxf.bus.spring.BusApplicationContext] - Refreshing org.apache.cxf.bus.spring.BusApplicationContext@75803b11: startup date [Tue Sep 16 15:00:35 CST 2025]; parent: Root WebApplicationContext 2025-09-16 15:00:35,392 [main] INFO [org.springframework.beans.factory.support.DefaultListableBeanFactory] - Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@638fff71: defining beans []; parent: org.springframework.beans.factory.support.DefaultListableBeanFactory@346fb908 九月 16, 2025 3:00:35 下午 org.apache.coyote.http11.Http11Protocol init 严重: Error initializing endpoint java.net.BindException: Address already in use: JVM_Bind <null>:8080 at org.apache.tomcat.util.net.JIoEndpoint.init(JIoEndpoint.java:549) at org.apache.coyote.http11.Http11Protocol.init(Http11Protocol.java:176) at org.apache.catalina.connector.Connector.initialize(Connector.java:1014) at org.apache.catalina.startup.Embedded.start(Embedded.java:830) at org.codehaus.mojo.tomcat.AbstractRunMojo.startContainer(AbstractRunMojo.java:558) at org.codehaus.mojo.tomcat.AbstractRunMojo.execute(AbstractRunMojo.java:255) at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:137) at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:210) at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:156) at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:148) at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:117) at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:81) at org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build(SingleThreadedBuilder.java:56) at org.apache.maven.lifecycle.internal.LifecycleStarter.execute(LifecycleStarter.java:128) at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:305) at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:192) at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:105) at org.apache.maven.cli.MavenCli.execute(MavenCli.java:957) at org.apache.maven.cli.MavenCli.doMain(MavenCli.java:289) at org.apache.maven.cli.MavenCli.main(MavenCli.java:193) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Launcher.java:282) at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:225) at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:406) at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:347) at org.codehaus.classworlds.Launcher.main(Launcher.java:47) Caused by: java.net.BindException: Address already in use: JVM_Bind at java.net.DualStackPlainSocketImpl.bind0(Native Method) at java.net.DualStackPlainSocketImpl.socketBind(DualStackPlainSocketImpl.java:106) at java.net.AbstractPlainSocketImpl.bind(AbstractPlainSocketImpl.java:376) at java.net.PlainSocketImpl.bind(PlainSocketImpl.java:190) at java.net.ServerSocket.bind(ServerSocket.java:376) at java.net.ServerSocket.<init>(ServerSocket.java:237) at java.net.ServerSocket.<init>(ServerSocket.java:181) at org.apache.tomcat.util.net.DefaultServerSocketFactory.createSocket(DefaultServerSocketFactory.java:50) at org.apache.tomcat.util.net.JIoEndpoint.init(JIoEndpoint.java:538) ... 28 more [INFO] ------------------------------------------------------------------------ [INFO] BUILD FAILURE [INFO] ------------------------------------------------------------------------ [INFO] Total time: 02:06 min [INFO] Finished at: 2025-09-16T15:00:35+08:00 [INFO] ------------------------------------------------------------------------ [ERROR] Failed to execute goal org.codehaus.mojo:tomcat-maven-plugin:1.1:run (default-cli) on project PowerDesk: Could not start Tomcat: Protocol handler initialization failed: java.net.BindException: Address already in use: JVM_Bind <null>:8080 -> [Help 1] [ERROR] [ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch. [ERROR] Re-run Maven using the -X switch to enable full debug logging. [ERROR] [ERROR] For more information about the errors and possible solutions, please read the following articles: [ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoExecutionException 已与地址为 ''127.0.0.1:1773',传输: '套接字'' 的目标虚拟机断开连接 进程已结束,退出代码为 1 分析下报错
最新发布
09-17
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值