SpringBoot启动原理

本文详细解析了SpringBoot项目的启动过程,从启动类创建到容器刷新的各个环节,并介绍了关键组件的作用。

# SpringBoot启动原理 #
1.创建SpringBoot时默认生成一个启动类

    @SpringBootApplication
    public class SpringBootWebApplication {
        public static void main(String[] args) {
            SpringApplication.run(SpringBootWebApplication.class, args);
        }
    }

2.启动时调用SpringApplication的run方法

    public static ConfigurableApplicationContext run(Class<?> primarySource,
            String... args) {
        //调用SpringApplication类中的重载run方法
        return run(new Class<?>[] { primarySource }, args);
    }

    public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
        return new SpringApplication(primarySources).run(args);
    }

3.此时调用两个方法:先创建一个SpringApplication对象,运行run方法

    //创建SpringApplication对象,参数为Class<?>[SpringBootWebApplication.class]
    new SpringApplication(primarySources);
    //运行run方法
    run(args);

4.创建SpringApplication对象时执行下面代码

    public SpringApplication(Class<?>... primarySources) {
        //调用重载构造方法
        this(null, primarySources);
    }

    public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        //resourceLoader为null
        this.resourceLoader = resourceLoader;

        //primarySources为Class<?>[SpringBootWebApplication.class]
        Assert.notNull(primarySources, "PrimarySources must not be null");

        //此时primarySources由数组变为LinkedHashSet集合
        this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));

        //4.1判断应用类型
        this.webApplicationType = WebApplicationType.deduceFromClasspath();

        //4.2从类路径下找到META‐INF/spring.factories配置的所有ApplicationContextInitializer;然后保存起来
        setInitializers((Collection) getSpringFactoriesInstances(
                ApplicationContextInitializer.class));

        //4.3从类路径下找到ETA‐INF/spring.factories配置的所有ApplicationListener
        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
        //4.4从多个配置类中找到有main方法的主配置类
        this.mainApplicationClass = deduceMainApplicationClass();
    }

4.1.调用WebApplicationType.deduceFromClasspath()方法判断应用类型

    //判断当前应用是普通web应用、响应式web应用还是非web应用
    static WebApplicationType deduceFromClasspath() {
        //isPresent方法判断所提供的类名的类是否存在,且可以被加载
        if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null)
                && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
                && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
            //应用是响应式web应用
            return WebApplicationType.REACTIVE;
        }
        for (String className : SERVLET_INDICATOR_CLASSES) {
            if (!ClassUtils.isPresent(className, null)) {
                //非web应用
                return WebApplicationType.NONE;
            }
        }
        //普通web应用
        return WebApplicationType.SERVLET;
    }

4.2.从类路径下找到META‐INF/spring.factories配置的所有ApplicationContextInitializer;然后保存起来

    setInitializers(
        //4.2.1获取Spring工厂实例
        (Collection) getSpringFactoriesInstances(
                ApplicationContextInitializer.class));

4.2.1.获取Spring工厂实例

    getSpringFactoriesInstances(
                ApplicationContextInitializer.class)
    
    //此处type参数为ApplicationContextInitializer.class
    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();
        //4.2.1.1使用LinkedHashSet存放加载的工厂名称防止重复
        Set<String> names = new LinkedHashSet<>(
                SpringFactoriesLoader.loadFactoryNames(type, classLoader));
        //4.2.1.2创建Spring工厂实例
        List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
                classLoader, args, names);
        //对实例进行排序
        AnnotationAwareOrderComparator.sort(instances);
        return instances;
    }

4.2.1.1使用LinkedHashSet存放加载的工厂名称防止重复

    Set<String> names = new LinkedHashSet<>(
                SpringFactoriesLoader.loadFactoryNames(type, classLoader));

    //加载工厂名称,此处factoryClass参数为ApplicationContextInitializer.class
    public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
        //此时factoryClassName为org.springframework.context.ApplicationContextInitializer
        String factoryClassName = factoryClass.getName();

        //加载Spring工厂,并从中获取key为org.springframework.context.ApplicationContextInitializer的value.此时值为:
        //org.springframework.context.ApplicationContextInitializer=\
        //org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
        //org.springframework.boot.context.ContextIdApplicationContextInitializer,\
        //org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
        //org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
        return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
    }

    //加载Spring工厂
    private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
        //第一次调用该方法,此时cache只是一个空集合
        MultiValueMap<String, String> result = cache.get(classLoader);
        if (result != null) {
            return result;
        }

        try {
            //加载所有的/META-INF/spring.factories文件
            Enumeration<URL> urls = (classLoader != null ?
                    classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
                    ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
            result = new LinkedMultiValueMap<>();
            while (urls.hasMoreElements()) {
                URL url = urls.nextElement();
                UrlResource resource = new UrlResource(url);
                
                //将spring.factories文件转为properties文件
                Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                for (Map.Entry<?, ?> entry : properties.entrySet()) {
                    String factoryClassName = ((String) entry.getKey()).trim();
                    for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
                        result.add(factoryClassName, factoryName.trim());
                    }
                }
            }

            //将所有的spring.factories文件中的数据存入cache中
            cache.put(classLoader, result);
            return result;
        }
        catch (IOException ex) {
            throw new IllegalArgumentException("Unable to load factories from location [" +
                    FACTORIES_RESOURCE_LOCATION + "]", ex);
        }
    }

4.2.1.2创建Spring工厂实例
    
    //创建Spring工厂实例
    List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
                classLoader, args, names);

    //遍历传入的names,对其进行实例化,放入instances中返回
    private <T> List<T> createSpringFactoriesInstances(Class<T> type,
            Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args,
            Set<String> names) {
        List<T> instances = new ArrayList<>(names.size());
        for (String name : names) {
            try {
                //根据name和classLoader获取Class对象
                Class<?> instanceClass = ClassUtils.forName(name, classLoader);
                Assert.isAssignable(type, instanceClass);

                //获取指定参数类型的所有构造器,包括public的和非public的
                Constructor<?> constructor = instanceClass
                        .getDeclaredConstructor(parameterTypes);

                //创建实例化对象
                T instance = (T) BeanUtils.instantiateClass(constructor, args);
                
                //加如instances中
                instances.add(instance);
            }
            catch (Throwable ex) {
                throw new IllegalArgumentException(
                        "Cannot instantiate " + type + " : " + name, ex);
            }
        }
        return instances;
    }
4.3从类路径下找到ETA‐INF/spring.factories配置的所有ApplicationListener和4.2流程类似

5.运行run方法

    /**
     * 运行spring应用,创建并刷新新的spring容器
     */
    public ConfigurableApplicationContext run(String... args) {
        //首先创建开始停止的监听
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
        //与awt应用有关的配置
        configureHeadlessProperty();
        //5.1从类路径下找到META‐INF/spring.factories获取SpringApplicationRunListeners
        SpringApplicationRunListeners listeners = getRunListeners(args);
        //回调所有的SpringApplicationRunListeners的starting方法
        listeners.starting();
        try {
            //封装命令行参数
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                    args);
            //5.2准备环境
            ConfigurableEnvironment environment = prepareEnvironment(listeners,
                    applicationArguments);
            configureIgnoreBeanInfo(environment);
            //打印banner图标
            Banner printedBanner = printBanner(environment);
            //创建ioc容器,根据环境不同创建不同的ioc容器
            context = createApplicationContext();
            //出现异常用来查看异常报告
            exceptionReporters = getSpringFactoriesInstances(
                    SpringBootExceptionReporter.class,
                    new Class[] { ConfigurableApplicationContext.class }, context);
            //5.3准备上下文环境
            prepareContext(context, environment, listeners, applicationArguments,
                    printedBanner);
            //5.4刷新容器,ioc容器初始化流程
            refreshContext(context);
            //5.5从ioc容器中获取ApplicationRunner和CommandLineRunner并进行回调
            afterRefresh(context, applicationArguments);
            stopWatch.stop();
            if (this.logStartupInfo) {
                new StartupInfoLogger(this.mainApplicationClass)
                        .logStarted(getApplicationLog(), stopWatch);
            }
            listeners.started(context);
            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);
        }
        //springboot启动完成返回ioic容器
        return context;
    }

5.2准备环境

    private ConfigurableEnvironment prepareEnvironment(
            SpringApplicationRunListeners listeners,
            ApplicationArguments applicationArguments) {
        // 获取或创建环境
        ConfigurableEnvironment environment = getOrCreateEnvironment();
        //配置环境
        configureEnvironment(environment, applicationArguments.getSourceArgs());
        //回调所有的SpringApplicationRunListeners的environmentPrepared方法,表示环境准备完成
        listeners.environmentPrepared(environment);
        //将环境绑定到spring应用中
        bindToSpringApplication(environment);
        if (!this.isCustomEnvironment) {
            environment = new EnvironmentConverter(getClassLoader())
                    .convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
        }
        ConfigurationPropertySources.attach(environment);
        return environment;
    }

5.3准备上下文环境

    private void prepareContext(ConfigurableApplicationContext context,
            ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
            ApplicationArguments applicationArguments, Banner printedBanner) {
        //设置环境到ioc容器中
        context.setEnvironment(environment);
        //使用后置处理器给ioc容器注册bean
        postProcessApplicationContext(context);
        //回调4.2步骤中获取的所有ApplicationContextInitializer的initialize方法
        applyInitializers(context);
        //回调5.1步骤中获取的所有SpringApplicationRunListener的contextPrepared方法
        listeners.contextPrepared(context);
        //记录日志
        if (this.logStartupInfo) {
            logStartupInfo(context.getParent() == null);
            logStartupProfileInfo(context);
        }
        // 注册命令行参数
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
        if (printedBanner != null) {
            //将banner注册进来
            beanFactory.registerSingleton("springBootBanner", printedBanner);
        }
        if (beanFactory instanceof DefaultListableBeanFactory) {
            ((DefaultListableBeanFactory) beanFactory)
                    .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
        }
        // 获取主类
        Set<Object> sources = getAllSources();
        Assert.notEmpty(sources, "Sources must not be empty");
        load(context, sources.toArray(new Object[0]));
        //回调5.1步骤中获取的所有SpringApplicationRunListener的contextLoaded方法
        listeners.contextLoaded(context);
    }

5.5从ioc容器中获取ApplicationRunner和CommandLineRunner并进行回调,其中ApplicationRunner优先级高于CommandLineRunner

    protected void afterRefresh(ConfigurableApplicationContext context,
            ApplicationArguments args) {
    }

    private void callRunners(ApplicationContext context, ApplicationArguments args) {
        List<Object> runners = new ArrayList<>();
        runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
        runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
        AnnotationAwareOrderComparator.sort(runners);
        for (Object runner : new LinkedHashSet<>(runners)) {
            if (runner instanceof ApplicationRunner) {
                callRunner((ApplicationRunner) runner, args);
            }
            if (runner instanceof CommandLineRunner) {
                callRunner((CommandLineRunner) runner, args);
            }
        }
    }

### Spring Boot 应用的启动原理深入解析 Spring Boot 的启动过程是一个高度自动化的流程,它通过一系列标准化和约定优于配置的理念,极大地简化了基于 Spring 框架的应用程序开发。整个启动过程可以分为几个关键阶段:应用入口、上下文准备、自动配置、内嵌容器初始化以及扩展点执行。 #### 1. 应用入口与 SpringApplication 初始化 Spring Boot 应用通常从一个带有 `main` 方法的类开始,该类使用 `@SpringBootApplication` 注解。这个注解是组合注解,包含了 `@ComponentScan`、`@EnableAutoConfiguration` 和 `@SpringBootConfiguration`,分别用于组件扫描、启用自动配置和声明当前类为配置类[^3]。 在 `main` 方法中调用 `SpringApplication.run()` 启动应用,此时会创建 `SpringApplication` 实例并进行初始化。这个过程中会加载一些基础配置,如设置 `ApplicationContextInitializer` 和 `ApplicationListener` 等。 ```java public static void main(String[] args) { SpringApplication app = new SpringApplication(MyApplication.class); app.run(args); } ``` #### 2. 上下文准备与刷新 当 `run()` 方法被调用后,Spring Boot 开始准备 `ApplicationContext` 并刷新上下文。这一步骤涉及多个核心操作: - **环境准备**:根据运行环境(如 Web 环境或非 Web 环境)创建对应的 `Environment` 对象。 - **事件发布机制初始化**:注册监听器以支持生命周期事件的发布。 - **上下文刷新**:调用 `refresh()` 方法触发 Spring 容器的核心功能,包括 BeanFactory 的初始化、Bean 的定义加载、自动装配等[^2]。 #### 3. 自动配置机制 Spring Boot 的自动配置机制是其最核心的功能之一。它通过 `spring-boot-autoconfigure` 模块提供的大量自动配置类来实现。这些类通常位于 `META-INF/spring.factories` 文件中,并由 `@EnableAutoConfiguration` 触发加载。 自动配置类使用条件注解(如 `@ConditionalOnClass`、`@ConditionalOnMissingBean`)来决定是否生效。例如,只有在类路径中存在 `DataSource` 类时,才会尝试配置数据源相关的 Bean。 ```java @Configuration @ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class }) @EnableConfigurationProperties(DataSourceProperties.class) public class DataSourceAutoConfiguration { // 配置内容 } ``` #### 4. 内嵌容器初始化 对于 Web 应用,Spring Boot 默认使用 Tomcat、Jetty 或 Undertow 作为内嵌服务器。在上下文刷新完成后,会触发 `WebServerInitializedEvent` 事件,并启动内嵌服务器。这一过程发生在 `refreshContext()` 中的 `onRefresh()` 方法里。 具体来说,`ServletWebServerApplicationContext` 会负责创建并启动内嵌的 Web 服务器实例,绑定到指定端口并准备好接收 HTTP 请求[^2]。 #### 5. 扩展点执行 在整个启动过程中,Spring Boot 提供了多个扩展点供开发者自定义行为。常见的扩展点包括: - `ApplicationRunner` 和 `CommandLineRunner`:在应用启动完成后执行特定逻辑。 - `SmartInitializingSingleton`:在所有单例 Bean 初始化完成后执行。 - `BeanFactoryPostProcessor` 和 `BeanPostProcessor`:用于修改 Bean 定义或实例。 - `ApplicationListener`:监听并响应应用事件。 这些扩展点允许开发者在不破坏框架结构的前提下,灵活地定制应用的行为。 #### 6. 日志输出与调试 为了更好地理解启动过程,可以通过日志输出查看每个步骤的详细信息。Spring Boot 支持多种日志框架(如 Logback、Log4j2),并且可以通过 `application.properties` 或 `application.yml` 文件控制日志级别和格式。 此外,还可以通过 `SpringApplication.setWebApplicationType()` 设置应用类型(如 `WebApplicationType.NONE` 表示非 Web 应用),进一步定制启动行为[^1]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值