6-springboot启动流程源码分析

 

目录

1、SpringApplication构造函数

1.1、deduceFromClasspath()

1.2、getSpringFactoriesInstances(Class type)

2、SpringApplication的run(String… args)启动函数运行过程分解

2.1、prepareEnvironment

2.2、createApplicationContext

2.3、prepareContext

 2.4、refreshContext

2.5、callRunners


使用SpringApplication类用来启动Spring时,默认会执行以下主要步骤来启动应用:

  1. 根据classpath创建一个相应的ApplicationContext
  2. 注册一个CommandLinePropertySource来曝光命令行参数作为spring的属性
  3. 刷新application context来加载所有的单例bean
  4. 触发CommandLineRunner的bean实例的run方法

//启动SpringBootApplication
@SpringBootApplication
public class MvcApplication {

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

}

SpringBoot的启动流程分析:

1、SpringApplication构造函数

运行SpringApplication.run(MvcApplication.class, args)方法时,SpringApplication类首先会创建一个SpringApplication对象实例,然后再执行其相应的run方法

我们先看SpringApplication的构造函数:

该构造函数主要是为一些变量属性赋值:

  • primarySources属性,即SpringApplication.run(MvcApplication.class, args) 中传入的MvcApplication.class,该类为SpringBoot项目的启动类,主要从该类的configuration类加载bean
  • webApplicationType属性,代表应用类型,根据classpath存在的相应Application类来判断,

见1.1 deduceFromClasspath()方法

  • initializers属性,ApplicationContextInitializer 数组,从spring.factories配置文件中加载,

见1.2 getSpringFactoriesInstances(Class type)方法。

  • listeners属性,ApplicationListener 数组,从spring.factories配置文件中加载
  • mainApplicationClass属性,获得哪个类调用了main(String[] args) 方法的信息,比如mainApplicationClass这里是“class com.sinkfish.generatemybatis.GenerateMyBatisApplication”启动了main方法,仅仅用来打印日志

 

 

1.1、deduceFromClasspath()

此类将根据classpath判断应用类型

通过反射加载classpath判断指定的标志类存在与否来分别判断是reactive应用,web应用还是非web应用

1.2、getSpringFactoriesInstances(Class type)

先根据接口类从项目的所有spring.factories配置文件中获得相应的具体实现类放入names集合,然后再调用createSpringFactoriesInstances方法创建具体的实例,下面是createSpringFactoriesInstances方法:

根据传入的接口类型从spring.factories配置文件中加载相应的实例,

比如:getSpringFactoriesInstances(ApplicationContextInitializer.class)方法根据ApplicationContextInitializer.class接口类去spring.factories配置文件寻找具体实现类,最终初始化到初始化 initializers 属性中,给initializers属性打上断点

经过调试,ApplicationContextInitializer接口具体实现类有:

 同理ApplicationListener的具体实现类如下:

2、SpringApplication的run(String… args)启动函数运行过程分解

前面分析了SpringApplication的构造函数,下面来分析该类的run方法。

在SpringApplication类中按下

 alt + 7,查看该类的结构图

查看三个方法:

用上面相同的方法找到重载的run构造方法

 

总结如下:

1,实例化startTime变量用于统计run启动过程时长

2,调用configureHeadlessProperty()方法配置headless

即“java.awt.headless”属性,默认为ture,那么做了这样的操作后,SpringBoot想干什么呢?

其实是想设置该应用程序,即使没有检测到显示器,也允许其启动.对于服务器来说,是不需要显示器的,所以要这样设置.

参考:www.cnblogs.com/wangxuejian…

3,获取SpringApplicationRunListeners,并启动SpringApplicationRunListener的监听,此时利用EventPublishingRunListener的initialMulticaster对象来发布ApplicationStartingEvent事件,表示SpringApplication开始启动,监听ApplicationStartingEvent事件的listeners们将会执行相应逻辑。

注意:EventPublishingRunListener拥有SpringApplication属性。

4,调用prepareEnvironment方法准备environment,比如准备配置参数有servlet配置参数,外部配置参数比如jvm启动参数等。详情请见2.1 prepareEnvironment方法

5,调用printBanner(environment)方法打印springboot的bannner

6,调用createApplicationContext()方法根据webApplicationType创建容器context,因为是web项目,这里创建的是AnnotationConfigServletWebServerApplicationContext容器对象,详情请见2.2 createApplicationContext方法

7,调用prepareContext方法准备容器事项,比如调用调用各个ApplicationContextInitializer的initialize初始化方法

和触发SpringApplicationRunListeners的contextPrepared及contextLoaded方法等,详情请见2.3 prepareContext方法

8,调用refreshContext方法最终调用AbstractApplicatioinContetext.refresh()方法刷新容器,这一步至关重要,因为很多spring重要的逻辑都在这里处理,请见2.4 refreshContext方法

9,调用afterRefresh方法执行刷新容器后的后置处理逻辑,注意目前这里为空方法,留给子类去实现。

10,触发SpringApplicationRunListener的started方法,发布ApplicationStartedEvent事件,通知spring容器已经启动,相应listener监听该事件执行相应处理逻辑。

11,调用callRunners方法,即会调用ApplicationRunner和CommandLineRunner的run方法,实现spring容器启动后需要做的一些东西,详情请见2.5 callRunners方法

 

2.1、prepareEnvironment

上面代码首先根据webApplicationType创建不同的environment对象,这里创建的是StandardServletEnvironment对象;然后对environment对象进行配置,比如增加 environment 的 PropertySource 属性源,还有配置哪个profile为actived;environment环境变量准备好后,此时发布ApplicationEnvironmentPreparedEvent事件,执行environment准备好后的相关逻辑;然后再将environment绑定到SpringApplication,对非自定义的environment进行转换等。

2.2、createApplicationContext

//该方法简化后的结构如下
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);
}

2.3、prepareContext

prepareContext主要是准备context,主要做了以下事情:

  • postProcessApplicationContext(context),子类可以覆盖该方法做一些定制处理
  • applyInitializers(context):这个是将之前从spring.factories中加载的ApplicationContextInitializer接口的具体实现类逐个应用其initialize初始化方法,执行一些初始化逻辑

  • listeners.contextPrepared(context):发布ApplicationContextInitializedEvent事件,说明context已经准备好
  • getAllSources():将primarySource加载出来,应该是之后要对其配置的javaconfig或xml配置的bean进行加载,这里留个疑问先。
  • load(context, sources.toArray(new Object[0])):加载所有的配置文件比如javaconfig和xml配置文件的bean定义,这里创建了一个BeanDefinitionLoader对象;BeanDefinitionLoader作为AnnotatedBeanDefinitionReader,XmlBeanDefinitionReader和ClassPathBeanDefinitionScanner的门面,从底层源加载bean定义
  • listeners.contextLoaded(context):发布ApplicationPreparedEvent,说明context已经加载完毕。

 

 

 2.4、refreshContext

 

private void refreshContext(ConfigurableApplicationContext context) {
    // 刷新容器
    refresh(context);
    // 若注册了shutdownHook钩子,则注册shutdownHook钩子
    // JVM shutdownHook的作用是当jvm关闭时,关闭context容器,销毁相关bean
    if (this.registerShutdownHook) {
    try {
    context.registerShutdownHook();
    }
    catch (AccessControlException ex) {
        // Not allowed in some environments.
        }
    }
}


protected void refresh(ApplicationContext applicationContext) {
    // 断言:applicationContext必须是AbstractApplicationContext的子类,否则报错
    Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
    // 刷新容器
    ((AbstractApplicationContext) applicationContext).refresh();
}



public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
    // Prepare this context for refreshing.
    // 在context刷新前做一些准备工作,比如设置context的启动日期,active flag和property sources的初始化工作
    prepareRefresh();

    // Tell the subclass to refresh the internal bean factory.
    // 让子类刷新其内部bean factory,实质就是再新建一个DefaultListableBeanFactory类型的bean factory对象
    // 加载xml配置的bean定义,注意加载annotation的bean定义应该是在invokeBeanFactoryPostProcessors方法中加载??
    ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
    
    // Prepare the bean factory for use in this context.
    // 上面那一步工厂刚建好,什么都没有,因此需要准备一些配置,
    // 比如配置factory的标准容器特性,比如容器的类加载器和一些后置处理器比如ApplicationContextAwareProcessor 等
    prepareBeanFactory(beanFactory);
    
    try {
    // Allows post-processing of the bean factory in context subclasses.
    // 在经过上面的标准初始化后,修改应用容器的内部bean factory。
    // 在这一步,所有的bean definitions将会被加载,但此时bean还不会被实例化
    // 此时会注册一些BeanPostProcessors
    postProcessBeanFactory(beanFactory);

    // Invoke factory processors registered as beans in the context.
    // Bean工厂的后置处理器的相关逻辑,BeanFactoryPostProcessor(触发时机:bean定义注册之后bean实例化之前)
    // 和BeanDefinitionRegistryPostProcessor(触发时机:bean定义注册之前),所以可以在Bean工厂的后置处理器中修改Bean的定义信息,
    // 比如是否延迟加载、加入一些新的Bean的定义信息等实例化所有注册的BeanFactoryPostProcessor beans,并且调用其后置处理方法
    // BeanFactoryPostProcessor 是针对 BeanFactory 的扩展,主要用在 bean 实例化之前,读取 bean 的定义,并可以修改它。
    invokeBeanFactoryPostProcessors(beanFactory);

    // Register bean processors that intercept bean creation.
    // 实例化并注册所有Bean的后置处理器:BeanPostProcessor beans,册所有的 BeanPostProcessor,将所有实现了 BeanPostProcessor 接口的类加载到 BeanFactory 中。
    // registerBeanPostProcessors 方法主要用于处理 BeanPostProcessor 接口,调用时机:必须在所有bean的实例化之前调用
    // BeanPostProcessor 是针对 bean 的扩展,主要用在 bean 实例化之后,执行初始化方法前后,允许开发者对 bean 实例进行修改。
    registerBeanPostProcessors(beanFactory);
    
    // Initialize message source for this context.
    // 这里的逻辑主要跟国际化有关
    initMessageSource();
    
    // Initialize event multicaster for this context.
    // 初始化事件广播器,如果自定义了广播器,就用自定义的,
    // 如果没有自定义就用默认得SimpleApplicationEventMulticaster广播器。并且把广播器设置到上下文的applicationEventMulticaster属性中。
    initApplicationEventMulticaster();
    
    // Initialize other special beans in specific context subclasses.
    // 这个是个模板方法,留给子类实现,在特殊的bean初始化之前和单例bean实例化之前调用
    // 这里应该可以扩展ThemeSource接口
    onRefresh();

    // Check for listener beans and register them.
    // 注册实现了ApplicationListener接口的监听器,这里不会影响其他监听器的使用
    registerListeners();
    
    // Instantiate all remaining (non-lazy-init) singletons.
    // 完成容器bean factory的初始化,并初始化所有剩余的单例bean
    // 这个方法在容器刷新过程中非常重要,因为所有的bean,如果不是lazy-init的都会在这一步进行实例化,并且做一些处理。
    finishBeanFactoryInitialization(beanFactory);
    
    // Last step: publish corresponding event.
    // 完成容器的刷新共工作,并且调用生命周期处理器的onRefresh()方法,并且发布ContextRefreshedEvent事件
    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();
        }
    }
}

refresh方法是spring容器的重头戏,很多重要复杂的逻辑都在这里实现。

比如所有beanFactory的创建,所有bean定义的加载,实例化,注册一些事件监听器比如实现了ApplicationListener接口的监听器,还有一些后置处理方法也在这里处理,下面主要讲下后置处理方法,beanFactoryPostProcessor和beanPostProcessor等后置处理器也在这里实现调用相应的后置处理方法,比如beanFactoryPostProcessor是bean工厂的bean属性处理容器,说通俗一些就是可以管理我们的bean工厂内所有的beandefinition(未实例化)数据,可以随心所欲的修改属性;BeanPostProcessor是Spring IOC容器给我们提供的一个扩展接口,通过该接口的postProcessBeforeInitialization和postProcessAfterInitialization方法可以在bean的初始化前后执行一些自定义的逻辑。

2.5、callRunners

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

沉鱼的字符串

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

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

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

打赏作者

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

抵扣说明:

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

余额充值