Spring的源码分析-(SpringBoot 启动流程)

Scroll Down

SpringBoot 启动流程源码分析

首先需要了解一下SpringBoot的启动流程,创建SpringApplication的构造方法主要逻辑为指定了容器类型,并且使用ClassLoad加载META-INF/spring.factories下配置的一个应用启动监听器和一个上下文监听器,如果没有配置文件META-INF/spring.factories,那么返回空的List集合。

    public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        setInitializers((Collection) getSpringFactoriesInstances(
                ApplicationContextInitializer.class));
        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
        this.mainApplicationClass = deduceMainApplicationClass();
    }

再来追踪run方法,

public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
        configureHeadlessProperty();
        ①    
        SpringApplicationRunListeners listeners = getRunListeners(args);
        listeners.starting();
        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                    args);
            ②
            ConfigurableEnvironment environment = prepareEnvironment(listeners,
                    applicationArguments);
            configureIgnoreBeanInfo(environment);
            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);
            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;
    }
  1. 获取事件监听器SpringApplicationRunListener,并且启动监听器,播报启动事件

  2. 准备环境,执行environmentPrepared()方法播报事件,被ConfigFileApplicationListener监听处理。

读取配置项spring.profiles.active,用于构建ActiveProfiles,同时绑定一系列转换器,用于解析配置文件中的值。

    public static void addApplicationConverters(ConverterRegistry registry) {
        addDelimitedStringConverters(registry);
        registry.addConverter(new StringToDurationConverter());
        registry.addConverter(new DurationToStringConverter());
        registry.addConverter(new NumberToDurationConverter());
        registry.addConverter(new DurationToNumberConverter());
        registry.addConverter(new StringToDataSizeConverter());
        registry.addConverter(new NumberToDataSizeConverter());
        registry.addConverterFactory(new StringToEnumIgnoringCaseConverterFactory());
    }

同时在SpringBoot2.0版本后增加了对@ConfigurationProperties支持。把ConfigurationPropertySource绑定在environment内部.

ConfigFileApplicationListener监听处理主要是使用ClassLoad加载META-INF/spring.factories下配置的EnvironmentPostProcessor.然后把当前类也加入返回集合内,在ConfigFileApplicationListener#postProcessEnvironment时把配置文件内配置项,转换在尾插法添加到propertySourceList中。

    protected void addPropertySources(ConfigurableEnvironment environment,
            ResourceLoader resourceLoader) {
        RandomValuePropertySource.addToEnvironment(environment);
        new Loader(environment, resourceLoader).load();
    }
        public void load() {
            this.profiles = new LinkedList<>();
            this.processedProfiles = new LinkedList<>();
            this.activatedProfiles = false;
            this.loaded = new LinkedHashMap<>();
            initializeProfiles();
            while (!this.profiles.isEmpty()) {
                Profile profile = this.profiles.poll();
                if (profile != null && !profile.isDefaultProfile()) {
                    addProfileToEnvironment(profile.getName());
                }
                load(profile, this::getPositiveProfileFilter,
                        addToLoaded(MutablePropertySources::addLast, false));
                this.processedProfiles.add(profile);
            }
            resetEnvironmentProfiles(this.processedProfiles);
            load(null, this::getNegativeProfileFilter,
                    addToLoaded(MutablePropertySources::addFirst, true));
            addLoadedPropertySources();
        }
  1. 创建上下文,根据项目类型创建上下文
    protected ConfigurableApplicationContext createApplicationContext() {
        Class<?> contextClass = this.applicationContextClass;
        if (contextClass == null) {
            try {
                switch (this.webApplicationType) {
                case SERVLET:
                    contextClass = Class.forName(DEFAULT_SERVLET_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对象,该类支持

@Configuration,org.springframework.stereotype的注解.

  1. 准备上下文,执行完成后调用contextPrepared方法,contextLoaded方法
    private void prepareContext(ConfigurableApplicationContext context,
            ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
            ApplicationArguments applicationArguments, Banner printedBanner) {
        context.setEnvironment(environment);
        postProcessApplicationContext(context);
        applyInitializers(context);
        listeners.contextPrepared(context);
        if (this.logStartupInfo) {
            logStartupInfo(context.getParent() == null);
            logStartupProfileInfo(context);
        }
        // Add boot specific singleton beans
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
        if (printedBanner != null) {
            beanFactory.registerSingleton("springBootBanner", printedBanner);
        }
        if (beanFactory instanceof DefaultListableBeanFactory) {
            ((DefaultListableBeanFactory) beanFactory)
                    .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
        }
        // Load the sources
        Set<Object> sources = getAllSources();
        Assert.notEmpty(sources, "Sources must not be empty");
        load(context, sources.toArray(new Object[0]));
        listeners.contextLoaded(context);
    }
  • 绑定environment到当前context,同时在上下文中指定Resource,和Bean名称的默认生成类,以及绑定转换器。

  • 触发ApplicationContextInitializer#initialize的执行

  • 播报容器上下文启动事件

  • 日志打印入口类信息和当前的active profile

  • 添加一些特殊的bean注册在当前上下文。

  • 播报上下文准备完毕事件

  1. spring容器启动,去扫描并且初始化单例bean

代码直接看AbstractApplicationContext#refresh

 public void refresh() throws BeansException, IllegalStateException {
        synchronized(this.startupShutdownMonitor) {
            //这里会设置Context的启动时间,以及校验配置项的正确性和是否缺失配置项
            this.prepareRefresh();
            //获取创建上下文时候建立的
            ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
            //填充BeanFactory,设定转换器,解析器,类加载器等.
            // 这里提前注册一个用于检测ApplicationListener接口实现类的Bean是否是单例的后置处理器
            this.prepareBeanFactory(beanFactory);
            try {
                1.
                this.postProcessBeanFactory(beanFactory);
                2.
                this.invokeBeanFactoryPostProcessors(beanFactory);
                3.               
                this.registerBeanPostProcessors(beanFactory);
                4.
                this.initMessageSource();
                5.               
                this.initApplicationEventMulticaster();
                6.
                this.onRefresh();
                this.registerListeners();
                this.finishBeanFactoryInitialization(beanFactory);
                this.finishRefresh();
            } catch (BeansException var9) {
                if (this.logger.isWarnEnabled()) {
                    this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
                }

                this.destroyBeans();
                this.cancelRefresh(var9);
                throw var9;
            } finally {
                this.resetCommonCaches();
            }

        }
    }
  • 扫描添加默认的BeanFactoryPostProcessor的后置处理器的并执行postProcessBeanFactory方法
  • 加载工程中的所有的BeanFactoryPostProcessor的子类。按照@Order排序,反之无序执行。最终调用invokeBeanFactoryPostProcessors触发所有实现类的postProcessBeanFactory方法的执行。所有后置处理器的执行顺序的implement PriorityOrdered.>implement Ordered.> 无序。
  • 处理实现BeanPostProcessor接口的处理器。和上面工厂方式的一样。
  • 初始化MessageSource的对象
  • 初始化ApplicationEventMulticaster对象
  • 用于特殊子类上下文重写去处理特殊性Bean,例如UiApplicationContext则是用来处理对应的themeSource的Bean
  • 添加实现ApplicationListener的类,加载到监听器内。并且如果配置了容器启动播报事件,则触发一次播报。
  • 实例化所有的非懒加载的Bean.
    • 配置转换Bean

    • 添加value解析器(内嵌的Bean后置处理器),Resolve $ placeholders in the given text,

    • 提前初始化LoadTimeWeaverAware,用来提前注册对应的的转换器。

    • 初始化bean参见org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons方法

      处理所有的非抽象,非懒加载的单例bean和工厂bean。

    • 在getBean方法就是闻名遐迩的IOC

      1. 首选去获取单例对象,这里提到二级缓存

      2. 如果该对象已存在,则要去判断circular reference

      3. 判断bean工厂中是否有该bean

      4. 执行到此标记Bean被创建

      5. 初始化当前bean所依赖的bean

      6. 针对生命周期,创建单例bean或者是原型模式创建bean

      7. 比较创建的bean对象和Class是否一致。不一致通过调用TypeConverter来处理

  1. 执行程序入口类覆盖的afterRefresh方法。如果没有重写则跳过

  2. 执行ApplicationRunListeners中的started方法,执行ApplicationRunnerCommandLineRunner

如果需要在Spring Bean初始化之后执行需要执行特定的动作,可以利用ApplicationRunner接口 或者 CommandLineRunner接口,这俩个接口的实现类型执行顺序在没有指定@Order的时候,默认自然排序,反之按照@Order的值从小到大执行。

SpringBoot的启动流程如上所示,今日突然考虑到对于IOC三级缓存的一点理解,懒得另开篇。就在此地赘述一番!

为什么是三级缓存,二级就完事的事情为何还要多添步骤?

在spring解决循环依赖的过程:一级缓存singletonObject存完整的的Bean,二级缓存earlySingletonObject存储未完成全部注入的Bean,就是那些当出现循环依赖时可以先注入earlySingletonObject中的Bean实例。三级缓存singletonFactory存对应bean的工厂对象 ObjectFactory,看最新代码这里已经改成了函数式接口执行addSingletonFactory方法,来往singletonFactory添加ObjectFactory的匿名内部类中,具体的执行方法为getEarlyBeanReference

this.addSingletonFactory(beanName, () -> {
                return this.getEarlyBeanReference(beanName, mbd, bean);
            });
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    Object exposedObject = bean;
    if (bean != null && !mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
                exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
                if (exposedObject == null) {
                    return exposedObject;
                }
            }
        }
    }
    return exposedObject;
}

InfrastructureAdvisorAutoProxyCreator
hasInstantiationAwareBeanPostProcessors()这里证明预留了一个扩展点用于执行InstantiationAwareBeanPostProcessor后置处理器的扩展点,允许在对象返回之前修改甚至替换bean,这里就是IOC和AOP的交汇点。由此来看:如果存在AOP代理Proxy,singletonFactory返回的不是原始的Bean实例,而是实现AOP方法的Proxy
在研究Spring事务的章节就理清楚了,Spring结合AOP跟Bean的生命周期本身就是通过AnnotationAwareAspectJAutoProxyCreator这个bean后置处理器来完成的,在这个后置处理的postProcessAfterInitialization方法中对初始化后的Bean完成AOP代理。如果出现了循环依赖,那没有办法,只有给Bean先创建代理,但是没有出现循环依赖的情况下,设计之初就是让Bean在生命周期的最后一步完成代理而不是在实例化后就立马完成代理。

  • 存在AOP的情况

singletonFactory暴露ObjectFactory目的是为了完成AOP代理。对象工厂清楚如何创建对象的AOP代理,但是不会立马创建,而是到合适的时机进行AOP代理对象的创建。
earlySingletonObject存在的目的之一是保证对象只有一次AOP代理。当调用三级缓存的getObject()方法返回的对象会存入二级缓存,这样,当接下来的依赖者调用的时候, 会先判断二级缓存是否有目标对象,如果存在直接返回。

  • 不存在AOP的情况

singletonFactory 直接粗暴就是为了暴露ObjectFactory为了完成对象的实例化。earlySingletonObject:用于存放半成品的循环依赖bean。

所以 三级缓存的存在 既解决了单例的循环依赖,同时提供了AOP代理的支持。