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

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方法。通过 ConfigurableApplicationContext 的实例获取容器的环境Environment,从而实现对配置文件的修改完善等工作。

spring-boot中ApplicationContextInitializer的实现如下:

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

如果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;
    }
}

2.5 加载Spring应用事件监听器

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

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

spring-boot中ApplicationListener的实现如下:

  1. spring-boot-autoconfigure
    • BackgroundPreinitializer
  2. spring-boot
    • ClearCachesApplicationListener
    • ParentContextCloserApplicationListener
    • FileEncodingApplicationListener
    • AnsiOutputApplicationListener
    • DelegatingApplicationListener
    • LoggingApplicationListener
    • EnvironmentPostProcessorApplicationListener
  • 43
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

fanderboy

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

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

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

打赏作者

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

抵扣说明:

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

余额充值