SpringBoot启动流程深度剖析:从@SpringBootApplication到Servlet容器就绪

一、启动入口:不止是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容器的启动,每一步都有其明确的职责和优化考虑。

理解这个流程不仅要记住各个步骤的顺序,更要理解每个环节的设计意图和可扩展点,这样才能在面试中展现出深度,也能在实际工作中更好地排查问题和进行性能优化。

相关推荐
OpenTiny社区2 小时前
TinyPro v1.4.0 正式发布:支持 Spring Boot、移动端适配、新增卡片列表和高级表单页面
java·前端·spring boot·后端·开源·opentiny
计算机毕设指导62 小时前
基于微信小程序民宿预订管理系统【源码文末联系】
java·spring boot·mysql·微信小程序·小程序·tomcat·maven
爱码猿3 小时前
Springboot结合thymeleaf模板生成pdf文件
spring boot·后端·pdf
Coder_Boy_4 小时前
基于SpringAI的在线考试系统软件系统验收案例
人工智能·spring boot·软件工程·devops
qq_12498707534 小时前
基于SSM框架的智能密室逃脱信息管理系统(源码+论文+部署+安装)
java·大数据·人工智能·spring boot·后端·毕业设计·计算机毕业设计
用户2190326527355 小时前
Spring Boot Admin终极监控方案:从零搭建企业级微服务监控平台,含高可用集群配置
spring boot·微服务·监控
咕叽咕叽的汪6 小时前
Es/Kibana7.17.9中数据迁移到openSearch3.4.0【DockerDesktop模拟】
运维·spring boot·elasticsearch·docker·容器·devops
千寻技术帮6 小时前
10340_基于Springboot的游戏网站
spring boot·后端·游戏·vue·商城
WX-bisheyuange6 小时前
基于SpringBoot的诊疗预约平台
java·spring boot·后端·毕业设计