SpringBoot启动流程:从main方法到应用运行的全过程

在这里插入图片描述

引言

SpringBoot作为当今Java开发生态中最流行的框架之一,极大地简化了Spring应用的初始搭建和开发过程。从简单的几行代码就能启动一个完整的Web服务,到丰富的自动配置特性,这些都使得SpringBoot成为开发者的首选框架。虽然使用SpringBoot非常简单,但了解其启动原理和内部工作机制对于深入掌握框架、排查问题以及进行自定义扩展至关重要。本文将详细解析SpringBoot的启动流程,从main方法的入口开始,到应用完全运行的全过程,帮助开发者理解SpringBoot的启动机制。

一、SpringBoot应用的入口点

1.1 典型的SpringBoot应用入口

SpringBoot应用通常从一个带有main方法的引导类开始。这个类使用@SpringBootApplication注解进行标记,该注解是SpringBoot的核心注解之一,它结合了多个功能性注解。在main方法中,通过调用SpringApplication.run()方法启动应用程序。这个看似简单的调用实际上隐藏了复杂的启动流程。

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * SpringBoot应用程序的入口类
 */
@SpringBootApplication
public class MyApplication {
    
    /**
     * 应用程序入口点
     * @param args 命令行参数
     */
    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}

1.2 @SpringBootApplication注解解析

@SpringBootApplication是一个复合注解,它组合了三个主要的注解:

  1. @SpringBootConfiguration:标记该类为配置类,相当于@Configuration
  2. @EnableAutoConfiguration:启用SpringBoot的自动配置机制
  3. @ComponentScan:启用组件扫描,自动发现并注册Bean

这个复合注解简化了配置,使开发者只需添加一个注解就能够启用SpringBoot的核心功能。

/**
 * @SpringBootApplication注解源码简化版
 */
@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 {
    // 可配置项...
}

二、SpringApplication的创建与配置

2.1 SpringApplication实例化

SpringBoot应用的启动始于创建SpringApplication实例。在调用静态run方法时,首先会创建一个SpringApplication对象,然后再调用这个对象的run方法。在创建过程中,SpringApplication会执行一系列初始化操作,为后续启动做准备。

/**
 * SpringApplication静态run方法内部实现简化版
 */
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
    // 创建SpringApplication实例
    SpringApplication application = new SpringApplication(primarySources);
    // 调用实例的run方法
    return application.run(args);
}

/**
 * SpringApplication构造函数简化版
 */
public SpringApplication(Class<?>... primarySources) {
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    // 推断应用类型(SERVLET、REACTIVE或NONE)
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    // 设置初始化器,从spring.factories加载
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    // 设置监听器,从spring.factories加载
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    // 推断主类(即调用main方法的类)
    this.mainApplicationClass = deduceMainApplicationClass();
}

2.2 初始化器与监听器加载

在SpringApplication的构造过程中,会加载ApplicationContextInitializer和ApplicationListener的实现类。这些类是通过Spring的工厂加载机制从META-INF/spring.factories文件中读取的。初始化器负责在应用上下文刷新前执行初始化逻辑,而监听器则用于响应应用启动过程中的各种事件。

/**
 * 从spring.factories加载实例的方法简化版
 */
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
    // 获取类加载器
    ClassLoader classLoader = getClassLoader();
    // 加载指定类型的工厂实现类名
    Set<String> names = new LinkedHashSet<>(
            SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    // 实例化这些类
    List<T> instances = createSpringFactoriesInstances(type, names, classLoader);
    // 排序
    AnnotationAwareOrderComparator.sort(instances);
    return instances;
}

三、应用启动执行流程

3.1 SpringApplication.run()方法

SpringApplication的run方法是启动流程的核心,它包含了从准备环境到创建和刷新应用上下文的完整流程。整个过程通过SpringApplicationRunListener提供事件通知,使得其他组件可以在特定阶段执行自定义逻辑。

/**
 * SpringApplication.run方法简化版
 */
public ConfigurableApplicationContext run(String... args) {
    // 启动计时器
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    
    // 创建应用上下文
    ConfigurableApplicationContext context = null;
    // 异常报告收集器
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    
    // 配置Headless模式
    configureHeadlessProperty();
    
    // 获取SpringApplicationRunListeners
    SpringApplicationRunListeners listeners = getRunListeners(args);
    // 发布应用启动事件
    listeners.starting();
    
    try {
        // 创建应用参数对象
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        
        // 准备环境
        ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
        configureIgnoreBeanInfo(environment);
        
        // 打印Banner
        Banner printedBanner = printBanner(environment);
        
        // 创建应用上下文
        context = createApplicationContext();
        
        // 获取异常报告器
        exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                new Class[] { ConfigurableApplicationContext.class }, context);
        
        // 准备上下文
        prepareContext(context, environment, listeners, applicationArguments, printedBanner);
        
        // 刷新上下文
        refreshContext(context);
        
        // 刷新后的操作
        afterRefresh(context, applicationArguments);
        
        // 停止计时
        stopWatch.stop();
        
        // 记录启动日志
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
        }
        
        // 发布应用已启动事件
        listeners.started(context);
        
        // 调用ApplicationRunner和CommandLineRunner
        callRunners(context, applicationArguments);
    }
    catch (Throwable ex) {
        // 处理异常
        handleRunFailure(context, ex, exceptionReporters, listeners);
        throw new IllegalStateException(ex);
    }
    
    try {
        // 发布应用运行中事件
        listeners.running(context);
    }
    catch (Throwable ex) {
        // 处理异常
        handleRunFailure(context, ex, exceptionReporters, null);
        throw new IllegalStateException(ex);
    }
    
    // 返回应用上下文
    return context;
}

3.2 环境准备阶段

环境准备是启动流程的重要阶段,在这个阶段,SpringBoot会创建并配置ConfigurableEnvironment实例,加载各种属性源,包括命令行参数、application.properties/application.yml配置文件、环境变量等。这些配置将影响后续的bean创建和自动配置过程。

/**
 * 环境准备方法简化版
 */
private ConfigurableEnvironment prepareEnvironment(
        SpringApplicationRunListeners listeners,
        ApplicationArguments applicationArguments) {
    
    // 创建环境实例
    ConfigurableEnvironment environment = getOrCreateEnvironment();
    
    // 配置环境,添加命令行属性源
    configureEnvironment(environment, applicationArguments.getSourceArgs());
    
    // 发布环境准备事件
    listeners.environmentPrepared(environment);
    
    // 绑定环境到SpringApplication
    bindToSpringApplication(environment);
    
    // 根据需要转换环境类型
    if (!this.isCustomEnvironment) {
        environment = new EnvironmentConverter(getClassLoader())
                .convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
    }
    
    // 添加属性源包装器
    ConfigurationPropertySources.attach(environment);
    
    return environment;
}

3.3 应用上下文创建阶段

根据应用类型(Servlet、Reactive或Standard),SpringBoot会创建不同类型的ApplicationContext实例。常见的有AnnotationConfigServletWebServerApplicationContext(用于Servlet Web应用)和AnnotationConfigApplicationContext(用于非Web应用)。

/**
 * 创建应用上下文方法简化版
 */
protected ConfigurableApplicationContext createApplicationContext() {
    Class<?> contextClass = this.applicationContextClass;
    if (contextClass == null) {
        try {
            // 根据应用类型选择上下文类
            switch (this.webApplicationType) {
                case SERVLET:
                    contextClass = Class.forName(
                            "org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext");
                    break;
                case REACTIVE:
                    contextClass = Class.forName(
                            "org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext");
                    break;
                default:
                    contextClass = Class.forName(
                            "org.springframework.context.annotation.AnnotationConfigApplicationContext");
            }
        }
        catch (ClassNotFoundException ex) {
            throw new IllegalStateException(
                    "Unable to create a default ApplicationContext, please specify an ApplicationContextClass", ex);
        }
    }
    
    // 实例化上下文
    return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}

3.4 应用上下文准备阶段

在这个阶段,SpringBoot会为ApplicationContext设置环境,注册BeanNameGenerator和ResourceLoader,应用所有的ApplicationContextInitializer,并将启动类注册为Bean。还会发布上下文准备事件,使监听器能够在上下文刷新前执行自定义逻辑。

/**
 * 准备上下文方法简化版
 */
private void prepareContext(
        ConfigurableApplicationContext context,
        ConfigurableEnvironment environment,
        SpringApplicationRunListeners listeners,
        ApplicationArguments applicationArguments,
        Banner printedBanner) {
    
    // 设置环境
    context.setEnvironment(environment);
    
    // 应用上下文后处理
    postProcessApplicationContext(context);
    
    // 应用初始化器
    applyInitializers(context);
    
    // 发布上下文准备事件
    listeners.contextPrepared(context);
    
    // 加载启动类为Bean
    load(context, sources.toArray(new Object[0]));
    
    // 发布上下文加载事件
    listeners.contextLoaded(context);
}

四、应用上下文刷新阶段

4.1 应用上下文刷新过程

上下文刷新是Spring应用启动的核心部分,这个过程继承自Spring框架,包括Bean定义的加载和解析、BeanFactoryPostProcessor的调用、Bean的实例化和初始化等。在Web应用中,还会启动内嵌的Web服务器。

/**
 * 刷新上下文方法简化版
 */
private void refreshContext(ConfigurableApplicationContext context) {
    // 刷新上下文
    refresh(context);
    
    // 注册关闭钩子
    if (this.registerShutdownHook) {
        try {
            context.registerShutdownHook();
        }
        catch (AccessControlException ex) {
            // 忽略AccessControlException
        }
    }
}

/**
 * 刷新方法简化版
 */
protected void refresh(ApplicationContext applicationContext) {
    Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
    ((AbstractApplicationContext) applicationContext).refresh();
}

4.2 Bean工厂准备阶段

在刷新过程中,首先会准备Bean工厂,这包括设置类加载器、表达式解析器和属性编辑器注册器等。然后调用BeanFactoryPostProcessor来修改Bean定义信息,这是Spring框架的扩展点之一,也是SpringBoot实现自动配置的关键环节。

/**
 * Bean工厂准备方法(Spring框架中AbstractApplicationContext的部分代码)
 */
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    // 设置类加载器
    beanFactory.setBeanClassLoader(getClassLoader());
    
    // 设置表达式解析器
    beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
    
    // 添加属性编辑器注册器
    beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
    
    // 添加BeanPostProcessor
    beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
    
    // 忽略特定接口的自动装配
    beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
    beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
    beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
    beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
    beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
    beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
    
    // 注册特殊依赖
    beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
    beanFactory.registerResolvableDependency(ResourceLoader.class, this);
    beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
    beanFactory.registerResolvableDependency(ApplicationContext.class, this);
    
    // 注册环境Bean
    if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
        beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
    }
    
    // 注册系统属性Bean
    if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
        beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
    }
    
    // 注册系统环境Bean
    if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
        beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
    }
}

4.3 自动配置过程

SpringBoot的自动配置是在BeanFactoryPostProcessor阶段执行的,主要由ConfigurationClassPostProcessor负责处理@Configuration注解的类,包括处理@ComponentScan、@Import和@Bean等注解。在这个过程中,SpringBoot会根据条件注解筛选合适的自动配置类,并将它们注册为Bean。

/**
 * 自动配置过程中的条件评估(简化版)
 */
public class OnClassCondition extends SpringBootCondition {
    
    @Override
    public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
        MultiValueMap<String, Object> attributes = metadata.getAllAnnotationAttributes(
                ConditionalOnClass.class.getName(), true);
        
        if (attributes != null) {
            for (Object values : attributes.get("value")) {
                for (Object value : (Object[]) values) {
                    String className = ((Class<?>) value).getName();
                    // 检查类是否存在
                    if (!isPresent(className, context.getClassLoader())) {
                        return ConditionOutcome.noMatch("Class " + className + " not found");
                    }
                }
            }
        }
        
        return ConditionOutcome.match();
    }
    
    private boolean isPresent(String className, ClassLoader classLoader) {
        try {
            Class.forName(className, false, classLoader);
            return true;
        }
        catch (Throwable ex) {
            return false;
        }
    }
}

五、Web服务器启动阶段

5.1 内嵌Web服务器的创建

对于Web应用,SpringBoot会在刷新上下文的过程中创建并启动内嵌的Web服务器。这个过程主要由ServletWebServerFactory负责,它会根据应用的配置创建相应的Web服务器实例,如Tomcat、Jetty或Undertow。

/**
 * Web服务器创建过程(简化版)
 */
public class ServletWebServerApplicationContext extends GenericWebApplicationContext {
    
    private WebServer webServer;
    
    /**
     * 创建并启动Web服务器
     */
    private void createWebServer() {
        // 获取ServletWebServerFactory
        ServletWebServerFactory factory = getWebServerFactory();
        
        // 创建Web服务器
        this.webServer = factory.getWebServer(getSelfInitializer());
    }
    
    /**
     * 启动Web服务器
     */
    @Override
    protected void onRefresh() {
        super.onRefresh();
        try {
            createWebServer();
        }
        catch (Throwable ex) {
            throw new ApplicationContextException("Unable to start web server", ex);
        }
    }
    
    /**
     * 获取ServletWebServerFactory
     */
    protected ServletWebServerFactory getWebServerFactory() {
        // 查找容器中的ServletWebServerFactory Bean
        String[] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class);
        
        if (beanNames.length == 0) {
            throw new ApplicationContextException("No ServletWebServerFactory beans found");
        }
        
        if (beanNames.length > 1) {
            throw new ApplicationContextException(
                    "Multiple ServletWebServerFactory beans found: " + StringUtils.arrayToCommaDelimitedString(beanNames));
        }
        
        return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
    }
}

5.2 Web应用的初始化

在Web服务器启动后,SpringBoot会初始化Servlet容器,注册DispatcherServlet、Filters和Listeners等。这个过程主要由ServletWebServerApplicationContext和ServletWebServerFactory协作完成。

/**
 * Servlet容器初始化过程(简化版)
 */
public interface ServletContextInitializer {
    
    /**
     * 初始化Servlet上下文
     */
    void onStartup(ServletContext servletContext) throws ServletException;
}

/**
 * DispatcherServlet注册(简化版)
 */
public class DispatcherServletRegistrationBean extends ServletRegistrationBean<DispatcherServlet> {
    
    /**
     * 构造函数
     */
    public DispatcherServletRegistrationBean(DispatcherServlet servlet, String... urlMappings) {
        super(servlet, urlMappings);
    }
    
    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        // 注册Servlet
        String name = getServletName();
        ServletRegistration.Dynamic registration = servletContext.addServlet(name, getServlet());
        
        // 设置初始化参数
        registration.setInitParameters(getInitParameters());
        
        // 设置加载顺序
        registration.setLoadOnStartup(getLoadOnStartup());
        
        // 设置URL映射
        registration.addMapping(getUrlMappings().toArray(new String[0]));
    }
}

六、应用启动完成阶段

6.1 Runner的执行

SpringBoot提供了两种接口,ApplicationRunner和CommandLineRunner,它们都会在应用启动完成后执行。开发者可以实现这些接口来执行一些初始化逻辑或启动任务。ApplicationRunner接受ApplicationArguments参数,而CommandLineRunner接受原始的命令行参数数组。

/**
 * 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());
    
    // 排序
    AnnotationAwareOrderComparator.sort(runners);
    
    // 执行
    for (Object runner : new LinkedHashSet<>(runners)) {
        if (runner instanceof ApplicationRunner) {
            ((ApplicationRunner) runner).run(args);
        }
        if (runner instanceof CommandLineRunner) {
            ((CommandLineRunner) runner).run(args.getSourceArgs());
        }
    }
}

6.2 应用启动完成事件发布

在所有初始化工作完成后,SpringBoot会发布ApplicationReadyEvent事件,表示应用已准备就绪,可以开始处理请求。监听这个事件的组件可以在应用完全启动后执行一些操作,如缓存预热、数据加载等。

/**
 * 应用启动完成事件发布(简化版)
 */
public void started(ConfigurableApplicationContext context) {
    // 发布ApplicationStartedEvent事件
    context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context));
    
    // 计算启动时间
    AvailabilityChangeEvent.publish(context, LivenessState.CORRECT);
    
    // 发布ApplicationReadyEvent事件
    context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context));
    
    // 标记应用可用
    AvailabilityChangeEvent.publish(context, ReadinessState.ACCEPTING_TRAFFIC);
}

七、SpringBoot启动流程监控与调试

7.1 启动时间分析

SpringBoot提供了启动时间分析功能,可以通过配置查看应用启动过程中各阶段的耗时情况,有助于识别启动慢的环节并进行优化。

# application.properties中开启启动时间分析
debug=true
spring.main.log-startup-info=true

7.2 启动过程自定义

开发者可以通过多种方式自定义SpringBoot的启动过程,如实现ApplicationContextInitializer、ApplicationListener、CommandLineRunner等接口,或者编写自定义的BeanFactoryPostProcessor和BeanPostProcessor。

/**
 * 自定义ApplicationRunner示例
 */
@Component
public class MyApplicationRunner implements ApplicationRunner {
    
    private static final Logger logger = LoggerFactory.getLogger(MyApplicationRunner.class);
    
    @Override
    public void run(ApplicationArguments args) throws Exception {
        logger.info("应用启动完成,执行初始化操作...");
        
        // 执行初始化逻辑
        
        logger.info("初始化操作完成");
    }
}

总结

SpringBoot的启动流程是一个复杂而精密的过程,从main方法的简单调用开始,经历了环境准备、上下文创建、Bean定义加载、上下文刷新、Web服务器启动等多个阶段,最终完成应用的初始化和启动。了解这一过程不仅有助于排查启动问题,还能帮助开发者更好地利用SpringBoot提供的扩展点进行自定义开发。

SpringBoot框架的设计理念是约定优于配置,通过自动配置和合理的默认值,大大简化了应用开发。但作为开发者,理解其内部工作机制能够让我们更加灵活地使用这个强大的框架,在保持高效开发的同时,能够根据实际需要进行精细调整和优化。在SpringBoot的实际应用中,可以充分利用其提供的监控和扩展机制,构建更加健壮和高性能的应用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值