SpringBoot启动配置原理

本文详细解析了SpringBoot应用程序的启动流程,从SpringApplication对象的创建到容器的刷新过程,涵盖了初始化、环境准备、容器创建、刷新及启动后的扩展等关键步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 SpringApplication.run(SpringbootDay06Application.class, args);
 //先创建SpringApplication再运行run

1、创建SpringApplication对象

initialize(sources);
private void initialize(Object[] sources) {     
//保存主配置类     
	if (sources != null && sources.length > 0) {
	this.sources.addAll(Arrays.asList(sources));              			
	}     
//判断当前是否一个web应用(设置servlet环境)    
this.webEnvironment = deduceWebEnvironment();    
 //从类路径下找到META‐INF/spring.factories配置的所有ApplicationContextInitializer;然后保存起来(首次加载spring.factories文件)
 setInitializers((Collection) getSpringFactoriesInstances(
 ApplicationContextInitializer.class));     
 //从类路径下找到ETA‐INF/spring.factories配置的所ApplicationListener(第二次首次加载spring.factories文件)     
setListeners((Collection)getSpringFactoriesInstances(ApplicationListener.class));     
//从多个配置类中找到有main方法的主配置类     
this.mainApplicationClass = deduceMainApplicationClass(); 
}

deduceWebEnvironment()方法:主要是通过判断REACTIVE相关的字节码是否存在,如果不存在,则web环境即为SERVLET类型。这里设置好web环境类型,在后面会根据类型初始化对应环境。
ApplicationContextInitializer:是spring-context的一个接口,主要是spring ioc容器刷新之前的一个回调接口,用于处于自定义逻辑。
getSpringFactoriesInstances:这个方法是从META‐INF/spring.factories配置中相应的配置。
SpringApplication创建结束后
在这里插入图片描述
在这里插入图片描述
2、运行run方法

public ConfigurableApplicationContext run(String... args) {
		//时间监控
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		configureHeadlessProperty();
//获取SpringApplicationRunListeners;从类路径下META‐INF/spring.factories (getRunListeners有getSpringFactoriesInstances方法)
		//第一步:获取并启动监听器
		SpringApplicationRunListeners listeners = getRunListeners(args);
		//回调所有的获取SpringApplicationRunListener.starting()方法
		listeners.starting();
		try {
		//封装命令行参数
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(
					args);
			//第二步:构造容器环境(准备环境) 创建环境完成后回调SpringApplicationRunListener.environmentPrepared();表示环境准备完成 
			ConfigurableEnvironment environment = prepareEnvironment(listeners,
					applicationArguments);
			//设置需要忽略的bean
			configureIgnoreBeanInfo(environment);
			//打印banner
			Banner printedBanner = printBanner(environment);
			//第三步:创建容器(ioc);决定创建web的ioc还是普通的ioc 
			context = createApplicationContext();
			//第四步:实例化SpringBootExceptionReporter.class,用来支持报告关于启动的错误
			exceptionReporters = getSpringFactoriesInstances(
					SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
		 //applyInitializers():回调之前保存的所有的ApplicationContextInitializer的initialize方法       
		  //回调所有的SpringApplicationRunListener的contextPrepared();
			//第五步:准备容器
			//prepareContext运行完成以后回调所有的SpringApplicationRunListener的contextLoaded(); 
			prepareContext(context, environment, listeners, applicationArguments,printedBanner);
			//第六步:刷新容器
			//ioc容器初始化(如果是web应用还会创建嵌入式的Tomcat);Spring注解版        
			//扫描,创建,加载所有组件的地方;(配置类,组件,自动配置) 
			refreshContext(context);
			//第七步:刷新容器后的扩展接口
			 //从ioc容器中获取所有的ApplicationRunner和CommandLineRunner进行回调        
			 //ApplicationRunner先回调,CommandLineRunner再回调 
			afterRefresh(context, applicationArguments);
			stopWatch.stop();
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass)
						.logStarted(getApplicationLog(), stopWatch);
			}
			listeners.started(context);
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, listeners);
			throw new IllegalStateException(ex);
		}

		try {
			listeners.running(context);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, null);
			throw new IllegalStateException(ex);
		}
		//整个SpringBoot应用启动完成以后返回启动的ioc容器
		return context;
	}

SpringApplicationRunListeners类中有:starting()、 environmentPrepared(ConfigurableEnvironment environment) 、
contextPrepared(ConfigurableApplicationContext context)、contextLoaded(ConfigurableApplicationContext context)、
started(ConfigurableApplicationContext context)、
running(ConfigurableApplicationContext context)、
failed(ConfigurableApplicationContext context, Throwable exception)

<think>我们正在处理Spring Boot 3升级后出现的SLF4J日志绑定冲突问题。错误信息表明: - SLF4J无法加载`org.slf4j.impl.StaticLoggerBinder`类 - 检测到Logback在类路径上,但LoggerFactory却是`org.slf4j.helpers.NOPLoggerFactory`(无操作实现) 根本原因:类路径中存在多个SLF4J绑定(例如同时存在Logback和Log4j2的绑定),或者存在桥接器冲突。 ### 解决方案步骤 #### 1. **检查并排除冲突的依赖** Spring Boot 3默认使用Logback作为日志实现。但如果你引入了其他日志实现的绑定(如Log4j2),或者存在桥接器(例如`log4j-over-slf4j`)与绑定冲突,就会导致此问题。 使用Maven依赖树分析: ```bash mvn dependency:tree -Dincludes=org.slf4j,ch.qos.logback,org.apache.logging.log4j ``` 重点关注: - 是否同时存在`logback-classic`(Logback绑定)和`slf4j-log4j12`(Log4j1绑定) - 是否同时存在`log4j-slf4j2-impl`(Log4j2绑定)和`logback-classic` - 是否引入了`log4j-over-slf4j`(将Log4j调用重定向到SLF4J)但同时又引入了Log4j绑定(导致循环) #### 2. **排除多余的绑定** 假设你希望使用Logback(Spring Boot默认),则排除其他绑定: **示例:排除Log4j2(如果不需要)** ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <!-- 如果引入了Log4j2的starter,排除它 --> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-log4j2</artifactId> </exclusion> </exclusions> </dependency> ``` **排除其他模块传递的冲突绑定** ```xml <dependency> <groupId>com.example</groupId> <artifactId>some-library</artifactId> <exclusions> <!-- 排除该库传递的SLF4J绑定(如slf4j-log4j12) --> <exclusion> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> </exclusion> <!-- 或者排除Log4j2的绑定 --> <exclusion> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-slf4j2-impl</artifactId> </exclusion> </exclusions> </dependency> ``` #### 3. **确保正确的Logback依赖** Spring Boot 3默认包含`spring-boot-starter-logging`(即Logback),所以通常不需要显式添加。但如果排除了其他绑定后问题依旧,可以显式引入Logback: ```xml <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.4.5</version> <!-- 使用Spring Boot管理的版本,无需指定 --> </dependency> ``` #### 4. **处理桥接器冲突** 如果使用了`log4j-over-slf4j`(将Log4j1的API调用重定向到SLF4J),则必须确保: - 移除`log4j`依赖 - 移除`slf4j-log4j12`(Log4j1的SLF4J绑定) 同理,对于`jul-to-slf4j`或`jcl-over-slf4j`,确保没有其他绑定冲突。 #### 5. **检查重复的StaticLoggerBinder类** 在IDE中(如IntelliJ IDEA)搜索类`StaticLoggerBinder`,确保只存在一个实现(Logback的实现位于`ch.qos.logback.classic.spi`包中)。如果发现多个(如来自`slf4j-log4j12`的),则排除该依赖。 #### 6. **清理和重新构建** 执行Maven清理并重新构建: ```bash mvn clean install ``` ### 常见错误配置示例及修正 **错误配置1**:同时存在Logback和Log4j2绑定 ```diff - org.springframework.boot:spring-boot-starter-logging (包含Logback) + org.springframework.boot:spring-boot-starter-log4j2 (包含Log4j2绑定) ``` 修正:只能选择一个日志实现。若要使用Log4j2,则排除`spring-boot-starter-logging`并引入`spring-boot-starter-log4j2`。 **错误配置2**:存在`slf4j-log4j12`(Log4j1绑定)和Logback ```xml <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.36</version> </dependency> <!-- 同时存在Logback --> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.4.5</version> </dependency> ``` 修正:移除`slf4j-log4j12`。 ### Spring Boot 3的日志依赖管理 Spring Boot 3通过`spring-boot-starter-logging`提供Logback支持[^1]。如果你没有显式排除它,它会被默认引入。升级后出现此问题,通常是因为新版本依赖传递发生了变化,或者项目显式引入了冲突的依赖。 ### 验证解决方案 启动应用,观察日志开头是否有Logback的初始化信息: ``` 00:00:00.000 [main] INFO org.springframework.boot.StartupInfoLogger - Starting Application ... ``` 且不再出现SLF4J的绑定警告。 ### 相关问题 1. 如何在Spring Boot 3中从Logback切换到Log4j2? 2. Spring Boot 3的日志自动配置机制是怎样的? 3. 如何排除Maven依赖树中的冲突? 4. SLF4J桥接器(如log4j-over-slf4j)的工作原理是什么? 5. 日志框架冲突会导致哪些运行时异常? [^1]: Spring Boot的日志依赖管理通过`spring-boot-starter-logging`实现,默认绑定Logback。</think>### 解决 Spring Boot 3 升级后的 SLF4J 日志绑定冲突 #### 问题根源分析 当升级到 Spring Boot 3 后出现 `LoggerFactory is not a Logback LoggerContext but Logback is on the classpath` 错误时,根本原因是 **类路径中存在多个冲突的日志绑定实现**。具体表现为: 1. Spring Boot 3 默认使用 Logback 作为日志实现 2. 项目中同时存在 Logback 和另一个 SLF4J 绑定(如 `slf4j-log4j12` 或 `slf4j-nop`) 3. SLF4J 检测到多个绑定实现,无法确定使用哪个,最终回退到 NOP(无操作)实现 #### 解决方案(分步操作) ##### 1. **排除冲突的日志绑定(关键步骤)** 在 `pom.xml` 中排除冲突的依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <!-- 排除默认 Logback(如果不需要) --> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> </exclusion> </exclusions> </dependency> <!-- 显式添加你需要的日志实现 --> <!-- 选项1:使用 Logback(Spring Boot 默认) --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> </dependency> <!-- 选项2:切换到 Log4j2 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-log4j2</artifactId> </dependency> ``` ##### 2. **检查并清理冲突的绑定器** 在依赖树中搜索冲突的绑定器: ```bash mvn dependency:tree -Dincludes=org.slf4j:slf4j-log4j12,org.slf4j:slf4j-nop ``` 若发现以下任何绑定器,需排除: ```xml <!-- 排除冲突绑定器示例 --> <exclusions> <exclusion> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <!-- Log4j1.x 绑定器 --> </exclusion> <exclusion> <groupId>org.slf4j</groupId> <artifactId>slf4j-nop</artifactId> <!-- NOP 绑定器 --> </exclusion> <exclusion> <groupId>org.slf4j</groupId> <artifactId>slf4j-simple</artifactId> <!-- Simple 绑定器 --> </exclusion> </exclusions> ``` ##### 3. **验证绑定器唯一性** 正确的绑定关系应满足: $$ \begin{cases} \text{SLF4J API} + \text{单一绑定器} \\ \downarrow \\ \text{日志实现 (Logback/Log4j2)} \end{cases} $$ 使用以下命令验证: ```bash mvn dependency:tree | grep -E 'slf4j-api|logback|log4j' ``` 输出应类似: ``` [INFO] +- org.slf4j:slf4j-api:jar:2.0.7:compile [INFO] \- ch.qos.logback:logback-classic:jar:1.4.7:compile ``` ##### 4. **处理桥接器冲突(特殊场景)** 若使用日志桥接器(如 `log4j-over-slf4j`),确保: ```xml <!-- 正确配置示例 --> <dependency> <groupId>org.slf4j</groupId> <artifactId>jcl-over-slf4j</artifactId> <!-- Commons Logging → SLF4J --> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>log4j-over-slf4j</artifactId> <!-- Log4j → SLF4J --> </dependency> <!-- 必须排除原始实现 --> <exclusions> <exclusion> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> </exclusion> <exclusion> <groupId>log4j</groupId> <artifactId>log4j</artifactId> </exclusion> </exclusions> ``` ##### 5. **Spring Boot 3 特别注意事项** 升级到 Spring Boot 3 后: 1. SLF4J 版本升级到 2.x(原 1.7.x) 2. Logback 最低要求 1.4.x(原 1.2.x) 3. 检查自定义 `logback.xml` 兼容性: ```xml <!-- 将 legacy 配置更新为 --> <configuration scan="true" scanPeriod="30 seconds"> <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern> </encoder> </appender> ``` #### 验证成功的标志 应用启动时应显示正确的绑定信息: ``` SLF4J: Actual binding is of type [ch.qos.logback.classic.LoggerContext] ``` #### 常见错误配置对比 | **错误配置** | **正确配置** | |-------------|-------------| | SLF4J API + `slf4j-log4j12` + Logback | SLF4J API + `logback-classic` | | SLF4J API + `log4j-over-slf4j` + `slf4j-log4j12` | SLF4J API + `log4j-over-slf4j` + `logback-classic` | | 多个 `StaticLoggerBinder` 实现 | 单一 `ch.qos.logback.classic.spi.StaticLoggerBinder` | > 提示:Spring Boot 3 的日志自动配置优先级为:`logback-spring.xml` > `logback.xml` > 默认配置[^1] #### 相关问题 1. 如何在 Spring Boot 3 中从 Logback 切换到 Log4j2? 2. Spring Boot 3 的日志依赖管理机制有何变化? 3. 如何解决 `log4j-over-slf4j` 和 `slf4j-log4j12` 的循环调用问题? 4. Spring Boot 3 中如何自定义日志格式和输出? 5. 升级到 Spring Boot 3 后日志性能下降如何优化? [^1]: Spring Boot 3 使用 `LoggingApplicationListener` 自动初始化日志系统,优先级由 `spring.logging.config` 属性控制。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值