Spring Boot的启动原理与优化技巧
Spring Boot 的启动原理
Spring Boot 以其快速开发、自动配置等特性,成为了Java 开发者构建应用的热门选择。深入了解其启动原理,有助于开发者更好地优化和定制应用。
SpringApplication 类
Spring Boot 的启动入口是 SpringApplication
类。当我们在 main
方法中创建 SpringApplication
实例并调用 run
方法时,一系列复杂的启动流程就开始了。
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
在 SpringApplication
的构造函数中,它会对传入的主配置类进行分析。它会检测该类是否是 Web 应用(基于 WebApplicationType
枚举),以及初始化各种初始化器和监听器。
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
启动流程
-
初始化阶段
- 推断应用类型:
WebApplicationType
用于判断应用是SERVLET
、REACTIVE
还是NONE
类型。它通过检查类路径中是否存在特定的类(如javax.servlet.Servlet
、org.springframework.web.reactive.DispatcherHandler
)来做出判断。 - 加载初始化器和监听器:
SpringApplication
从META-INF/spring.factories
文件中加载ApplicationContextInitializer
和ApplicationListener
。这些初始化器和监听器可以在应用上下文创建前后执行特定的逻辑。例如,ConfigFileApplicationListener
就是一个重要的初始化器,它负责加载application.properties
或application.yml
等配置文件。
- 推断应用类型:
-
创建应用上下文阶段
- 创建上下文实例:根据推断出的应用类型,
SpringApplication
创建相应类型的ApplicationContext
。对于SERVLET
应用,通常是AnnotationConfigServletWebServerApplicationContext
;对于REACTIVE
应用,是AnnotationConfigReactiveWebServerApplicationContext
。 - 应用初始化器:在上下文实例创建后,
SpringApplication
会调用所有已加载的ApplicationContextInitializer
的initialize
方法。这些初始化器可以对上下文进行进一步的配置,比如添加属性源等。
- 创建上下文实例:根据推断出的应用类型,
-
刷新应用上下文阶段
- 加载 bean 定义:上下文刷新过程中,Spring 会扫描主配置类及其相关的配置类,加载所有的 bean 定义。这包括通过
@ComponentScan
扫描到的组件,以及使用@Bean
注解定义的 bean。 - 实例化和初始化 bean:Spring 根据 bean 定义,实例化并初始化所有的 bean。在这个过程中,会处理 bean 的依赖注入、生命周期回调等。例如,如果一个 bean 依赖另一个 bean,Spring 会确保先实例化和初始化被依赖的 bean,然后再注入到依赖它的 bean 中。
- 加载 bean 定义:上下文刷新过程中,Spring 会扫描主配置类及其相关的配置类,加载所有的 bean 定义。这包括通过
-
启动阶段
- 启动 web 服务器:对于
SERVLET
或REACTIVE
类型的应用,上下文刷新完成后,会启动相应的 web 服务器(如 Tomcat、Jetty 或 Undertow)。服务器启动后,应用就可以接收外部请求了。 - 发布事件:整个启动过程中,
SpringApplication
会发布各种ApplicationEvent
,如ApplicationStartingEvent
、ApplicationReadyEvent
等。监听器可以监听这些事件,执行特定的逻辑,比如在应用启动完成后打印启动日志等。
- 启动 web 服务器:对于
Spring Boot 的优化技巧
了解了 Spring Boot 的启动原理后,我们可以从多个方面对应用进行优化,以提高性能和启动速度。
减少不必要的依赖
- 分析依赖树
在
pom.xml
(Maven 项目)或build.gradle
(Gradle 项目)中,我们可以使用工具来分析项目的依赖树。对于 Maven,可以使用mvn dependency:tree
命令。这个命令会打印出项目所有依赖的树形结构,我们可以从中找出不必要的依赖。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
假设项目中引入了 spring-boot-starter-web
,它会自动引入一系列依赖,包括 Tomcat 作为默认的 web 服务器。如果项目不需要 web 功能,就可以移除这个依赖。
- 排除传递依赖
有时候,一个依赖会传递引入一些我们不需要的依赖。我们可以通过在依赖中使用
exclusions
标签(Maven)或exclude
方法(Gradle)来排除这些传递依赖。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
<exclusions>
<exclusion>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</exclusion>
</exclusions>
</dependency>
在上述例子中,spring-boot-starter-jdbc
可能会传递引入 h2
数据库依赖。如果项目不使用 h2
,就可以通过这种方式排除。
优化配置文件加载
- 合理组织配置文件
Spring Boot 支持多种配置文件格式,如
properties
和yml
。在大型项目中,将配置文件按功能模块进行拆分,可以提高配置的可读性和维护性。例如,可以创建application - common.yml
存放通用配置,application - dev.yml
存放开发环境配置,application - prod.yml
存放生产环境配置。
# application - common.yml
server:
port: 8080
# application - dev.yml
spring:
datasource:
url: jdbc:mysql://localhost:3306/dev_db
username: dev_user
password: dev_password
# application - prod.yml
spring:
datasource:
url: jdbc:mysql://prod - db - server:3306/prod_db
username: prod_user
password: prod_password
- 减少配置文件加载时间
Spring Boot 启动时会加载多个配置文件,包括默认的
application.properties
或application.yml
。可以通过设置spring.config.location
属性来指定配置文件的位置,避免不必要的文件搜索。
public class Application {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(Application.class);
app.setDefaultProperties(Collections.singletonMap("spring.config.location", "classpath:/config/,file:./config/"));
app.run(args);
}
}
这样配置后,Spring Boot 会优先从指定的位置加载配置文件,减少了在整个类路径下搜索配置文件的时间。
优化 bean 的加载和初始化
- 懒加载 bean
对于一些不急于使用的 bean,可以使用懒加载。在 Spring Boot 中,只需在
@Component
或@Bean
注解的类或方法上添加@Lazy
注解即可。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
@Component
@Lazy
public class LazyLoadedBean {
public LazyLoadedBean() {
System.out.println("LazyLoadedBean is initialized");
}
}
当 LazyLoadedBean
被标记为懒加载后,它不会在应用启动时立即初始化,而是在第一次被使用时才初始化,从而加快了应用的启动速度。
- 优化 bean 的依赖关系 尽量减少 bean 之间的复杂依赖关系。复杂的依赖关系可能导致 bean 的初始化顺序难以控制,增加启动时间。例如,如果一个 bean A 依赖于 bean B、C 和 D,而 bean B 又依赖于 bean E 和 F,这种多层依赖关系可能会使启动过程变得复杂。可以通过重构代码,将一些依赖关系进行简化,比如将部分功能封装到一个新的 bean 中,减少依赖层次。
优化 web 服务器配置
- 选择合适的 web 服务器 Spring Boot 默认使用 Tomcat 作为 web 服务器。对于一些轻量级应用,可以考虑使用 Jetty 或 Undertow。Jetty 以其轻量级和高性能著称,而 Undertow 则在处理高并发场景下表现出色。
要切换到 Jetty,可以在 pom.xml
中添加以下依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
- 调整 web 服务器参数
对于 Tomcat,可以通过在
application.properties
或application.yml
中配置参数来优化性能。例如,可以调整最大线程数、连接超时时间等。
server:
tomcat:
max - threads: 200
min - spare - threads: 10
connection - timeout: 5000
通过合理调整这些参数,可以提高 web 服务器在高并发场景下的处理能力,同时也可以在一定程度上优化启动时间。
使用 AOT 编译
-
什么是 AOT 编译 AOT(Ahead - Of - Time)编译是 Spring Boot 2.6 引入的新特性。传统的 Spring 应用在启动时会进行大量的反射操作来加载和初始化 bean。AOT 编译则在构建阶段将这些反射操作提前处理,生成字节码,从而加快应用的启动速度。
-
启用 AOT 编译 要启用 AOT 编译,首先需要在项目中添加
spring - boot - aot - gradle - plugin
或spring - boot - aot - maven - plugin
。
对于 Gradle 项目,在 build.gradle
中添加:
plugins {
id 'org.springframework.boot' version '2.6.0'
id 'io.spring.aot' version '0.11.0'
}
然后运行 ./gradlew bootAotBuild
命令,Gradle 会在构建过程中生成 AOT 相关的代码和资源。在应用启动时,这些预编译的代码会被使用,大大加快启动速度。
优化日志配置
- 减少日志输出级别
在开发环境中,通常会将日志级别设置为
DEBUG
或TRACE
以方便调试。但在生产环境中,过高的日志级别会增加系统开销,影响性能。可以将日志级别调整为INFO
或WARN
。
在 application.yml
中配置:
logging:
level:
root: INFO
org.springframework: INFO
- 选择合适的日志框架 Spring Boot 默认支持多种日志框架,如 Logback、Log4j2。Log4j2 在性能方面表现较好,特别是在高并发场景下。如果项目对性能要求较高,可以考虑切换到 Log4j2。
在 pom.xml
中排除默认的 Logback 依赖,添加 Log4j2 依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter - logging</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
通过以上这些优化技巧,我们可以在深入理解 Spring Boot 启动原理的基础上,对应用进行全面的优化,提高应用的性能和启动速度,使其更适合不同的生产环境和业务需求。