Spring Boot 启动过程全解析:从main方法到Tomcat启动的魔法之旅
这里写目录标题
- [Spring Boot 启动过程全解析:从main方法到Tomcat启动的魔法之旅](#Spring Boot 启动过程全解析:从main方法到Tomcat启动的魔法之旅)
-
- [前言:Spring Boot的革命性设计](#前言:Spring Boot的革命性设计)
- [第一章:入口之谜 - 从main方法开始](#第一章:入口之谜 - 从main方法开始)
-
- [1.1 经典的启动入口](#1.1 经典的启动入口)
- [1.2 SpringApplication.run()方法解析](#1.2 SpringApplication.run()方法解析)
- [第二章:SpringApplication初始化 - 准备启动环境](#第二章:SpringApplication初始化 - 准备启动环境)
-
- [2.1 构造方法深入分析](#2.1 构造方法深入分析)
- [2.2 应用类型推断机制](#2.2 应用类型推断机制)
- [2.3 工厂加载机制](#2.3 工厂加载机制)
- [第三章:run()方法执行 - 启动流程的核心](#第三章:run()方法执行 - 启动流程的核心)
-
- [3.1 run方法完整流程](#3.1 run方法完整流程)
- [3.2 事件监听器机制](#3.2 事件监听器机制)
- [第四章:环境准备 - 配置的加载与解析](#第四章:环境准备 - 配置的加载与解析)
-
- [4.1 Environment的创建与配置](#4.1 Environment的创建与配置)
- [4.2 配置源加载顺序](#4.2 配置源加载顺序)
- [4.3 配置绑定机制](#4.3 配置绑定机制)
- 第五章:ApplicationContext的创建与刷新
-
- [5.1 应用上下文创建](#5.1 应用上下文创建)
- [5.2 prepareContext方法详解](#5.2 prepareContext方法详解)
- [5.3 refreshContext - Spring容器的核心](#5.3 refreshContext - Spring容器的核心)
- [第六章:自动配置机制 - Spring Boot的魔法核心](#第六章:自动配置机制 - Spring Boot的魔法核心)
-
- [6.1 @EnableAutoConfiguration原理](#6.1 @EnableAutoConfiguration原理)
- [6.2 条件注解机制](#6.2 条件注解机制)
- [6.3 自动配置执行流程](#6.3 自动配置执行流程)
- [第七章:内嵌Web容器启动 - Tomcat的集成](#第七章:内嵌Web容器启动 - Tomcat的集成)
-
- [7.1 Web服务器自动配置](#7.1 Web服务器自动配置)
- [7.2 Tomcat启动过程](#7.2 Tomcat启动过程)
- [7.3 Tomcat配置定制化](#7.3 Tomcat配置定制化)
- 第八章:DispatcherServlet的注册与初始化
-
- [8.1 DispatcherServlet自动配置](#8.1 DispatcherServlet自动配置)
- [8.2 DispatcherServlet初始化过程](#8.2 DispatcherServlet初始化过程)
- [8.3 Spring MVC完整请求处理流程](#8.3 Spring MVC完整请求处理流程)
- 第九章:启动完成与Runner执行
-
- [9.1 启动后回调机制](#9.1 启动后回调机制)
- [9.2 callRunners方法实现](#9.2 callRunners方法实现)
- 第十章:生产环境优化与调试技巧
-
- [10.1 启动性能优化](#10.1 启动性能优化)
- [10.2 启动过程调试](#10.2 启动过程调试)
- 第十一章:常见启动问题与解决方案
-
- [11.1 启动失败常见原因](#11.1 启动失败常见原因)
- [11.2 启动性能瓶颈识别](#11.2 启动性能瓶颈识别)
- [第十二章:Spring Boot启动机制的未来演进](#第十二章:Spring Boot启动机制的未来演进)
-
- [12.1 Spring Boot 2.4+的新特性](#12.1 Spring Boot 2.4+的新特性)
- [12.2 Spring Boot 3.0展望](#12.2 Spring Boot 3.0展望)
- [总结:Spring Boot启动的艺术](#总结:Spring Boot启动的艺术)
前言:Spring Boot的革命性设计
Spring Boot自2014年发布以来,彻底改变了Java企业级应用的开发方式。它通过"约定优于配置"的理念,将开发者从繁琐的XML配置中解放出来。本文将深入剖析Spring Boot的启动过程,揭示从简单的main方法到完整的Tomcat服务器启动背后的魔法机制。
启动过程整体流程图:
[main方法入口]
↓
[SpringApplication实例化]
↓
[run()方法执行]
↓
[环境准备与配置加载]
↓
[ApplicationContext创建与刷新]
↓
[自动装配机制执行]
↓
[内嵌Web容器初始化]
↓
[DispatcherServlet注册]
↓
[应用启动完成]
第一章:入口之谜 - 从main方法开始
1.1 经典的启动入口
每个Spring Boot应用都有一个看似简单的启动类:
java
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
这个简单的main方法背后隐藏着复杂的启动逻辑。让我们通过源码一步步解密。
1.2 SpringApplication.run()方法解析
查看SpringApplication的源码,run方法是静态的入口点:
java
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[]{primarySource}, args);
}
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
这里发生了两个重要步骤:
-
创建SpringApplication实例
-
调用实例的run方法
类图示意:┌─────────────────────────────────────┐
│ SpringApplication │
├─────────────────────────────────────┤
│ - primarySources: Set<Class<?>> │ │ - webApplicationType: WebApplication│ │ - bannerMode: Banner.Mode │ │ - logStartupInfo: boolean │ │ - headless: boolean │ │ - listeners: SpringApplicationRun...│ │ - initializers: ApplicationContext..│ ├─────────────────────────────────────┤ │ + run(String... args) │ │ + SpringApplication(Class<?>...) │
│ + setWebApplicationType() │
│ + deduceWebApplicationType() │
│ + setInitializers() │
│ + setListeners() │
└─────────────────────────────────────┘
第二章:SpringApplication初始化 - 准备启动环境
2.1 构造方法深入分析
java
public SpringApplication(Class<?>... primarySources) {
this(null, primarySources);
}
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 1. 推断应用类型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 2. 设置ApplicationContextInitializer
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
// 3. 设置ApplicationListener
setListeners((Collection) getSpringFactoriesInstances(
ApplicationListener.class));
// 4. 推断主应用类
this.mainApplicationClass = deduceMainApplicationClass();
}
2.2 应用类型推断机制
Spring Boot支持三种应用类型:
- Web应用:Servlet环境(Spring MVC)
- Reactive Web应用:响应式环境(WebFlux)
- 普通应用:非Web环境
推断逻辑在WebApplicationType.deduceFromClasspath()中实现:
java
static WebApplicationType deduceFromClasspath() {
// 类路径检测策略
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
return WebApplicationType.SERVLET;
}
类型推断决策图:
开始推断
↓
检测类路径中是否存在DispatcherHandler(WebFlux)?
├── 存在 → 检测是否存在DispatcherServlet?
│ ├── 存在 → SERVLET类型
│ └── 不存在 → REACTIVE类型
└── 不存在 → 检测Servlet API?
├── 存在 → SERVLET类型
└── 不存在 → NONE类型
2.3 工厂加载机制
getSpringFactoriesInstances()方法是Spring Boot扩展机制的核心:
java
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
return getSpringFactoriesInstances(type, new Class<?>[] {});
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
// 关键:从META-INF/spring.factories加载实现类
Set<String> names = new LinkedHashSet<>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 实例化所有找到的类
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
// 排序
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
spring.factories文件示例:
properties
# ApplicationContextInitializer
org.springframework.context.ApplicationContextInitializer=\
com.example.MyInitializer,\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer
# ApplicationListener
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener
第三章:run()方法执行 - 启动流程的核心
3.1 run方法完整流程
java
public ConfigurableApplicationContext run(String... args) {
// 1. 创建启动计时器
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// 2. 准备引导上下文(Spring Boot 2.4+)
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
// 3. 获取运行监听器并启动
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
// 4. 准备应用参数
ApplicationArguments applicationArguments =
new DefaultApplicationArguments(args);
// 5. 准备环境
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
configureIgnoreBeanInfo(environment);
// 6. 打印Banner
Banner printedBanner = printBanner(environment);
// 7. 创建应用上下文
context = createApplicationContext();
// 8. 准备异常报告器
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// 9. 准备上下文
prepareContext(context, environment, listeners,
applicationArguments, printedBanner);
// 10. 刷新上下文(核心!)
refreshContext(context);
// 11. 刷新后的后置处理
afterRefresh(context, applicationArguments);
// 12. 停止计时器
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
// 13. 发布应用已启动事件
listeners.started(context);
// 14. 调用Runner
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
// 15. 发布应用就绪事件
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
3.2 事件监听器机制
Spring Boot启动过程中通过事件监听器实现扩展点:
事件发布序列图:
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│SpringApplication│ │ EventPublisher│ │ Listener │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │ │
│ 1. starting() │ │
├─────────────────────►│ │
│ │ 2. 发布StartingEvent │
│ ├─────────────────────►│
│ │ │
│ 3. environmentPrepared()│ │
├─────────────────────►│ │
│ │ 4. 发布Environment...│
│ ├─────────────────────►│
│ │ │
│ 5. contextPrepared() │ │
├─────────────────────►│ │
│ │ 6. 发布ContextPrep...│
│ ├─────────────────────►│
│ │ │
│ 7. contextLoaded() │ │
├─────────────────────►│ │
│ │ 8. 发布ContextLoad...│
│ ├─────────────────────►│
│ │ │
│ 9. started() │ │
├─────────────────────►│ │
│ │ 10. 发布StartedEvent │
│ ├─────────────────────►│
│ │ │
│ 11. running() │ │
├─────────────────────►│ │
│ │ 12. 发布ReadyEvent │
│ └─────────────────────►│
│ │ │
自定义监听器示例:
java
@Component
public class MyApplicationListener
implements ApplicationListener<ApplicationStartedEvent> {
@Override
public void onApplicationEvent(ApplicationStartedEvent event) {
System.out.println("应用启动完成,开始执行初始化任务...");
// 获取应用上下文
ApplicationContext context = event.getApplicationContext();
// 执行自定义初始化逻辑
initializeDatabase(context);
warmUpCache(context);
registerHealthChecks(context);
}
private void initializeDatabase(ApplicationContext context) {
DataSource dataSource = context.getBean(DataSource.class);
// 数据库初始化逻辑
}
}
第四章:环境准备 - 配置的加载与解析
4.1 Environment的创建与配置
java
private ConfigurableEnvironment prepareEnvironment(
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// 创建合适类型的Environment
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 配置Environment
configureEnvironment(environment, applicationArguments.getSourceArgs());
// 触发环境准备事件
ConfigurationPropertySources.attach(environment);
listeners.environmentPrepared(environment);
// 绑定环境到SpringApplication
bindToSpringApplication(environment);
// 如果不是自定义环境,进行转换
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader())
.convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
// 配置PropertySources
ConfigurationPropertySources.attach(environment);
return environment;
}
4.2 配置源加载顺序
Spring Boot按照以下顺序加载配置:
java
// PropertySource加载顺序
1. 默认属性(通过SpringApplication.setDefaultProperties设置)
2. @PropertySource注解指定的配置文件
3. 配置数据(如application.properties/yml)
4. RandomValuePropertySource(随机值属性源)
5. 操作系统环境变量
6. Java系统属性
7. JNDI属性(来自java:comp/env)
8. ServletContext初始化参数
9. ServletConfig初始化参数
10. 来自SpringApplication的JSON属性
11. 命令行参数
配置加载流程图:
开始加载配置
↓
加载SpringApplication.setDefaultProperties()
↓
加载@PropertySource注解指定的文件
↓
加载application-{profile}.properties/yml
↓
加载application.properties/yml
↓
加载RandomValuePropertySource
↓
加载操作系统环境变量
↓
加载Java系统属性
↓
加载JNDI属性
↓
加载Servlet上下文参数
↓
加载命令行参数
↓
配置合并与优先级处理
4.3 配置绑定机制
Spring Boot 2.0引入了新的配置绑定机制:
java
// 传统方式
@Value("${server.port}")
private int port;
// 类型安全配置绑定方式
@ConfigurationProperties(prefix = "server")
public class ServerProperties {
private int port = 8080;
private String address;
private ErrorProperties error = new ErrorProperties();
// getters and setters
}
// 自动配置类中使用
@Configuration
@EnableConfigurationProperties(ServerProperties.class)
public class ServerAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public Server server(ServerProperties properties) {
Server server = new Server();
server.setPort(properties.getPort());
server.setAddress(properties.getAddress());
return server;
}
}
第五章:ApplicationContext的创建与刷新
5.1 应用上下文创建
java
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
// 根据应用类型选择不同的ApplicationContext实现
switch (this.webApplicationType) {
case SERVLET:
contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
break;
case REACTIVE:
contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
break;
default:
contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
}
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(...);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
不同类型的ApplicationContext:
- Servlet Web应用:AnnotationConfigServletWebServerApplicationContext
- Reactive Web应用:AnnotationConfigReactiveWebServerApplicationContext
- 非Web应用:AnnotationConfigApplicationContext
5.2 prepareContext方法详解
java
private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
// 1. 设置环境
context.setEnvironment(environment);
// 2. 后置处理ApplicationContext
postProcessApplicationContext(context);
// 3. 应用初始化器
applyInitializers(context);
// 4. 触发contextPrepared事件
listeners.contextPrepared(context);
// 5. 打印启动日志
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// 6. 注册单例Bean
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
// 7. 设置允许Bean定义覆盖
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
// 8. 延迟加载
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
// 9. 加载源(主配置类)
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[0]));
// 10. 触发contextLoaded事件
listeners.contextLoaded(context);
}
5.3 refreshContext - Spring容器的核心
refreshContext()方法调用了ApplicationContext的refresh()方法,这是Spring框架的核心:
java
protected void refresh(ApplicationContext applicationContext) {
Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
// 调用AbstractApplicationContext的refresh方法
((AbstractApplicationContext) applicationContext).refresh();
}
// AbstractApplicationContext.refresh()的关键步骤
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 1. 准备刷新上下文
prepareRefresh();
// 2. 获取新的BeanFactory
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 3. 准备BeanFactory
prepareBeanFactory(beanFactory);
try {
// 4. 后置处理BeanFactory
postProcessBeanFactory(beanFactory);
// 5. 调用BeanFactoryPostProcessor
invokeBeanFactoryPostProcessors(beanFactory);
// 6. 注册BeanPostProcessor
registerBeanPostProcessors(beanFactory);
// 7. 初始化MessageSource
initMessageSource();
// 8. 初始化事件广播器
initApplicationEventMulticaster();
// 9. 初始化特殊Bean
onRefresh();
// 10. 注册监听器
registerListeners();
// 11. 完成BeanFactory初始化,实例化单例Bean
finishBeanFactoryInitialization(beanFactory);
// 12. 完成刷新
finishRefresh();
}
catch (BeansException ex) {
// 清理资源
destroyBeans();
cancelRefresh(ex);
throw ex;
}
finally {
// 重置缓存
resetCommonCaches();
}
}
}
refresh()方法详细流程图:
开始refresh()
├── 1. prepareRefresh()
│ ├── 设置启动时间
│ ├── 设置活跃状态
│ └── 初始化属性源
│
├── 2. obtainFreshBeanFactory()
│ └── 刷新BeanFactory
│
├── 3. prepareBeanFactory(beanFactory)
│ ├── 设置类加载器
│ ├── 设置表达式解析器
│ ├── 添加属性编辑器注册器
│ └── 添加ApplicationContextAwareProcessor
│
├── 4. postProcessBeanFactory(beanFactory)
│ └── 空方法,子类可扩展
│
├── 5. invokeBeanFactoryPostProcessors(beanFactory)
│ ├── 调用BeanDefinitionRegistryPostProcessor
│ ├── 调用BeanFactoryPostProcessor
│ └── 这是自动配置的关键!
│
├── 6. registerBeanPostProcessors(beanFactory)
│ └── 注册所有BeanPostProcessor
│
├── 7. initMessageSource()
│ └── 初始化国际化资源
│
├── 8. initApplicationEventMulticaster()
│ └── 初始化事件广播器
│
├── 9. onRefresh()
│ └── 模板方法,初始化特殊Bean
│ └── Web应用中会创建Web服务器!
│
├── 10. registerListeners()
│ └── 注册应用监听器
│
├── 11. finishBeanFactoryInitialization(beanFactory)
│ ├── 初始化ConversionService
│ ├── 嵌入值解析器
│ ├── 加载LoadTimeWeaver
│ └── 实例化所有非懒加载单例Bean
│
└── 12. finishRefresh()
├── 清除资源缓存
├── 初始化生命周期处理器
├── 发布ContextRefreshedEvent
└── 注册JMX
第六章:自动配置机制 - Spring Boot的魔法核心
6.1 @EnableAutoConfiguration原理
@SpringBootApplication是组合注解,包含@EnableAutoConfiguration:
java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class)
})
public @interface SpringBootApplication {
// ...
}
@EnableAutoConfiguration的关键是@Import(AutoConfigurationImportSelector.class):
java
public class AutoConfigurationImportSelector
implements DeferredImportSelector, BeanClassLoaderAware, ... {
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
// 获取自动配置项
AutoConfigurationEntry autoConfigurationEntry =
getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
protected AutoConfigurationEntry getAutoConfigurationEntry(
AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
// 获取注解属性
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 加载候选配置
List<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);
// 去重
configurations = removeDuplicates(configurations);
// 获取排除项
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
// 过滤
configurations = getConfigurationClassFilter().filter(configurations);
// 触发事件
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
AnnotationAttributes attributes) {
// 从spring.factories加载配置类
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
Assert.notEmpty(configurations,
"No auto configuration classes found in META-INF/spring.factories.");
return configurations;
}
}
6.2 条件注解机制
自动配置类的条件判断是Spring Boot智能配置的核心:
java
@Configuration
// 条件:类路径下存在Servlet.class, Tomcat.class, UpgradeProtocol.class
@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
// 条件:是Web应用
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
// 条件:存在ServletWebServerFactory类型的Bean
@ConditionalOnMissingBean(value = ServletWebServerFactory.class,
search = SearchStrategy.CURRENT)
public class TomcatServletWebServerFactoryConfiguration {
@Bean
public TomcatServletWebServerFactory tomcatServletWebServerFactory(
ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers,
ObjectProvider<TomcatContextCustomizer> contextCustomizers,
ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
// 定制化配置
factory.getTomcatConnectorCustomizers()
.addAll(connectorCustomizers.orderedStream().collect(Collectors.toList()));
factory.getTomcatContextCustomizers()
.addAll(contextCustomizers.orderedStream().collect(Collectors.toList()));
factory.getTomcatProtocolHandlerCustomizers()
.addAll(protocolHandlerCustomizers.orderedStream().collect(Collectors.toList()));
return factory;
}
}
常用条件注解:
- @ConditionalOnClass:类路径下存在指定类
- @ConditionalOnMissingClass:类路径下不存在指定类
- @ConditionalOnBean:容器中存在指定Bean
- @ConditionalOnMissingBean:容器中不存在指定Bean
- @ConditionalOnProperty:配置文件中存在指定属性
- @ConditionalOnResource:存在指定资源文件
- @ConditionalOnWebApplication:是Web应用
- @ConditionalOnNotWebApplication:不是Web应用
- @ConditionalOnExpression:SpEL表达式结果为true
6.3 自动配置执行流程
自动配置决策树:
开始自动配置
↓
加载所有spring.factories中的自动配置类
↓
应用排除规则(@EnableAutoConfiguration.exclude)
↓
应用@AutoConfigureOrder排序
↓
应用@AutoConfigureBefore/@AutoConfigureAfter
↓
对每个配置类进行条件判断
↓
├── 类路径条件满足?→ 继续
│ └── 不满足 → 跳过
↓
├── Bean条件满足?→ 继续
│ └── 不满足 → 跳过
↓
├── 属性条件满足?→ 继续
│ └── 不满足 → 跳过
↓
└── 所有条件满足 → 注册配置类
第七章:内嵌Web容器启动 - Tomcat的集成
7.1 Web服务器自动配置
Spring Boot通过ServletWebServerFactoryAutoConfiguration自动配置Web服务器:
java
@Configuration
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(ServerProperties.class)
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {
@Bean
public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(
ServerProperties serverProperties) {
return new ServletWebServerFactoryCustomizer(serverProperties);
}
@Bean
@ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")
public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(
ServerProperties serverProperties) {
return new TomcatServletWebServerFactoryCustomizer(serverProperties);
}
}
7.2 Tomcat启动过程
在onRefresh()方法中启动Web服务器:
java
// ServletWebServerApplicationContext.java
@Override
protected void onRefresh() {
super.onRefresh();
try {
createWebServer();
} catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = getServletContext();
if (webServer == null && servletContext == null) {
// 获取ServletWebServerFactory
ServletWebServerFactory factory = getWebServerFactory();
// 创建WebServer
this.webServer = factory.getWebServer(getSelfInitializer());
}
else if (servletContext != null) {
try {
getSelfInitializer().onStartup(servletContext);
} catch (ServletException ex) {
throw new ApplicationContextException(...);
}
}
initPropertySources();
}
Tomcat启动序列图:
┌─────────────┐ ┌──────────────┐ ┌─────────────┐ ┌─────────────┐
│Application- │ │ServletWeb- │ │Tomcat- │ │ Tomcat │
│ Context │ │ServerFactory │ │ServletWeb- │ │ Instance │
└─────────────┘ └──────────────┘ │ServerFactory│ └─────────────┘
│ │ └─────────────┘ │
│onRefresh() │ │ │
│────────────────►│ │ │
│ │ │ │
│ │getWebServer() │ │
│ │───────────────────►│ │
│ │ │ │
│ │ │new Tomcat() │
│ │ │────────────────►│
│ │ │ │
│ │ │configureTomcat()│
│ │ │────────────────►│
│ │ │ │
│ │ │ Tomcat.start()│
│ │ │────────────────►│
│ │ │ │
│ │ │ initialize() │
│ │ │◄────────────────│
│ │ │ │
│ │ return │ │
│ │◄───────────────────│ │
│ │ │ │
│ WebServer │ │ │
│◄────────────────│ │ │
│ │ │ │
7.3 Tomcat配置定制化
Spring Boot提供了多种方式定制Tomcat:
java
@Configuration
public class TomcatConfiguration {
// 方式1:通过配置属性
// application.yml
// server:
// port: 8081
// tomcat:
// max-threads: 200
// uri-encoding: UTF-8
// 方式2:通过Bean定制
@Bean
public WebServerFactoryCustomizer<TomcatServletWebServerFactory> tomcatCustomizer() {
return factory -> {
factory.setPort(8081);
factory.setContextPath("/api");
factory.addConnectorCustomizers(connector -> {
connector.setProperty("maxThreads", "200");
connector.setProperty("acceptCount", "100");
});
};
}
// 方式3:直接配置Tomcat
@Bean
public TomcatServletWebServerFactory tomcatFactory() {
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
factory.setPort(8081);
factory.setContextPath("/app");
// 添加连接器
Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
connector.setPort(8082);
factory.addAdditionalTomcatConnectors(connector);
// 添加阀门
factory.addContextValves(new AccessLogValve());
return factory;
}
// 方式4:通过ServerProperties
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {
private Integer port;
private String servletPath = "/";
private Tomcat tomcat = new Tomcat();
public static class Tomcat {
private String uriEncoding = "UTF-8";
private int maxThreads = 200;
private int minSpareThreads = 10;
// ... 其他属性
}
}
}
第八章:DispatcherServlet的注册与初始化
8.1 DispatcherServlet自动配置
Spring MVC的核心DispatcherServlet由DispatcherServletAutoConfiguration自动配置:
java
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
public class DispatcherServletAutoConfiguration {
public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet";
@Configuration
@Conditional(DefaultDispatcherServletCondition.class)
@ConditionalOnClass(ServletRegistration.class)
@EnableConfigurationProperties(WebMvcProperties.class)
protected static class DispatcherServletConfiguration {
@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServlet dispatcherServlet(WebMvcProperties webMvcProperties) {
DispatcherServlet dispatcherServlet = new DispatcherServlet();
// 设置DispatcherServlet属性
dispatcherServlet.setDispatchOptionsRequest(
webMvcProperties.isDispatchOptionsRequest());
dispatcherServlet.setDispatchTraceRequest(
webMvcProperties.isDispatchTraceRequest());
dispatcherServlet.setThrowExceptionIfNoHandlerFound(
webMvcProperties.isThrowExceptionIfNoHandlerFound());
dispatcherServlet.setPublishEvents(
webMvcProperties.isPublishRequestHandledEvents());
dispatcherServlet.setEnableLoggingRequestDetails(
webMvcProperties.isLogRequestDetails());
return dispatcherServlet;
}
}
@Configuration
@Conditional(DispatcherServletRegistrationCondition.class)
@ConditionalOnClass(ServletRegistration.class)
@EnableConfigurationProperties(WebMvcProperties.class)
@Import(DispatcherServletConfiguration.class)
protected static class DispatcherServletRegistrationConfiguration {
@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
@ConditionalOnBean(value = DispatcherServlet.class, name =
DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServletRegistrationBean dispatcherServletRegistration(
DispatcherServlet dispatcherServlet,
WebMvcProperties webMvcProperties,
ObjectProvider<MultipartConfigElement> multipartConfig) {
// 创建注册Bean
DispatcherServletRegistrationBean registration =
new DispatcherServletRegistrationBean(
dispatcherServlet,
webMvcProperties.getServlet().getPath());
registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
registration.setLoadOnStartup(
webMvcProperties.getServlet().getLoadOnStartup());
// 设置Multipart配置
multipartConfig.ifAvailable(registration::setMultipartConfig);
return registration;
}
}
}
8.2 DispatcherServlet初始化过程
DispatcherServlet在初始化时会加载Spring MVC的各个组件:
java
// DispatcherServlet.java
@Override
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {
// 1. 初始化文件上传处理器
initMultipartResolver(context);
// 2. 初始化本地化解析器
initLocaleResolver(context);
// 3. 初始化主题解析器
initThemeResolver(context);
// 4. 初始化处理器映射器
initHandlerMappings(context);
// 5. 初始化处理器适配器
initHandlerAdapters(context);
// 6. 初始化处理器异常解析器
initHandlerExceptionResolvers(context);
// 7. 初始化请求到视图名翻译器
initRequestToViewNameTranslator(context);
// 8. 初始化视图解析器
initViewResolvers(context);
// 9. 初始化FlashMap管理器
initFlashMapManager(context);
}
DispatcherServlet组件初始化图:
DispatcherServlet初始化
├── 1. MultipartResolver(文件上传)
├── 2. LocaleResolver(国际化)
├── 3. ThemeResolver(主题)
├── 4. HandlerMappings(处理器映射)
│ ├── RequestMappingHandlerMapping
│ ├── BeanNameUrlHandlerMapping
│ └── RouterFunctionMapping
├── 5. HandlerAdapters(处理器适配器)
│ ├── RequestMappingHandlerAdapter
│ ├── HttpRequestHandlerAdapter
│ ├── SimpleControllerHandlerAdapter
│ └── HandlerFunctionAdapter
├── 6. HandlerExceptionResolvers(异常解析)
├── 7. RequestToViewNameTranslator(视图名翻译)
├── 8. ViewResolvers(视图解析)
│ ├── InternalResourceViewResolver
│ ├── BeanNameViewResolver
│ └── ContentNegotiatingViewResolver
└── 9. FlashMapManager(重定向属性)
8.3 Spring MVC完整请求处理流程
java
// DispatcherServlet的核心请求处理方法
protected void doDispatch(HttpServletRequest request,
HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
// 1. 检查文件上传请求
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// 2. 确定处理器
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// 3. 获取处理器适配器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 4. 执行预处理拦截器
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 5. 实际调用处理器
mv = ha.handle(processedRequest, response,
mappedHandler.getHandler());
// 6. 设置默认视图名
applyDefaultViewName(processedRequest, mv);
// 7. 执行后处理拦截器
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
dispatchException = new NestedServletException(...);
}
// 8. 处理结果
processDispatchResult(processedRequest, response,
mappedHandler, mv, dispatchException);
}
finally {
// 9. 触发完成回调
if (mappedHandler != null) {
mappedHandler.triggerAfterCompletion(processedRequest,
response, null);
}
// 10. 清理多部分请求
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
第九章:启动完成与Runner执行
9.1 启动后回调机制
Spring Boot提供了两种Runner接口,用于在应用启动后执行特定逻辑:
java
// ApplicationRunner示例
@Component
@Order(1) // 执行顺序
public class DatabaseInitializer implements ApplicationRunner {
private final DataSource dataSource;
private final JdbcTemplate jdbcTemplate;
public DatabaseInitializer(DataSource dataSource) {
this.dataSource = dataSource;
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("初始化数据库...");
// 执行数据库初始化脚本
Resource resource = new ClassPathResource("schema.sql");
String sql = StreamUtils.copyToString(
resource.getInputStream(), StandardCharsets.UTF_8);
jdbcTemplate.execute(sql);
// 初始化数据
initSampleData();
}
private void initSampleData() {
String insertSql = "INSERT INTO users (username, email) VALUES (?, ?)";
jdbcTemplate.update(insertSql, "admin", "admin@example.com");
jdbcTemplate.update(insertSql, "user", "user@example.com");
}
}
// CommandLineRunner示例
@Component
@Order(2)
public class CacheWarmUpRunner implements CommandLineRunner {
private final CacheManager cacheManager;
private final UserRepository userRepository;
public CacheWarmUpRunner(CacheManager cacheManager,
UserRepository userRepository) {
this.cacheManager = cacheManager;
this.userRepository = userRepository;
}
@Override
public void run(String... args) throws Exception {
System.out.println("预热缓存...");
// 预热用户缓存
Cache userCache = cacheManager.getCache("users");
List<User> users = userRepository.findAll();
for (User user : users) {
userCache.put(user.getId(), user);
}
// 检查命令行参数
if (args.length > 0) {
System.out.println("命令行参数:");
for (String arg : args) {
System.out.println(" - " + arg);
}
}
}
}
9.2 callRunners方法实现
java
private void callRunners(ApplicationContext context,
ApplicationArguments args) {
List<Object> runners = new ArrayList<>();
// 收集所有ApplicationRunner和CommandLineRunner
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
// 排序
AnnotationAwareOrderComparator.sort(runners);
// 执行Runner
for (Object runner : new LinkedHashSet<>(runners)) {
if (runner instanceof ApplicationRunner) {
callRunner((ApplicationRunner) runner, args);
}
if (runner instanceof CommandLineRunner) {
callRunner((CommandLineRunner) runner, args);
}
}
}
private void callRunner(ApplicationRunner runner,
ApplicationArguments args) {
try {
runner.run(args);
} catch (Exception ex) {
throw new IllegalStateException("Failed to execute ApplicationRunner", ex);
}
}
private void callRunner(CommandLineRunner runner,
ApplicationArguments args) {
try {
runner.run(args.getSourceArgs());
} catch (Exception ex) {
throw new IllegalStateException("Failed to execute CommandLineRunner", ex);
}
}
第十章:生产环境优化与调试技巧
10.1 启动性能优化
java
@SpringBootApplication
public class Application {
public static void main(String[] args) {
// 1. 关闭Banner打印加速启动
SpringApplication app = new SpringApplication(Application.class);
app.setBannerMode(Banner.Mode.OFF);
// 2. 延迟初始化(Spring Boot 2.2+)
app.setLazyInitialization(true);
// 3. 添加启动监听器记录启动时间
app.addListeners(new ApplicationStartupListener());
app.run(args);
}
static class ApplicationStartupListener
implements ApplicationListener<ApplicationReadyEvent> {
private long startTime;
@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
long endTime = System.currentTimeMillis();
System.out.println("应用启动完成,耗时: " +
(endTime - startTime) + "ms");
}
}
}
// 4. 配置优化
@Configuration
public class OptimizationConfig {
// 减少扫描包范围
@Bean
public static BeanDefinitionRegistryPostProcessor scanningOptimizer() {
return registry -> {
// 动态调整组件扫描路径
if (registry instanceof BeanDefinitionRegistry) {
BeanDefinitionRegistry bdr = (BeanDefinitionRegistry) registry;
// 移除不必要的扫描
}
};
}
}
10.2 启动过程调试
使用启动参数调试:
bash
# 启用调试日志
java -jar application.jar --debug
# 查看自动配置报告
java -jar application.jar --debug | grep "CONDITIONS EVALUATION REPORT"
# 启用Spring Boot Actuator的启动端点
# application.yml
management:
endpoints:
web:
exposure:
include: startup
endpoint:
startup:
enabled: true
# 访问启动报告
curl http://localhost:8080/actuator/startup
自定义启动诊断工具:
java
@Component
public class StartupDiagnosticTool {
private final ConfigurableApplicationContext context;
private final List<StartupStep> startupSteps = new ArrayList<>();
public StartupDiagnosticTool(ConfigurableApplicationContext context) {
this.context = context;
// 注册Bean定义加载监听器
context.addBeanFactoryPostProcessor(factory -> {
if (factory instanceof ConfigurableListableBeanFactory) {
((ConfigurableListableBeanFactory) factory)
.addBeanPostProcessor(new StartupDiagnosticBeanPostProcessor());
}
});
}
public void printStartupReport() {
System.out.println("======= Spring Boot启动诊断报告 =======");
System.out.println("启动总耗时: " + calculateTotalDuration() + "ms");
System.out.println("\n关键步骤耗时:");
startupSteps.stream()
.sorted(Comparator.comparingLong(StartupStep::getDuration).reversed())
.limit(10)
.forEach(step -> {
System.out.printf(" %-40s %6dms%n",
step.getName(), step.getDuration());
});
System.out.println("\nBean初始化统计:");
Map<String, Long> beanInitTimes = getBeanInitializationTimes();
beanInitTimes.entrySet().stream()
.sorted(Map.Entry.comparingByValue().reversed())
.limit(10)
.forEach(entry -> {
System.out.printf(" %-40s %6dms%n",
entry.getKey(), entry.getValue());
});
}
static class StartupStep {
private String name;
private long startTime;
private long endTime;
public long getDuration() {
return endTime - startTime;
}
// getters and setters
}
}
第十一章:常见启动问题与解决方案
11.1 启动失败常见原因
java
// 1. 配置问题
@Configuration
public class CommonStartupIssues {
// 问题1: 端口被占用
// 解决方案: 修改端口或停止占用进程
// application.yml
// server:
// port: 8081
// 问题2: 数据库连接失败
// 解决方案: 检查数据库配置
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
// 问题3: Bean循环依赖
// 解决方案1: 使用@Lazy延迟加载
@Service
public class ServiceA {
private final ServiceB serviceB;
@Lazy
public ServiceA(ServiceB serviceB) {
this.serviceB = serviceB;
}
}
// 解决方案2: 使用Setter注入
@Service
public class ServiceC {
private ServiceD serviceD;
@Autowired
public void setServiceD(ServiceD serviceD) {
this.serviceD = serviceD;
}
}
// 解决方案3: 使用@DependsOn明确依赖顺序
@Service
@DependsOn("serviceE")
public class ServiceF {
// ...
}
}
11.2 启动性能瓶颈识别
java
// 使用Spring Boot Actuator监控启动指标
@Configuration
@EnableConfigurationProperties(CacheProperties.class)
public class StartupMetricsConfig {
@Bean
public MeterRegistryCustomizer<MeterRegistry> metricsCommonTags() {
return registry -> registry.config().commonTags(
"application", "myapp",
"region", "us-east-1"
);
}
@Bean
public ApplicationRunner startupMetricsCollector(MeterRegistry registry) {
return args -> {
// 记录启动指标
Timer.Sample sample = Timer.start(registry);
// 模拟启动任务
initializeCaches();
warmUpServices();
sample.stop(registry.timer("application.startup.time"));
// 记录内存使用
Runtime runtime = Runtime.getRuntime();
registry.gauge("application.startup.memory.used",
runtime.totalMemory() - runtime.freeMemory());
registry.gauge("application.startup.memory.max",
runtime.maxMemory());
};
}
// 使用JMX监控Bean初始化
@Bean
public static MBeanExporter mbeanExporter() {
MBeanExporter exporter = new MBeanExporter();
exporter.setAutodetect(true);
exporter.setExcludedBeans("dataSource");
return exporter;
}
}
第十二章:Spring Boot启动机制的未来演进
12.1 Spring Boot 2.4+的新特性
java
// 1. 新的配置文件处理方式
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(Application.class);
// 配置层次结构
app.setDefaultProperties(Collections.singletonMap(
"spring.config.use-legacy-processing", "false"
));
// 支持Config Data API
app.run(args);
}
}
// 2. 启动生命周期改进
@Configuration
public class NewLifecycleConfig {
// 新的生命周期回调
@Bean
public ApplicationStartup applicationStartup() {
return new FlightRecorderApplicationStartup();
}
// 分层启动步骤
@Bean
public ApplicationRunner startupProfiler(ApplicationStartup startup) {
return args -> {
StartupStep step = startup.start("application.initialization");
try {
// 初始化代码
step.tag("phase", "cache");
initializeCache();
step.tag("phase", "database");
initializeDatabase();
step.tag("phase", "services");
initializeServices();
} finally {
step.end();
}
};
}
}
12.2 Spring Boot 3.0展望
java
// 1. 全面支持Java 17+
@SpringBootApplication
public class Application {
public static void main(String[] args) {
// 支持记录类
record User(String name, String email) {}
// 支持模式匹配
Object obj = "Hello";
if (obj instanceof String s) {
System.out.println(s.toLowerCase());
}
SpringApplication.run(Application.class, args);
}
}
// 2. 响应式编程的全面支持
@Configuration
@EnableWebFlux
public class ReactiveConfig {
@Bean
public RouterFunction<ServerResponse> routes() {
return RouterFunctions.route()
.GET("/api/users", this::getAllUsers)
.POST("/api/users", this::createUser)
.build();
}
private Mono<ServerResponse> getAllUsers(ServerRequest request) {
return ServerResponse.ok()
.contentType(MediaType.APPLICATION_JSON)
.body(userRepository.findAll(), User.class);
}
}
总结:Spring Boot启动的艺术
Spring Boot的启动过程是一个精心设计的交响乐,每个组件都在正确的时间演奏自己的部分。从main方法的简单调用开始,经过环境准备、上下文创建、自动配置、Bean加载,到最终的Web服务器启动,每一步都体现了Spring Boot"约定优于配置"的设计哲学。
启动过程的关键启示:
- 扩展性设计:通过SPI机制和事件监听器,提供了丰富的扩展点
- 条件化装配:智能的条件注解实现了"按需配置"
- 生命周期管理:清晰的启动阶段划分,便于理解和调试
- 生产就绪:内置的健康检查、指标收集等特性
通过深入理解Spring Boot的启动机制,开发者可以:
- 更有效地诊断和解决启动问题
- 合理优化应用启动性能
- 设计更符合Spring Boot哲学的应用程序
- 充分利用Spring Boot提供的各种特性
Spring Boot的启动过程虽然复杂,但正是这种复杂性被良好地封装和抽象,才使得开发者能够专注于业务逻辑,而不是框架配置。这正是Spring Boot最大的价值所在。