一、启动入口:不止是main方法那么简单
1.1 静态启动流程剖析
java
@SpringBootApplication // 这个注解包含了三个关键注解
public class DemoApplication {
public static void main(String[] args) {
// 看起来简单的一行,背后隐藏着完整的启动链条
SpringApplication.run(DemoApplication.class, args);
}
}
这行代码触发的完整调用链:
SpringApplication.run()
├── new SpringApplication(primarySources)
│ ├── deduceWebApplicationType() // 判断Web应用类型
│ ├── getSpringFactoriesInstances() // 加载ApplicationContextInitializer
│ ├── getSpringFactoriesInstances() // 加载ApplicationListener
│ └── deduceMainApplicationClass() // 推断主类
├── run(args)
│ ├── StopWatch.start()
│ ├── configureHeadlessProperty()
│ ├── getRunListeners() // 获取SpringApplicationRunListener
│ ├── listeners.starting() // 触发starting事件
│ ├── prepareEnvironment() // 准备环境
│ ├── listeners.environmentPrepared() // 环境准备完成
│ ├── createApplicationContext() // 创建应用上下文
│ ├── prepareContext() // 准备上下文
│ ├── refreshContext() // 刷新上下文(核心!)
│ ├── afterRefresh() // 刷新后处理
│ ├── listeners.started() // 触发started事件
│ └── listeners.running() // 触发running事件
1.2 Web应用类型推断的精确逻辑
java
public class WebApplicationTypeDetector {
enum WebApplicationType {
NONE, // 非Web应用
SERVLET, // Servlet Web应用
REACTIVE // 响应式Web应用
}
private WebApplicationType deduceFromClasspath() {
// 关键检测逻辑(按顺序):
// 1. 检测是否响应式Web应用
// 检查是否存在DispatcherHandler且不存在DispatcherServlet
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
// 2. 检测是否Servlet Web应用
// 关键类检测:Servlet、ConfigurableWebApplicationContext
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
// 不存在任何一个关键类,说明不是Servlet应用
return WebApplicationType.NONE;
}
}
// 3. 默认返回Servlet应用
return WebApplicationType.SERVLET;
}
// 实际检测的类列表
private static final String WEBFLUX_INDICATOR_CLASS =
"org.springframework.web.reactive.DispatcherHandler";
private static final String WEBMVC_INDICATOR_CLASS =
"org.springframework.web.servlet.DispatcherServlet";
private static final String[] SERVLET_INDICATOR_CLASSES = {
"javax.servlet.Servlet",
"org.springframework.web.context.ConfigurableWebApplicationContext"
};
}
二、@SpringBootApplication注解的三位一体
2.1 组合注解的完整结构
java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration // 这是@Configuration的变体
@EnableAutoConfiguration // 启用自动配置的核心
@ComponentScan( // 组件扫描
excludeFilters = {
@Filter(
type = FilterType.CUSTOM,
classes = TypeExcludeFilter.class
),
@Filter(
type = FilterType.CUSTOM,
classes = AutoConfigurationExcludeFilter.class
)
}
)
public @interface SpringBootApplication {
// 这三个注解各自承担不同职责
}
// 深入@SpringBootConfiguration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration // 本质上就是一个@Configuration
public @interface SpringBootConfiguration {
// 区别:用于标识这是Spring Boot的配置类
// IDE可以基于此提供更好的支持
}
2.2 @EnableAutoConfiguration的激活机制
java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage // 关键1:注册基础包
@Import(AutoConfigurationImportSelector.class) // 关键2:导入选择器
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
// 可以排除特定自动配置类
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
// AutoConfigurationImportSelector的核心逻辑
public class AutoConfigurationImportSelector implements ... {
protected List<String> getCandidateConfigurations(
AnnotationMetadata metadata,
AnnotationAttributes attributes) {
// 关键:从spring.factories加载自动配置类
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader()
);
// 去重处理
configurations = removeDuplicates(configurations);
// 排序(@AutoConfigureOrder, @AutoConfigureBefore, @AutoConfigureAfter)
configurations = sort(configurations, autoConfigurationMetadata);
// 排除用户指定的类
configurations = getExclusions(attributes, configurations);
// 过滤(基于各种条件注解)
configurations = filter(configurations, autoConfigurationMetadata);
// 触发自动配置导入事件
fireAutoConfigurationImportEvents(configurations, exclusions);
return configurations;
}
// 加载的key
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}
}
三、自动配置加载的详细流程
3.1 spring.factories的加载和过滤机制
properties
# META-INF/spring.factories中的配置示例
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration
# 实际加载过程:
# 1. 从所有jar包的META-INF/spring.factories加载
# 2. 合并所有EnableAutoConfiguration的值
# 3. 去重、排序、过滤
条件注解的过滤机制:
java
public class ConditionalFilter {
// Spring Boot的条件注解体系
interface Conditions {
// 类条件
@ConditionalOnClass({DataSource.class, EmbeddedDatabaseType.class})
// Bean条件
@ConditionalOnMissingBean(DataSource.class)
// 属性条件
@ConditionalOnProperty(prefix = "spring.datasource", name = "url")
// 资源条件
@ConditionalOnResource(resources = "classpath:db.properties")
// Web应用条件
@ConditionalOnWebApplication
// 表达式条件
@ConditionalOnExpression("${app.feature.enabled:false}")
}
// 过滤逻辑示例
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 1. 解析条件注解
List<Condition> conditions = getConditions(metadata);
// 2. 按顺序评估每个条件
for (Condition condition : conditions) {
if (!condition.matches(context, metadata)) {
// 任何一个条件不匹配,整个配置类不加载
return false;
}
}
// 3. 所有条件通过,配置类生效
return true;
}
}
3.2 自动配置类的执行顺序控制
java
// 自动配置类的三种顺序控制方式
// 方式1:@AutoConfigureOrder(数值越小越优先)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration
public class EarlyAutoConfiguration {
// 这个配置类会最先执行
}
// 方式2:@AutoConfigureBefore(在指定配置类之前执行)
@Configuration
@AutoConfigureBefore({DataSourceAutoConfiguration.class})
public class BeforeDataSourceAutoConfiguration {
// 在DataSourceAutoConfiguration之前执行
}
// 方式3:@AutoConfigureAfter(在指定配置类之后执行)
@Configuration
@ConditionalOnWebApplication
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
public class DispatcherServletAutoConfiguration {
// 在ServletWebServerFactoryAutoConfiguration之后执行
// 因为需要先有Servlet容器,才能配置DispatcherServlet
}
// Spring Boot内部的排序算法
public class AutoConfigurationSorter {
public List<String> sort(List<String> configurations) {
// 构建有向图,检测循环依赖
Graph graph = new Graph(configurations.size());
// 第一步:处理@AutoConfigureOrder
configurations.sort((o1, o2) -> {
int i1 = getOrder(o1);
int i2 = getOrder(o2);
return Integer.compare(i1, i2);
});
// 第二步:处理@AutoConfigureBefore/@AutoConfigureAfter
for (String configuration : configurations) {
Class<?> source = ClassUtils.forName(configuration, null);
// 处理@AutoConfigureBefore
AutoConfigureBefore before = source.getAnnotation(AutoConfigureBefore.class);
if (before != null) {
for (Class<?> target : before.value()) {
graph.addEdge(configuration, target.getName());
}
}
// 处理@AutoConfigureAfter
AutoConfigureAfter after = source.getAnnotation(AutoConfigureAfter.class);
if (after != null) {
for (Class<?> target : after.value()) {
graph.addEdge(target.getName(), configuration);
}
}
}
// 第三步:拓扑排序
return topologicalSort(graph);
}
}
四、ApplicationContext的创建与刷新
4.1 应用上下文的类型选择与创建
java
public class ApplicationContextCreator {
// 根据Web应用类型创建不同的ApplicationContext
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
// 根据WebApplicationType选择不同的上下文类
switch (this.webApplicationType) {
case SERVLET:
contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
// 实际:AnnotationConfigServletWebServerApplicationContext
break;
case REACTIVE:
contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
// 实际:AnnotationConfigReactiveWebServerApplicationContext
break;
default:
contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
// 实际:AnnotationConfigApplicationContext
}
} catch (ClassNotFoundException ex) {
throw new IllegalStateException(...);
}
}
// 使用反射创建实例
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
}
// Servlet Web应用的上下文结构
public class AnnotationConfigServletWebServerApplicationContext
extends ServletWebServerApplicationContext
implements AnnotationConfigRegistry {
// 包含的关键组件:
// 1. AnnotatedBeanDefinitionReader - 处理注解配置
// 2. ClassPathBeanDefinitionScanner - 类路径扫描
// 3. ServletWebServerFactory - 内嵌Servlet容器工厂
// 4. DispatcherServlet - Spring MVC前端控制器
public AnnotationConfigServletWebServerApplicationContext() {
// 创建读取器和扫描器
this.reader = new AnnotatedBeanDefinitionReader(this);
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
}
4.2 refresh()方法的十二个核心步骤
java
public abstract class AbstractApplicationContext {
// 这是Spring容器初始化的核心方法
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 1. 准备刷新:记录启动时间、激活状态、初始化属性源
prepareRefresh();
// 2. 获取新的BeanFactory:销毁旧的,创建新的
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 3. 准备BeanFactory:配置类加载器、Bean后置处理器等
prepareBeanFactory(beanFactory);
try {
// 4. 后置处理BeanFactory(模板方法,子类可扩展)
postProcessBeanFactory(beanFactory);
// 5. 调用BeanFactory后置处理器
invokeBeanFactoryPostProcessors(beanFactory);
// 6. 注册Bean后置处理器
registerBeanPostProcessors(beanFactory);
// 7. 初始化消息源(国际化)
initMessageSource();
// 8. 初始化事件广播器
initApplicationEventMulticaster();
// 9. 初始化特殊Bean(模板方法)
onRefresh();
// 10. 注册监听器
registerListeners();
// 11. 实例化所有非懒加载的单例Bean
finishBeanFactoryInitialization(beanFactory);
// 12. 完成刷新:发布ContextRefreshedEvent
finishRefresh();
} catch (BeansException ex) {
// 异常处理:销毁已创建的Bean
destroyBeans();
cancelRefresh(ex);
throw ex;
}
}
}
}
4.3 Spring Boot对refresh()的扩展
java
public class ServletWebServerApplicationContext {
// 重写onRefresh()方法,创建内嵌Servlet容器
@Override
protected void onRefresh() {
super.onRefresh();
try {
// 关键:创建Web服务器
createWebServer();
} catch (Throwable ex) {
throw new ApplicationContextException(...);
}
}
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = getServletContext();
if (webServer == null && servletContext == null) {
// 从BeanFactory获取ServletWebServerFactory
ServletWebServerFactory factory = getWebServerFactory();
// 创建WebServer
this.webServer = factory.getWebServer(getSelfInitializer());
// 注册Shutdown Hook
getBeanFactory().registerSingleton(
"webServerGracefulShutdown",
new WebServerGracefulShutdownLifecycle(this.webServer)
);
getBeanFactory().registerSingleton(
"webServerStartStop",
new WebServerStartStopLifecycle(this, this.webServer)
);
} else if (servletContext != null) {
// 已存在ServletContext(外置容器)
try {
getSelfInitializer().onStartup(servletContext);
} catch (ServletException ex) {
throw new ApplicationContextException(...);
}
}
initPropertySources();
}
// 获取ServletWebServerFactory的优先级
private ServletWebServerFactory getWebServerFactory() {
// 从BeanFactory获取所有ServletWebServerFactory
String[] beanNames = getBeanFactory()
.getBeanNamesForType(ServletWebServerFactory.class);
if (beanNames.length == 0) {
throw new ApplicationContextException(...);
}
if (beanNames.length > 1) {
// 多个时,优先使用@Primary标注的
// 然后按@Order排序
// 最后按bean名称排序
}
return getBeanFactory().getBean(beanNames[0],
ServletWebServerFactory.class);
}
}
五、内嵌Servlet容器的启动细节
5.1 内嵌Tomcat的启动过程
java
public class TomcatServletWebServerFactory {
public WebServer getWebServer(ServletContextInitializer... initializers) {
// 1. 创建Tomcat实例
Tomcat tomcat = new Tomcat();
// 2. 配置基础目录
File baseDir = (this.baseDirectory != null) ? this.baseDirectory
: createTempDir("tomcat");
tomcat.setBaseDir(baseDir.getAbsolutePath());
// 3. 创建Connector
Connector connector = new Connector(this.protocol);
connector.setThrowOnFailure(true);
tomcat.getService().addConnector(connector);
customizeConnector(connector);
tomcat.setConnector(connector);
// 4. 配置Engine和Host
tomcat.getEngine().setBackgroundProcessorDelay(-1);
for (Connector additionalConnector : this.additionalTomcatConnectors) {
tomcat.getService().addConnector(additionalConnector);
}
// 5. 准备Context
prepareContext(tomcat.getHost(), initializers);
// 6. 返回可用的WebServer
return getTomcatWebServer(tomcat);
}
protected void prepareContext(Host host,
ServletContextInitializer[] initializers) {
// 创建TomcatEmbeddedContext
TomcatEmbeddedContext context = new TomcatEmbeddedContext();
// 配置Context
context.setName(getContextPath());
context.setDisplayName(getDisplayName());
context.setPath(getContextPath());
// 添加ServletContainerInitializer
context.addServletContainerInitializer((c) -> {
for (ServletContextInitializer initializer : initializers) {
initializer.onStartup(c);
}
}, null);
// 配置Session、MIME类型等
configureContext(context);
// 添加到Host
host.addChild(context);
}
}
// TomcatWebServer的启动
public class TomcatWebServer implements WebServer {
private void initialize() throws WebServerException {
// 1. 添加Connector
TomcatConnectorCustomizer compressionCustomizer = getCompressionCustomizer();
if (compressionCustomizer != null) {
compressionCustomizer.customize(this.tomcat.getConnector());
}
// 2. 启动Tomcat
this.tomcat.start();
// 3. 启动监控线程
startDaemonAwaitThread();
}
private void startDaemonAwaitThread() {
// 创建非守护线程监听关闭命令
Thread awaitThread = new Thread("container-" + (containerCounter.get())) {
@Override
public void run() {
// 监听8005端口的SHUTDOWN命令
TomcatWebServer.this.tomcat.getServer().await();
}
};
awaitThread.setContextClassLoader(getClass().getClassLoader());
awaitThread.setDaemon(false);
awaitThread.start();
}
}
5.2 自动端口分配与健康检查
java
public class PortManagement {
// Spring Boot的端口分配策略
public int determinePort(ServletWebServerFactory factory) {
// 优先级:
// 1. 显式配置的server.port
// 2. 环境变量PORT(云平台兼容)
// 3. 默认端口(8080)
int port = getProperty("server.port", Integer.class, 0);
if (port == 0) {
port = getEnv("PORT", Integer.class, 8080);
}
// 特殊端口处理
if (port == -1) {
// -1表示禁用HTTP端口
return -1;
}
if (port == 0) {
// 0表示随机端口
port = SocketUtils.findAvailableTcpPort();
logger.info("Server initialized with port: " + port);
}
return port;
}
}
// 健康检查端口的自动配置
@Configuration
@ConditionalOnWebApplication
@ConditionalOnProperty(value = "management.endpoint.health.enabled",
havingValue = "true", matchIfMissing = true)
public class HealthEndpointConfiguration {
@Bean
@ConditionalOnMissingBean
public HealthEndpoint healthEndpoint() {
return new HealthEndpoint();
}
// 如果使用不同的管理端口
@Configuration
@ConditionalOnProperty(value = "management.server.port")
public static class DifferentPortConfiguration {
@Bean
public ServletWebServerFactoryManagementContextCustomizer
managementPortCustomizer() {
return (context) -> {
// 配置管理端口的Servlet容器
int port = context.getEnvironment()
.getProperty("management.server.port", Integer.class);
configureManagementPort(context, port);
};
}
}
}
六、启动事件与监听机制
6.1 完整的启动事件序列
java
public class SpringApplicationEventSequence {
// Spring Boot启动过程中的完整事件流
public enum EventType {
// 1. 应用启动事件(按顺序)
APPLICATION_STARTING, // 启动开始
APPLICATION_ENVIRONMENT_PREPARED, // 环境准备完成
APPLICATION_CONTEXT_INITIALIZED, // 上下文初始化
APPLICATION_PREPARED, // 应用准备完成
APPLICATION_STARTED, // 应用已启动
APPLICATION_READY, // 应用就绪
APPLICATION_FAILED, // 启动失败
// 2. 上下文事件(来自Spring框架)
CONTEXT_REFRESHED, // 上下文刷新完成
CONTEXT_CLOSED, // 上下文关闭
CONTEXT_STOPPED, // 上下文停止
}
// 事件监听器的执行时机
public static class StartupListener implements ApplicationListener<ApplicationStartingEvent> {
@Override
public void onApplicationEvent(ApplicationStartingEvent event) {
// 最早执行的事件,此时:
// 1. 环境尚未准备
// 2. 上下文尚未创建
// 3. Bean尚未初始化
// 用途:最早期的初始化工作
}
}
// 内置的重要监听器
public static class ImportantListeners {
// 1. 配置FileEncoding(解决中文乱码)
public class FileEncodingApplicationListener
implements ApplicationListener<ApplicationEnvironmentPreparedEvent> {
@Override
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
// 确保文件编码正确
System.setProperty("file.encoding", "UTF-8");
}
}
// 2. LoggingApplicationListener(配置日志)
public class LoggingApplicationListener
implements ApplicationListener<ApplicationStartingEvent>,
ApplicationListener<ApplicationEnvironmentPreparedEvent>,
ApplicationListener<ApplicationPreparedEvent>,
ApplicationListener<ContextClosedEvent> {
// 分阶段初始化日志系统
}
// 3. BackgroundPreinitializer(后台预初始化)
public class BackgroundPreinitializer
implements ApplicationListener<ApplicationStartingEvent> {
@Override
public void onApplicationEvent(ApplicationStartingEvent event) {
// 在后台线程中预初始化:
// 1. Jackson的ObjectMapper
// 2. Validation的Validator
// 3. Charset的defaultCharset
// 4. ResourceBundle的Control
// 减少应用启动时的延迟
}
}
}
}
6.2 ApplicationRunner与CommandLineRunner的执行
java
// 这两个接口允许在应用启动后执行特定代码
public class RunnersExecution {
// 执行顺序:先ApplicationRunner,后CommandLineRunner
// 相同类型的Runner按@Order排序
@Component
@Order(1)
public class DatabaseInitializer implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
// 可以访问解析后的参数
boolean debug = args.containsOption("debug");
List<String> nonOptionArgs = args.getNonOptionArgs();
// 执行初始化逻辑
initializeDatabase();
}
}
@Component
@Order(2)
public class CacheWarmupRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
// 接收原始参数
warmupCache();
}
}
// Spring Boot内部如何执行Runner
private void callRunners(ApplicationContext context, ApplicationArguments args) {
List<Object> runners = new ArrayList<>();
// 收集所有ApplicationRunner
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
// 收集所有CommandLineRunner
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
// 排序(@Order、@Priority、实现Ordered接口)
AnnotationAwareOrderComparator.sort(runners);
// 执行
for (Object runner : runners) {
if (runner instanceof ApplicationRunner) {
((ApplicationRunner) runner).run(args);
} else if (runner instanceof CommandLineRunner) {
((CommandLineRunner) runner).run(args.getSourceArgs());
}
}
}
}
七、启动性能优化关键点
7.1 类路径扫描优化
java
public class ScanningOptimization {
// 优化点1:减少扫描范围
@SpringBootApplication(
scanBasePackages = {"com.example.core", "com.example.web"}
// 明确指定扫描包,避免全类路径扫描
)
public class Application {
}
// 优化点2:使用索引文件(需要spring-context-indexer依赖)
// META-INF/spring.components文件内容:
// com.example.MyComponent=org.springframework.stereotype.Component
// 优化点3:延迟初始化
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(Application.class);
app.setLazyInitialization(true); // 启用延迟初始化
app.run(args);
}
}
// 优化点4:排除不必要的自动配置
@SpringBootApplication(
exclude = {
DataSourceAutoConfiguration.class, // 如果不使用数据库
RedisAutoConfiguration.class, // 如果不使用Redis
KafkaAutoConfiguration.class // 如果不使用Kafka
}
)
public class Application {
}
}
7.2 Bean定义的优化加载
java
public class BeanDefinitionOptimization {
// Spring Boot 2.2+ 引入的Bean定义覆盖策略
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(Application.class);
// 禁止Bean定义覆盖(默认)
// 避免意外覆盖导致的难以调试的问题
app.setAllowBeanDefinitionOverriding(false);
app.run(args);
}
}
// Bean的懒加载配置
@Configuration
public class LazyBeanConfiguration {
@Bean
@Lazy // 第一次使用时才初始化
public ExpensiveBean expensiveBean() {
return new ExpensiveBean();
}
@Bean
@Scope("prototype") // 每次注入时创建新实例
public PrototypeBean prototypeBean() {
return new PrototypeBean();
}
}
// 条件Bean的优化定义
@Configuration
public class ConditionalBeanConfiguration {
@Bean
@ConditionalOnClass(name = "com.example.ExternalService")
@ConditionalOnProperty(name = "external.service.enabled", havingValue = "true")
public ExternalServiceClient externalServiceClient() {
// 只有满足条件时才创建这个Bean
return new ExternalServiceClient();
}
}
}
八、面试深度问题解析
Q1:Spring Boot启动时,Bean的加载顺序是怎样的?
java
// 精确的Bean加载顺序:
public class BeanLoadingSequence {
// 阶段1:BeanFactoryPostProcessor(修改Bean定义)
// 1. ConfigurationClassPostProcessor(处理@Configuration)
// 2. PropertySourcesPlaceholderConfigurer(处理${})
// 3. 自定义BeanFactoryPostProcessor
// 阶段2:BeanPostProcessor(干预Bean生命周期)
// 1. AutowiredAnnotationBeanPostProcessor(处理@Autowired)
// 2. CommonAnnotationBeanPostProcessor(处理@Resource)
// 3. PersistenceAnnotationBeanPostProcessor(处理JPA)
// 4. 自定义BeanPostProcessor
// 阶段3:普通Bean的实例化
// 1. @DependsOn指定的依赖Bean
// 2. 非懒加载的单例Bean(按依赖顺序)
// 3. 所有Bean初始化完成后,SmartInitializingSingleton的回调
// 关键原则:
// 1. BeanFactoryPostProcessor > BeanPostProcessor > 普通Bean
// 2. 依赖关系决定顺序
// 3. @DependsOn显式指定顺序
// 4. @Order只对同一类型的Bean有效
}
Q2:Spring Boot如何支持多环境配置?
java
public class MultiEnvironmentSupport {
// 配置文件的加载顺序(优先级从高到低):
// 1. 命令行参数(--server.port=8081)
// 2. SPRING_APPLICATION_JSON环境变量
// 3. ServletConfig初始化参数
// 4. ServletContext初始化参数
// 5. JNDI属性(java:comp/env)
// 6. Java系统属性(System.getProperties())
// 7. 操作系统环境变量
// 8. random.*属性(RandomValuePropertySource)
// 9. Profile-specific应用属性(application-{profile}.yml)
// 10. 应用属性(application.yml)
// 11. @PropertySource注解
// 12. 默认属性(SpringApplication.setDefaultProperties())
// 激活Profile的方式:
// 1. 命令行:--spring.profiles.active=dev
// 2. 环境变量:SPRING_PROFILES_ACTIVE=dev
// 3. Java系统属性:-Dspring.profiles.active=dev
// 4. 配置文件:spring.profiles.active: dev
// 5. SpringApplication.setAdditionalProfiles()
}
Q3:Spring Boot应用如何优雅关闭?
java
public class GracefulShutdown {
// 方式1:使用Spring Boot的优雅关闭
@Bean
public GracefulShutdown gracefulShutdown() {
return new GracefulShutdown();
}
static class GracefulShutdown implements TomcatConnectorCustomizer,
ApplicationListener<ContextClosedEvent> {
private volatile Connector connector;
@Override
public void customize(Connector connector) {
this.connector = connector;
}
@Override
public void onApplicationEvent(ContextClosedEvent event) {
// 1. 停止接收新请求
this.connector.pause();
// 2. 获取当前Executor
Executor executor = this.connector.getProtocolHandler().getExecutor();
if (executor instanceof ThreadPoolExecutor) {
ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor;
// 3. 等待正在处理的请求完成(最多30秒)
threadPoolExecutor.shutdown();
try {
if (!threadPoolExecutor.awaitTermination(30, TimeUnit.SECONDS)) {
// 4. 强制关闭
threadPoolExecutor.shutdownNow();
}
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
}
}
// 方式2:使用Spring Boot Actuator的shutdown端点
// 需要配置:management.endpoint.shutdown.enabled=true
// 发送POST请求:/actuator/shutdown
}
总结
Spring Boot启动流程是一个精心设计的完整链条,从@SpringBootApplication注解的解析,到自动配置的加载,再到ApplicationContext的创建和刷新,最后到内嵌Servlet容器的启动,每一步都有其明确的职责和优化考虑。
理解这个流程不仅要记住各个步骤的顺序,更要理解每个环节的设计意图和可扩展点,这样才能在面试中展现出深度,也能在实际工作中更好地排查问题和进行性能优化。