SpringBoot启动流程分析1--SpringApplication实例化

SpringBoot启动流程解析:SpringApplication实例化与自动配置
本文详细分析了SpringBoot2.7.x启动流程中的第一个阶段——SpringApplication实例化过程,涉及primarySources的设置、应用类型推断、初始化器和监听器加载,以及mainApplicationClass的推断。

SpringBoot启动流程分析1–SpringApplication实例化

一、概述

源码基于SpringBoot 2.7.xx版本

1.1 简介

SpringBoot启动流程大致分为两个阶段,第一个阶段是SpringApplication 实例化,第二个阶段为执行 run 方法,本章关注第一阶段

第一个阶段:

  1. 设置primarySources属性
  2. 推断应用类型–WebApplicationType
  3. 加载Spring应用上下文初始化器–ApplicationContextInitializer
  4. 加载Spring应用事件监听器–ApplicationListener
  5. 推断应用引导类–mainApplicationClass

第二阶段:

  1. 初始化引导上下文–bootStrapContext
  2. 创建应用上下文环境–ConfigurableEnvironment
  3. 创建应用上下文–createApplicationContext
  4. 准备应用上下文–prepareContext
  5. 刷新应用上下文–refreshContext
  6. 刷新应用上下文后的扩展接口–afterRefresh(空方法)
  7. 调用ApplicationRunner和CommandLineRunner对应的run方法

1.2 名词解释

  • Instantiation
    实例化,是加载class文件,并在内存中开辟一块内容空间,创建一个属性值为默认值的对象
  • Initialization
    初始化,初始化是对对象的属性进行赋值即属性填充,是在Bean实例化完成后执行的。

二、详解

2.1 mian方法

// 能够扫描Spring组件并自动配置Spring Boot
@SpringBootApplication
public class SpringbootApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringbootApplication.class, args);
    }
}

在这个类中需要关注的是

  • @SpringBootApplication
  • SpringApplication.run()

关于 @SpringBootApplication 注解,在后面SpringBoot启动流程分析知识点–自动装配的章节会展开去分析。

2.2 SpringApplication的实例化

SpringBoot启动的时候,会创建一个SpringApplication的实例,实例化的时候会做以下几件事:

  1. 把primarySources(启动类Class对象)设置到SpringApplication的primarySources属性中,后续prepareContext方法会根据该属性加载启动类,
    并将启动类注入到容器,后续会根据该类进行包扫描;
  2. 推断应用类型,后面会根据类型初始化对应的环境,一般为WebApplicationType.SERVLET类型;
  3. 实例化classpath下 META-INF/spring.factories中已配置的 ApplicationContextInitializer;
  4. 实例化classpath下 META-INF/spring.factories中已配置的 ApplicationListener;
  5. 根据调用栈,推断出 main 方法的类名,用于发布事件,打印banner。
public class SpringApplication {

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

    // 两件事:1.实例化SpringApplication 2.执行run方法
    public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
        new SpringApplication(primarySources);
        return run(args);
    }

    public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        // resourceLoader参数默认为null
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        // 1.把primarySources设置到SpringApplication的primarySources属性中--启动类Class对象
        this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
        // 2.推断应用类型,后面会根据类型初始化对应的环境,一般为WebApplicationType.SERVLET类型
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        this.bootstrapRegistryInitializers = new ArrayList<>(
                getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
        // 3.实例化classpath下 META-INF/spring.factories中已配置的 ApplicationContextInitializer
        setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
        // 4.实例化classpath下 META-INF/spring.factories中已配置的 ApplicationListener
        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
        // 5.根据调用栈,推断出 main 方法的类名
        this.mainApplicationClass = deduceMainApplicationClass();
    }
}

2.3 推断应用类型

具体的判断逻辑如下:

  1. WebApplicationType.REACTIVE
    classpath下 存在org.springframework.web.reactive.DispatcherHandler,
    并且不存在org.springframework.web.servlet.DispatcherServlet和org.glassfish.jersey.servlet.ServletContainer;
  2. WebApplicationType.SERVLET
    classpath下 存在javax.servlet.Servlet和org.springframework.web.context.ConfigurableWebApplicationContext;
  3. WebApplicationType.NONE
    不满足以上条件。
public enum WebApplicationType {

    NONE,
    SERVLET,
    REACTIVE;

    private static final String[] SERVLET_INDICATOR_CLASSES = {"javax.servlet.Servlet",
            "org.springframework.web.context.ConfigurableWebApplicationContext"};
    private static final String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet";
    private static final String WEBFLUX_INDICATOR_CLASS = "org.springframework.web.reactive.DispatcherHandler";
    private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";

    /**
     * 判断 应用的类型
     * NONE: 应用程序不是web应用,也不应该用web服务器去启动
     * SERVLET: 应用程序应作为基于servlet的web应用程序运行,并应启动嵌入式servlet web(tomcat)服务器。
     * REACTIVE: 应用程序应作为 reactive web应用程序运行,并应启动嵌入式 reactive web服务器。
     * @return
     */
    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;
            }
        }
        // classpath下 存在javax.servlet.Servlet和org.springframework.web.context.ConfigurableWebApplicationContext
        return WebApplicationType.SERVLET;
    }
}

2.4 加载Spring应用上下文初始化器

实例化classpath下 META-INF/spring.factories中已配置的 ApplicationContextInitializer。

public class SpringApplication {

    public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        // ……
        setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
        // ……
    }
    private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
        return getSpringFactoriesInstances(type, new Class<?>[]{});
    }

    /**
     * 从META-INF/spring.factories获取指定的Spring的工厂实例
     */
    private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
        ClassLoader classLoader = getClassLoader();
        // Use names and ensure unique to protect against duplicates
        // 通过指定的classLoader从 META-INF/spring.factories 的资源文件中读取 key 为 type.getName() 的 value
        // type为接口,value为逗号分割的实现类
        Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
        // 创建Spring工厂实例,实际上就是type接口的实例对象
        List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
        // 对Spring工厂实例排序(org.springframework.core.annotation.Order注解指定的顺序)
        AnnotationAwareOrderComparator.sort(instances);
        return 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 {
                Class<?> instanceClass = ClassUtils.forName(name, classLoader);
                Assert.isAssignable(type, instanceClass);
                // 根据类名及其参数类型使用反射进行创建对象
                Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
                T instance = (T) BeanUtils.instantiateClass(constructor, args);
                instances.add(instance);
            } catch (Throwable ex) {
                throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
            }
        }
        return instances;
    }
}

2.4.1 loadFctoryNames()方法

loadFctoryNames(type,classLoader)方法就是从spring.factories中获取类型名称(其实就是一接口及其实现列表)

ApplicationContextInitializer 是Spring框架的类, 这个类的主要目的就是在 ConfigurableApplicationContext 调用refresh()方法之前回调这个类的initialize方法。允许我们对ConfiurableApplicationContext的实例做进一步的设置和处理。例如:通过 ConfigurableApplicationContext 的实例获取容器的环境Environment,从而实现对配置文件的修改完善等工作。

spring-boot中ApplicationContextInitializer的实现如下:

  1. spring-boot
    • DelegatingApplicationContextInitializer
    • ContextIdApplicationContextInitializer
    • ConfigurationWarningsApplicationContextInitializer
    • RSocketPortInfoApplicationContextInitializer
    • ServerPortInfoApplicationContextInitializer
  2. spring-boot-autoconfigure
    • SharedMetadataReaderFactoryContextInitializer
    • ConditionEvaluationReportLoggingListener

如果pom有依赖其他jar中存在spring.factories,则会一同加载进来,并缓存到cache中。

public final class SpringFactoriesLoader {
    public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
    static final Map<ClassLoader, Map<String, List<String>>> cache = new ConcurrentReferenceHashMap<>();

    public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
        ClassLoader classLoaderToUse = classLoader;
        if (classLoaderToUse == null) {
            classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
        }
        String factoryTypeName = factoryType.getName();
        // 在这里进行返回的时候会进行loadFactories
        return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
    }

    private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
        Map<String, List<String>> result = cache.get(classLoader);
        if (result != null) {
            return result;
        }

        result = new HashMap<>();
        try {
            Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
            while (urls.hasMoreElements()) {
                URL url = urls.nextElement();
                UrlResource resource = new UrlResource(url);
                Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                for (Map.Entry<?, ?> entry : properties.entrySet()) {
                    String factoryTypeName = ((String) entry.getKey()).trim();
                    String[] factoryImplementationNames =
                            StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
                    for (String factoryImplementationName : factoryImplementationNames) {
                        result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
                                .add(factoryImplementationName.trim());
                    }
                }
            }

            // Replace all lists with unmodifiable lists containing unique elements
            result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
                    .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
            cache.put(classLoader, result);
        } catch (IOException ex) {
            throw new IllegalArgumentException("Unable to load factories from location [" +
                    FACTORIES_RESOURCE_LOCATION + "]", ex);
        }
        return result;
    }
}

部分ApplicationContextInitializer作用:

  1. spring-boot
    • DelegatingApplicationContextInitializer:委托(代理)initializer,其核心功能是通过环境变量context.initializer.classes(类的全限定名,多个用,分割)动态加载自定义初始化器,若未指定,则不执行任何操作。这个初始化器的优先级是Spring Boot定义初始化器中优先级别最高的,因此会被第一个执行
    • ContextIdApplicationContextInitializer:给ApplicationContext设置一个ID,即spring.application.name,未设置默认为application
    • ConfigurationWarningsApplicationContextInitializer: 加载ConfigurationWarningsPostProcessor 处理器
    • RSocketPortInfoApplicationContextInitializer
    • ServerPortInfoApplicationContextInitializer:添加监听器, 当服务器启动时(WebServerInitializedEvent事件)将内嵌的Web服务器使用的端口添加进 spring 容器
  2. spring-boot-autoconfigure
    • SharedMetadataReaderFactoryContextInitializer:加载CachingMetadataReaderFactoryPostProcessor 处理器
    • ConditionEvaluationReportLoggingListener:添加监听器,监听ContextRefreshedEvent和ApplicationFailedEvent事件,通过日志输出条件评估报告(如自动配置类加载情况、条件匹配结果等),便于开发者理解应用启动时的配置逻辑。

2.5 加载Spring应用事件监听器

实例化classpath下 META-INF/spring.factories中已配置的 ApplicationListener。

ApplicationListener 的加载过程和上面的 ApplicationContextInitializer 类的加载过程是一样的。

spring-boot中ApplicationListener的实现如下:

  1. spring-boot
    • EnvironmentPostProcessorApplicationListener:主要监听ApplicationEnvironmentPreparedEvent事件,主要用于在Spring应用环境准备阶段加载环境后置处理器–EnvironmentPostProcessor,并按照优先级排序后调用它们的 postProcessEnvironment 方法。该方法允许开发者动态修改环境配置(如添加属性源、调整属性优先级等)。
    • AnsiOutputApplicationListener
    • LoggingApplicationListener
    • DelegatingApplicationListener
    • ParentContextCloserApplicationListener
    • ClearCachesApplicationListener
    • FileEncodingApplicationListener
  2. spring-boot-autoconfigure
    • BackgroundPreinitializer
      在 spring.factories 配置文件中默认定义的 EnvironmentPostProcessor 的实现类(即EnvironmentPostProcessorApplicationListener触发监听事件后调用):
  • RandomValuePropertySourceEnvironmentPostProcessor:用于动态生成随机值(如字符串、数值、UUID等)并将其注入到环境配置中的组件。
  • SystemEnvironmentPropertySourceEnvironmentPostProcessor:将环境中已保存的操作系统环境变量相关的属性进行替换,将原本保存环境变量的SystemEnvironmentPropertySource实例替换成其子类OriginAwareSystemEnvironmentPropertySource,两者的协作关系是:后置处理器在环境初始化阶段完成替换,而增强后的属性源在运行时提供Origin查询能力。
  • SpringApplicationJsonEnvironmentPostProcessor:解析 spring.application.json 或 SPRING_APPLICATION_JSON 配置的 json 字符串。
  • CloudFoundryVcapEnvironmentPostProcessor:是 Spring Boot 中一个专门用于处理 Cloud Foundry 环境(一个开源 PaaS 云平台)的 EnvironmentPostProcessor。 它的主要作用是从 Cloud Foundry 提供的 VCAP_APPLICATION 和 VCAP_SERVICES 环境变量中读取配置信息,并将这些信息作为属性源添加到 Spring Boot 的 Environment 中。
  • ConfigDataEnvironmentPostProcessor:配置文件解析application.yml,以及application-dev.yml等,每个yml生成一个配置源(替代旧版ConfigFileApplicationListener)
  • DebugAgentEnvironmentPostProcessor
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

fanderboy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值