美文网首页Spring cloud
SpringApplication.run 到底做了什么?

SpringApplication.run 到底做了什么?

作者: habit_learning | 来源:发表于2019-07-06 10:16 被阅读13次

    用过 SpringBoot 的同学都知道,其程序的启动类是在一个main方法中调用SpringApplication.run方法执行的,如:

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

    那么,这里面到底做了什么呢?本篇文章将深入源码,带你一起探究底层实现。

    SpringApplication 初始化阶段

    进入到SpringApplication.run方法,其首先会创建一个SpringApplication对象,我们看其构造函数:

    public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
            this.resourceLoader = resourceLoader;
            Assert.notNull(primarySources, "PrimarySources must not be null");
            // primarySources 为 run 方法传入的引导类
            this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
            // 推断web应用类
            this.webApplicationType = deduceWebApplicationType();
            // 初始化 initializers 属性
            setInitializers((Collection) getSpringFactoriesInstances(
                    ApplicationContextInitializer.class));
            // 初始化监听器
            setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
            // 推断应用引导类
            this.mainApplicationClass = deduceMainApplicationClass();
        }
    

    我们先看推断web应用类的方法deduceWebApplicationType()

        private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet",
                "org.springframework.web.context.ConfigurableWebApplicationContext" };
    
        private static final String REACTIVE_WEB_ENVIRONMENT_CLASS = "org.springframework."
                + "web.reactive.DispatcherHandler";
    
        private static final String MVC_WEB_ENVIRONMENT_CLASS = "org.springframework."
                + "web.servlet.DispatcherServlet";
    
        private WebApplicationType deduceWebApplicationType() {
            if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null)
                    && !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)) {
                return WebApplicationType.REACTIVE;
            }
            for (String className : WEB_ENVIRONMENT_CLASSES) {
                if (!ClassUtils.isPresent(className, null)) {
                    return WebApplicationType.NONE;
                }
            }
            return WebApplicationType.SERVLET;
        }
    

    根据 classpath 下是否存在某个特征类来决定是否应该创建一个为 Web 应用使用的ApplicationContext类型。具体判断为:

    如果仅存在 Reactive 的包,则为WebApplicationType.REACTIVE类型;
    如果 Servlet 和 Reactive的包都不存在,则为WebApplicationType.NONE类型;
    其他情况都为WebApplicationType.SERVLET类型。

    接下来我们看初始化initializers属性的过程,其通过getSpringFactoriesInstances(ApplicationContextInitializer.class)方法获取初始化器:

    private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
                Class<?>[] parameterTypes, Object... args) {
            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
            // Use names and ensure unique to protect against duplicates
            Set<String> names = new LinkedHashSet<>(
                    SpringFactoriesLoader.loadFactoryNames(type, classLoader));
            List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
                    classLoader, args, names);
            AnnotationAwareOrderComparator.sort(instances);
            return instances;
        }
    

    该方法流程为:

    1. 通过SpringFactoriesLoader.loadFactoryNames(type, classLoader)方法,在 META-INF/spring.factories 文件下查找ApplicationContextInitializer类型对应的资源名称。
    2. 实例化上面的资源信息(初始化器)。
    3. 对初始化器根据Ordered接口或者@Order注解进行排序。

    同理,初始化listeners监听器也是类似的,这里不再累赘。

    SpringApplication 初始化阶段的最后一步是推断引导类deduceMainApplicationClass():

    private Class<?> deduceMainApplicationClass() {
            try {
                StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
                for (StackTraceElement stackTraceElement : stackTrace) {
                    if ("main".equals(stackTraceElement.getMethodName())) {
                        return Class.forName(stackTraceElement.getClassName());
                    }
                }
            }
            catch (ClassNotFoundException ex) {
                // Swallow and continue
            }
            return null;
        }
    

    其将调用栈中main方法所在的类作为引导类。

    SpringApplication 运行阶段

    SpringApplication 运行阶段属于核心过程,完全围绕 run(String...) 方法展开。该过程结合初始化阶段完成的状态,进一步完善运行时所需要准备的资源,随后启动 Spring 应用上下文。在此期间伴随着 Spring Boot 和 Spring 事件的触发,形成完整的 SpringApplication 生命周期。因此,下面将围绕以下三个子议题进行讨论。

    • SpringApplication 准备阶段
    • ApplicationContext 启动阶段
    • ApplicationContext 启动后阶段
    1. SpringApplication 准备阶段

    本阶段属于 ApplicationContext 启动阶段的前一阶段,设计的范围从 run(String...)方法调用开始,到refreshContext(ConfigurableApplicationContext)调用前:

    public ConfigurableApplicationContext run(String... args) {
            //记录程序运行时间
            StopWatch stopWatch = new StopWatch();
            stopWatch.start();
            //Spring 应用的上下文
            ConfigurableApplicationContext context = null;
            Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
            configureHeadlessProperty();
            // 获取 SpringApplicationRunListeners
            SpringApplicationRunListeners listeners = getRunListeners(args);
            listeners.starting();
            try {
                // 创建 ApplicationArguments 对象
                ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                        args);
                // 加载属性配置
                ConfigurableEnvironment environment = prepareEnvironment(listeners,
                        applicationArguments);
                // 处理需要忽略的Bean
                configureIgnoreBeanInfo(environment);
                // 打印 banner
                Banner printedBanner = printBanner(environment);
                // 创建 Spring 应用上下文
                context = createApplicationContext();
                // 实例化 SpringBootExceptionReporter,用来报告关于启动过程中的错误
                exceptionReporters = getSpringFactoriesInstances(
                        SpringBootExceptionReporter.class,
                        new Class[] { ConfigurableApplicationContext.class }, context);
                // 应用上下文的准备阶段
                prepareContext(context, environment, listeners, applicationArguments,
                        printedBanner);
                // 刷新应用上下文(自动装配,初始化 IOC 容器)
                refreshContext(context);
                ...
            }
      }
    
    • getRunListeners(args) 方法:
    private SpringApplicationRunListeners getRunListeners(String[] args) {
            Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
            return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
                    SpringApplicationRunListener.class, types, this, args));
        }
    

    该方法会通过getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args)方法,获取 META-INF/spring.factories 文件下SpringApplicationRunListener对应的资源,并且实例化这些资源:

    # Run Listeners
    org.springframework.boot.SpringApplicationRunListener=\
    org.springframework.boot.context.event.EventPublishingRunListener
    

    我们发现,其有且仅有一个实现类EventPublishingRunListener

    public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
    
        private final SpringApplication application;
    
        private final String[] args;
    
        private final SimpleApplicationEventMulticaster initialMulticaster;
    
        public EventPublishingRunListener(SpringApplication application, String[] args) {
            this.application = application;
            this.args = args;
            this.initialMulticaster = new SimpleApplicationEventMulticaster();
            for (ApplicationListener<?> listener : application.getListeners()) {
                this.initialMulticaster.addApplicationListener(listener);
            }
        }
    
        @Override
        public int getOrder() {
            return 0;
        }
    
        @Override
        public void starting() {
            this.initialMulticaster.multicastEvent(
                    new ApplicationStartingEvent(this.application, this.args));
        }
    
        @Override
        public void environmentPrepared(ConfigurableEnvironment environment) {
            this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(
                    this.application, this.args, environment));
        }
    
        @Override
        public void contextPrepared(ConfigurableApplicationContext context) {
    
        }
    
        @Override
        public void contextLoaded(ConfigurableApplicationContext context) {
            for (ApplicationListener<?> listener : this.application.getListeners()) {
                if (listener instanceof ApplicationContextAware) {
                    ((ApplicationContextAware) listener).setApplicationContext(context);
                }
                context.addApplicationListener(listener);
            }
            this.initialMulticaster.multicastEvent(
                    new ApplicationPreparedEvent(this.application, this.args, context));
        }
    
        @Override
        public void started(ConfigurableApplicationContext context) {
            context.publishEvent(
                    new ApplicationStartedEvent(this.application, this.args, context));
        }
    
        @Override
        public void running(ConfigurableApplicationContext context) {
            context.publishEvent(
                    new ApplicationReadyEvent(this.application, this.args, context));
        }
    
        @Override
        public void failed(ConfigurableApplicationContext context, Throwable exception) {
            ApplicationFailedEvent event = new ApplicationFailedEvent(this.application,
                    this.args, context, exception);
            if (context != null && context.isActive()) {
                // Listeners have been registered to the application context so we should
                // use it at this point if we can
                context.publishEvent(event);
            }
            else {
                // An inactive context may not have a multicaster so we use our multicaster to
                // call all of the context's listeners instead
                if (context instanceof AbstractApplicationContext) {
                    for (ApplicationListener<?> listener : ((AbstractApplicationContext) context)
                            .getApplicationListeners()) {
                        this.initialMulticaster.addApplicationListener(listener);
                    }
                }
                this.initialMulticaster.setErrorHandler(new LoggingErrorHandler());
                this.initialMulticaster.multicastEvent(event);
            }
        }
        ...
    
    }
    

    在实例化EventPublishingRunListener的过程中,会给它最重要的属性initialMulticaster赋值,其类型是SimpleApplicationEventMulticaster。接着遍历 SpringApplication 初始化阶段的listeners监听器集合,将监听器存入其关联的ListenerRetriever#applicationListeners属性中。

    了解 Spring 事件监听机制的同学应该对SimpleApplicationEventMulticaster不陌生,它是ApplicationEvent事件的发布者。Spring Boot 的事件监听机制也是如出一辙,具体可参考我的另一篇文章 深入理解 Spring 的事件发布监听机制

    于是接下来调用listeners.starting()方法就会通过其内部的initialMulticaster属性发布ApplicationStartingEvent事件。

    • prepareEnvironment(listeners,applicationArguments) 方法:

    加载属性配置。执行完成后,所有的environment的属性都会加载进来,包括 application.properties 和外部的属性配置。

    private ConfigurableEnvironment prepareEnvironment(
                SpringApplicationRunListeners listeners,
                ApplicationArguments applicationArguments) {
            // 创建 ConfigurableEnvironment 对象
            ConfigurableEnvironment environment = getOrCreateEnvironment();
            // 配置 ConfigurableEnvironment
            configureEnvironment(environment, applicationArguments.getSourceArgs());
            // 发布 ApplicationEnvironmentPreparedEvent 事件
            listeners.environmentPrepared(environment);
            // 将 ConfigurableEnvironment 绑定到 SpringApplication 中
            bindToSpringApplication(environment);
            if (this.webApplicationType == WebApplicationType.NONE) {
                environment = new EnvironmentConverter(getClassLoader())
                        .convertToStandardEnvironmentIfNecessary(environment);
            }
            ConfigurationPropertySources.attach(environment);
            return environment;
        }
    

    大致流程为:

    1. 创建ConfigurableEnvironment对象。
    2. 配置environment变量。
    3. 发布ApplicationEnvironmentPreparedEvent事件。
    4. 绑定environmentSpringApplication上。
    • createApplicationContext 方法

    该方法会根据webApplicationType类型,创建不同的ConfigurableApplicationContextSpring 应用上下文:

    protected ConfigurableApplicationContext createApplicationContext() {
            Class<?> contextClass = this.applicationContextClass;
            if (contextClass == null) {
                try {
                    switch (this.webApplicationType) {
                    case SERVLET:
                        contextClass = Class.forName(DEFAULT_WEB_CONTEXT_CLASS);
                        break;
                    case REACTIVE:
                        contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
                        break;
                    default:
                        contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
                    }
                }
                catch (ClassNotFoundException ex) {
                    throw new IllegalStateException(
                            "Unable create a default ApplicationContext, "
                                    + "please specify an ApplicationContextClass",
                            ex);
                }
            }
            return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
        }
    

    我们以 SERVLET 类型为例,它会创建AnnotationConfigServletWebServerApplicationContext应用上下文实例。

    • 获取 Spring 异常报告器
      getSpringFactoriesInstances(SpringBootExceptionReporter.class,new Class[] { ConfigurableApplicationContext.class }, context)方法,获取 META-INF/spring.factories 文件下类型为SpringBootExceptionReporter的资源实例:
    # Error Reporters
    org.springframework.boot.SpringBootExceptionReporter=\
    org.springframework.boot.diagnostics.FailureAnalyzers
    

    其实现类有且仅有一个,即FailureAnalyzers

    • prepareContext 方法
      Spring 应用上下文启动前的准备工作:
    private void prepareContext(ConfigurableApplicationContext context,
                ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
                ApplicationArguments applicationArguments, Banner printedBanner) {
            //设置 context 的 environment 属性
            context.setEnvironment(environment);
            // Spring 应用上下文的后置处理
            postProcessApplicationContext(context);
            // 运用 Spring 应用上下文初始化器
            applyInitializers(context);
            listeners.contextPrepared(context);
            if (this.logStartupInfo) {
                logStartupInfo(context.getParent() == null);
                logStartupProfileInfo(context);
            }
    
            // Add boot specific singleton beans
            context.getBeanFactory().registerSingleton("springApplicationArguments",
                    applicationArguments);
            if (printedBanner != null) {
                context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
            }
    
            // Load the sources
            Set<Object> sources = getAllSources();
            Assert.notEmpty(sources, "Sources must not be empty");
            // 加载 BeanDefinition
            load(context, sources.toArray(new Object[0]));
            listeners.contextLoaded(context);
        }
    

    大致流程为:

    1. context的属性做赋值,如设置环境变量,调用初始化器来初始化context
    2. 获取所有配置源信息,包括 Configuration Class、类名、包名及Spring XML 配置资源路径信息。
    3. 加载 Spring 应用上下文配置源。将BeanDefinition加载到context中。
    4. 发布上下文已准备事件ApplicationPreparedEvent

    这里,我们要着重看第三步load(context, sources.toArray(new Object[0])):

    protected void load(ApplicationContext context, Object[] sources) {
            if (logger.isDebugEnabled()) {
                logger.debug(
                        "Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
            }
            BeanDefinitionLoader loader = createBeanDefinitionLoader(
                    getBeanDefinitionRegistry(context), sources);
            if (this.beanNameGenerator != null) {
                loader.setBeanNameGenerator(this.beanNameGenerator);
            }
            if (this.resourceLoader != null) {
                loader.setResourceLoader(this.resourceLoader);
            }
            if (this.environment != null) {
                loader.setEnvironment(this.environment);
            }
            loader.load();
        }
    

    该方法将 Spring 应用上下文转载的任务交给了BeanDefinitionLoader:

    class BeanDefinitionLoader {
    
        private final Object[] sources;
    
        private final AnnotatedBeanDefinitionReader annotatedReader;
    
        private final XmlBeanDefinitionReader xmlReader;
    
        private BeanDefinitionReader groovyReader;
    
        private final ClassPathBeanDefinitionScanner scanner;
    
        private ResourceLoader resourceLoader;
        ...
    }
    

    BeanDefinitionLoader组合了多个属性,第一个属性为SpringApplication#getAllSources()方法返回值,而属性annotatedReaderxmlReadergroovyReader分别为注解驱动实现AnnotatedBeanDefinitionReader、XML 配置实现XmlBeanDefinitionReader和 Groovy 实现GroovyBeanDefinitionReader。其中AnnotatedBeanDefinitionReaderClassPathBeanDefinitionScanner配合,形成AnnotationConfigApplicationContext扫描和注册配置类的基础,随后这些配置类被解析为 Bean 定义BeanDefinition

    public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry {
    
        private final AnnotatedBeanDefinitionReader reader;
    
        private final ClassPathBeanDefinitionScanner scanner;
    
    
        /**
         * Create a new AnnotationConfigApplicationContext that needs to be populated
         * through {@link #register} calls and then manually {@linkplain #refresh refreshed}.
         */
        public AnnotationConfigApplicationContext() {
            this.reader = new AnnotatedBeanDefinitionReader(this);
            this.scanner = new ClassPathBeanDefinitionScanner(this);
        }
    
        ...
    
        public void register(Class<?>... annotatedClasses) {
            Assert.notEmpty(annotatedClasses, "At least one annotated class must be specified");
            this.reader.register(annotatedClasses);
        }
    
        public void scan(String... basePackages) {
            Assert.notEmpty(basePackages, "At least one base package must be specified");
            this.scanner.scan(basePackages);
        }
        ...
    }
    

    不难看出,Spring Boot 中的BeanDefinitionLoader是以上BeanDefinition读取的综合实现。当其load()方法调用时,这些BeanDefinitionReader类型的属性各司其职,为 Spring 应用上下文从不同的配置源装载 Spring Bean 定义(BeanDefinition)。

    装载完BeanDefinition到 Spring 应用上下文之后,就调用listeners.contextLoaded(context)方法,发布 Spring 应用上下文已准备ApplicationPreparedEvent事件,以结束 SpringApplication 准备阶段。

    2. ApplicationContext 启动阶段

    本阶段的执行由refreshContext(ConfigurableApplicationContext)完成,其核心方法是AbstractApplicationContext#refresh:

    public void refresh() throws BeansException, IllegalStateException {
            synchronized (this.startupShutdownMonitor) {
                // Prepare this context for refreshing.
                prepareRefresh();
    
                // Tell the subclass to refresh the internal bean factory.
                ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
    
                // Prepare the bean factory for use in this context.
                prepareBeanFactory(beanFactory);
    
                try {
                    // Allows post-processing of the bean factory in context subclasses.
                    postProcessBeanFactory(beanFactory);
    
                    // Invoke factory processors registered as beans in the context.
                    invokeBeanFactoryPostProcessors(beanFactory);
    
                    // Register bean processors that intercept bean creation.
                    registerBeanPostProcessors(beanFactory);
    
                    // Initialize message source for this context.
                    initMessageSource();
    
                    // Initialize event multicaster for this context.
                    initApplicationEventMulticaster();
    
                    // Initialize other special beans in specific context subclasses.
                    onRefresh();
    
                    // Check for listener beans and register them.
                    registerListeners();
    
                    // Instantiate all remaining (non-lazy-init) singletons.
                    finishBeanFactoryInitialization(beanFactory);
    
                    // Last step: publish corresponding event.
                    finishRefresh();
                }
    
                catch (BeansException ex) {
                    if (logger.isWarnEnabled()) {
                        logger.warn("Exception encountered during context initialization - " +
                                "cancelling refresh attempt: " + ex);
                    }
    
                    // Destroy already created singletons to avoid dangling resources.
                    destroyBeans();
    
                    // Reset 'active' flag.
                    cancelRefresh(ex);
    
                    // Propagate exception to caller.
                    throw ex;
                }
    
                finally {
                    // Reset common introspection caches in Spring's core, since we
                    // might not ever need metadata for singleton beans anymore...
                    resetCommonCaches();
                }
            }
        }
    

    随着该方法的执行,Spring Boot 核心特性也随之启动,如组件自动装配、嵌入式容器启动。了解过 Spring Boot 自动装配机制的同学应该知道(如不清楚,请参考我的另一篇文章 Spring Boot 自动装配),在根据应用类型创建不同的 Spring 应用上下文的方法createApplicationContext()中,会实例化AnnotatedBeanDefinitionReader对象,该对象的构造方法中,会将ConfigurationClassPostProcessor封装成 Spring Bean 定义(BeanDefinition),并将其注入到 Ioc 容器DefaultListableBeanFactory(其实现了BeanDefinitionRegistry,具有注入BeanDefinition的功能)中。于是,在该阶段的invokeBeanFactoryPostProcessors(beanFactory)方法中,就会取出ConfigurationClassPostProcessor对象,随后调用其postProcessBeanFactory(beanFactory)方法进行装配工作。

    3. ApplicationContext 启动后阶段

    实际上,SpringApplication#afterRefresh方法并未给 Spring 应用上下文启动后阶段提供实现,而是将其交给开发人员自行扩展:

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

    所有,直接跳过该步,接下来调用listeners.started(context)方法,发布 Spring 应用上下文已启动ApplicationStartedEvent事件。

    该阶段最后,调用callRunners(context, applicationArguments)方法,来调用实现了CommandLineRunner或者ApplicationRunner接口的类的 run 方法,得以满足需要在 Spring 应用上下文完全准备完毕后,执行一些操作的场景。

    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);
                }
            }
        }
    

    总结

    至此,SpringApplciation.run 方法的执行流程已经讲解完毕。下面我们来整理一下大体的步骤:

    1. 初始化 SpringApplication 实例:决定web应用类型、加载初始化器和监听器、推断 main 方法的定义类。
    2. 通过 SpringFactoriesLoader 加载的 SpringApplicationRunListener,调用它们的 started 方法。
    3. 创建并配置当前 Spring Boot 应用将要使用的 Environment,如 applocation.properties 文件和外部配置。
    4. 根据 Web 服务类型创建不同的 Spring 应用上下文,并将之前准备好的 Environment 设置给 Spring 应用上下文 ApplicationContext 使用。
    5. 遍历初始化器,对 ApplicationContext 进行初始化操作。
    6. 加载所有资源,如 Configuration Class、类名、包名以及 Spring XML 配置资源路径,将所有 BeanDefinition 加载至 ApplicationContext。
    7. 初始化上下文 refresh(),进行自动装配,初始化 Ioc 容器等操作。
    8. 寻找当前 ApplicationContext 中是否注册有 CommandLineRunner 或者 ApplicationRunner,如果有,则遍历执行它们。


    相关文章

      网友评论

        本文标题:SpringApplication.run 到底做了什么?

        本文链接:https://www.haomeiwen.com/subject/uhxqhctx.html