spring boot 框架凭借极简配置,一键运行和强大的第三方框架集成等特点,受到广大开发者的青睐,基本成为java开发中必不可少的基础的框架。spirng boot带给我们这么多便利的背后,它都做了些什么,让我们就跟随spirng boot的整个启动流程一探究竟。

上图可见spring boot的整个启动流程及各组件的相互调用关系。

  1. java程序由启动主类调用main()方法开始。
  2. 调用 SpringApplication的构造方法,实例一个Spirng应用对象。在构造方法里主要完成启动环境初始化工作,如,推断主类,spring应用类型,加载配置文件,读取spring.factories文件等。
  3. 调用run方法,所有的启动工作在该方法内完成,主要完成加载配置资源,准备上下文,创建上下文,刷新上下文,过程事件发布等。
    整个启动流程细节我们跟着源码分析。

1.启动入口 (SrpingApplication)

大家熟悉的springboot的启动类,@SpringBootApplication + psvm(main方法)+ new SpringApplication().run(XXXX.class, args)

@SpringBootApplication
public class SummaryApplication {public static void main(String[] args) {SpringApplication application = new SpringApplication(); // 2application.run(SummaryApplication.class, args); //3
//      SpringApplication.run(SummaryApplication.class, args);  也可简化调用静态方法}
}
复制代码

1.1 @SpringBootApplication 注解

通过源码发现该注解只是@Configuration,@EnableAutoConfiguration,@ComponentScan 三个注解的组合,这是在springboot 1.5以后为这三个注解做的一个简写。接下来简单说下这三个注解的功能:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration //1.1.1 注册为配置类
@EnableAutoConfiguration //1.1.2 配置可自动装配
@ComponentScan(excludeFilters = { //1.1.3 声明可扫描Bean @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {...
}复制代码

1.1.1 @SpringBootConfiguration

该注解就是spirng ioc容器中java config 配置方式的@Configuration ,注册当前类为spring ioc容器的配置类。

搭配@bean注解创建一个简单spring ioc配置类

 @Configuration    public class Conf {   @Bean  public Car car() {     Car car = new Car();   car.setWheel(wheel());     return car;    }  @Bean  public Wheel wheel() {     return new Wheel();   }  }
复制代码

1.1.2 @EnableAutoConfiguration

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class) //最为重要
public @interface EnableAutoConfiguration {String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";/*** Exclude specific auto-configuration classes such that they will never be applied.* @return the classes to exclude*/Class<?>[] exclude() default {};/*** Exclude specific auto-configuration class names such that they will never be* applied.* @return the class names to exclude* @since 1.3.0*/String[] excludeName() default {};}
复制代码

@EnableAutoConfiguration借助@Import的帮助,将所有符合自动配置条件的bean定义加载到IoC容器,会根据类路径中的jar依赖为项目进行自动配置,如:添加了spring-boot-starter-web依赖,会自动添加Tomcat和Spring MVC的依赖,Spring Boot会对Tomcat和Spring MVC进行自动配置。

最关键的要属@Import(EnableAutoConfigurationImportSelector.class) ,借助EnableAutoConfigurationImportSelector,@EnableAutoConfiguration可以帮助SpringBoot应用将所有符合条件的@Configuration配置都加载到当前SpringBoot创建并使用的IoC容器。就像一只“八爪鱼”一样,借助于Spring框架原有的一个工具类:SpringFactoriesLoader的支持,@EnableAutoConfiguration可以智能的自动配置功效才得以大功告成!

1.1.3 @ComponentScan

@ComponentScan这个注解在Spring中很重要,它对应XML配置中的元素,@ComponentScan的功能其实就是自动扫描并加载符合条件的组件(比如@Component和@Repository等)或者bean定义,最终将这些bean定义加载到IoC容器中。

我们可以通过basePackages等属性来细粒度的定制@ComponentScan自动扫描的范围,如果不指定,则默认Spring框架实现会从声明@ComponentScan所在类的package进行扫描。

注:所以SpringBoot的启动类最好是放在root package下,因为默认不指定basePackages。

2.构造器(Constructor)

/*** Create a new {@link SpringApplication} instance. The application context will load* beans from the specified primary sources (see {@link SpringApplication class-level}* documentation for details. The instance can be customized before calling* {@link #run(String...)}.* @param resourceLoader the resource loader to use* @param primarySources the primary bean sources* @see #run(Class, String[])* @see #setSources(Set)*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {this.resourceLoader = resourceLoader;Assert.notNull(primarySources, "PrimarySources must not be null");this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));//2.1 判断当前程序类型this.webApplicationType = WebApplicationType.deduceFromClasspath();//2.2 使用SpringFactoriesLoader 实例化所有可用的初始器setInitializers((Collection)                        getSpringFactoriesInstances(ApplicationContextInitializer.class));//2.3 使用SpringFactoriesLoader 实例化所有可用的监听器setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));//2.4 配置应用主方法所在类this.mainApplicationClass = deduceMainApplicationClass();
}
复制代码

2.1 判断当前程序类型

根据classpath里面是否存在某个特征类

(org.springframework.web.context.ConfigurableWebApplicationContext)来决定是否应该创建一个为Web应用使用的ApplicationContext类型。

/*** The application should not run as a web application and should not start an* embedded web server.*/
NONE,/*** The application should run as a servlet-based web application and should start an* embedded servlet web server.*/
SERVLET,/*** The application should run as a reactive web application and should start an* embedded reactive web server.*/
REACTIVE;
复制代码

3.启动方法(RUN)

初始化完成之后就进到了run方法,run方法完成了所有Spring的整个启动过程:

  • 准备Environment

  • 发布事件

  • 创建上下文、bean

  • 刷新上下文

  • 结束,

    其中穿插了很多监听器的动作,并且很多逻辑都是靠各种监听器的实现类执行的。

/*** Run the Spring application, creating and refreshing a new* {@link ApplicationContext}.* @param args the application arguments (usually passed from a Java main method)* @return a running {@link ApplicationContext}*/
public ConfigurableApplicationContext run(String... args) {//开启时钟计时StopWatch stopWatch = new StopWatch();stopWatch.start();//spirng 上下文ConfigurableApplicationContext context = null;//启动异常报告容器Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();//开启设置,让系统模拟不存在io设备configureHeadlessProperty();// 3.1 初始化SpringApplicationRunListener 监听器,并进行封装SpringApplicationRunListeners listeners = getRunListeners(args);listeners.starting();try {ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);//3.2 Environment 的准备 ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);configureIgnoreBeanInfo(environment);Banner printedBanner = printBanner(environment); // 打印标语 彩蛋//3.3 创建上下文实例context = createApplicationContext();//异常播报器,默认有org.springframework.boot.diagnostics.FailureAnalyzersexceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,new Class[] { ConfigurableApplicationContext.class }, context);//3.4 容器初始化prepareContext(context, environment, listeners, applicationArguments, printedBanner);//3.5 刷新上下文容器 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;
}
复制代码

3.1 SpringApplicationRunListener 的使用

首先通过getSpringFactoriesInstances 获取到所有实现SpringApplicationRunListener 接口的实例,默认情况下该接口的实现类只有 EventPublishingRunListener 他的主要作用是作为springboot 的一个广播器

public interface SpringApplicationRunListener {/**EventPublishingRunListener 前期采用 SimpleApplicationEventMulticaster.multicastEvent(ApplicationEvent) 进行广播**/default void starting() {} default void environmentPrepared(ConfigurableEnvironment environment) {}default void contextPrepared(ConfigurableApplicationContext context) {}default void contextLoaded(ConfigurableApplicationContext context) {}/**EventPublishingRunListener 后期采用 context.publishEvent(ApplicationEvent)**/default void started(ConfigurableApplicationContext context) {}default void running(ConfigurableApplicationContext context) {}default void failed(ConfigurableApplicationContext context, Throwable exception) {}
}
复制代码

3.2 prepareEnvironment

一般在写业务代码时使用的都是只读类型的接口Environment,该接口是对运行程序环境的抽象,是保存系统配置的中心,而在启动过程中使用的则是可编辑的ConfigurableEnvironment。接口的UML类图如下,提供了合并父环境、添加active profile以及一些设置解析配置文件方式的接口。

其中一个比较重要的方法MutablePropertySources getPropertySources();,该方法返回一个可编辑的PropertySources,如果有在启动阶段自定义环境的PropertySources的需求,就可以通过该方法设置。

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,ApplicationArguments applicationArguments) {// Create and configure the environment//根据不同环境不同的Enviroment (StandardServletEnvironment,StandardReactiveWebEnvironment,StandardEnvironment)ConfigurableEnvironment environment = getOrCreateEnvironment();//填充启动类参数到enviroment 对象configureEnvironment(environment, applicationArguments.getSourceArgs());//更新参数ConfigurationPropertySources.attach(environment);//发布事件 listeners.environmentPrepared(environment);//绑定主类 bindToSpringApplication(environment);if (!this.isCustomEnvironment) {//转换environment的类型,但这里应该类型和deduce的相同不用转换environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,deduceEnvironmentClass());}//将现有参数有封装成proertySourcesConfigurationPropertySources.attach(environment);return environment;
}
复制代码

3.3 创建springApplicationContext 上下文

继承的三个父类接口里,Closeable提供了关闭时资源释放的接口,Lifecycle是提供对生命周期控制的接口(start\stop)以及查询当前运行状态的接口,ApplicationContext则是配置上下文的中心配置接口,继承了其他很多配置接口,其本身提供查询诸如id、应用程序名等上下文档案信息的只读接口,以及构建自动装配bean的工厂。

  • EnvironmentCapable

提供Environment接口。

  • MessageSource

国际化资源接口。

  • ApplicationEventPublisher

事件发布器。

  • ResourcePatternResolver

资源加载器。

  • HierarchicalBeanFactory、ListableBeanFactory

这两个都继承了bean容器的根接口BeanFactory

简而言之就是根据Web容器类型的不同来创建不用的上下文实例。

3.4 上下文初始化

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {//绑定环境context.setEnvironment(environment);//如果application有设置beanNameGenerator、resourceLoader就将其注入到上下文中,并将转换工具也注入到上下文中postProcessApplicationContext(context);//调用初始化的切面applyInitializers(context);//发布ApplicationContextInitializedEvent事件listeners.contextPrepared(context);//日志if (this.logStartupInfo) {logStartupInfo(context.getParent() == null);logStartupProfileInfo(context);}// Add boot specific singleton beansConfigurableListableBeanFactory beanFactory = context.getBeanFactory();beanFactory.registerSingleton("springApplicationArguments", applicationArguments);if (printedBanner != null) {beanFactory.registerSingleton("springBootBanner", printedBanner);}if (beanFactory instanceof DefaultListableBeanFactory) {//如果bean名相同的话是否允许覆盖,默认为false,相同会抛出异常((DefaultListableBeanFactory) beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);}if (this.lazyInitialization) {context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());}// Load the sources// 这里获取到的是BootstrapImportSelectorConfiguration这个class,而不是自己写的启动来,这个class是在之前注册的BootstrapApplicationListener的监听方法中注入的Set<Object> sources = getAllSources();Assert.notEmpty(sources, "Sources must not be empty");//加载sources 到上下文中load(context, sources.toArray(new Object[0]));//发布ApplicationPreparedEvent事件listeners.contextLoaded(context);
}
复制代码

3.5 刷新上下文

AbstractApplicationContext
复制代码
public void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {//记录启动时间、状态,web容器初始化其property,复制listenerprepareRefresh();//这里返回的是context的BeanFactoryConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();//beanFactory注入一些标准组件,例如ApplicationContextAwareProcessor,ClassLoader等prepareBeanFactory(beanFactory);try {//给实现类留的一个钩子,例如注入BeanPostProcessors,这里是个空方法postProcessBeanFactory(beanFactory);// 调用切面方法invokeBeanFactoryPostProcessors(beanFactory);// 注册切面beanregisterBeanPostProcessors(beanFactory);// Initialize message source for this context.initMessageSource();// bean工厂注册一个key为applicationEventMulticaster的广播器initApplicationEventMulticaster();// 给实现类留的一钩子,可以执行其他refresh的工作,这里是个空方法onRefresh();// 将listener注册到广播器中registerListeners();// 实例化未实例化的beanfinishBeanFactoryInitialization(beanFactory);// 清理缓存,注入DefaultLifecycleProcessor,发布ContextRefreshedEventfinishRefresh();}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 启动主要工作基本完成,接下来发布AppStartedEvent事件,回调ApplicationRunner,CommandLineRunner等runner,发布applicationReadyEvent事件,spring 正式启动开始运行。

spring boot 启动流程分析相关推荐

  1. Spring Boot————Spring Boot启动流程分析

    一.引言 Spring Boot 的启动虽然仅仅是执行了一个main方法,但实际上,运行流程还是比较复杂的,其中包含几个非常重要的事件回调机制.在实际生产开发中,有时候也会利用这些启动流程中的回调机制 ...

  2. Spring Boot(四):Spring Boot启动原理分析

    文章目录 Spring Boot启动原理分析 一.依赖导入原理 二.Spring Boot包扫描原理 三.Spring Boot自动配置原理 Spring Boot启动原理分析 一.依赖导入原理 父项 ...

  3. 介绍一下Spring Boot启动流程以及生命周期勾子函数?

    代码那些事儿 2020-02-22 16:10:15 上篇文章介绍了Spring Boot自动配置的原理,但是对于Spring Boot应用的整个启动流程还是一头雾水,虽然Spring Boot大大简 ...

  4. Spring Boot 启动流程

    写作中- //org.springframework.boot.SpringApplication#run(java.lang.String...) public ConfigurableApplic ...

  5. springboot中获得app_Spring Boot 应用程序启动流程分析

    SpringBoot 有两个关键元素: @SpringBootApplication SpringApplication 以及 run() 方法 SpringApplication 这个类应该算是 S ...

  6. Spring Boot启动过程源码分析--转

    https://blog.csdn.net/dm_vincent/article/details/76735888 关于Spring Boot,已经有很多介绍其如何使用的文章了,本文从源代码(基于Sp ...

  7. 分析Spring容器启动流程 Spring初始化

    分析Spring容器启动流程 Spring初始化 每当启动Web容器时(例如Tomcat),会读取Web应用中的web.xml文件.以下这段代码就是启动Spring容器的关键代码. ContextLo ...

  8. SpringBoot启动流程分析(四):IoC容器的初始化过程

    SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...

  9. SpringBoot(十二)启动流程分析之创建应用上下文AnnotationConfigServletWebServerApplicationContext

    SpringBoot版本:2.1.1      ==>启动流程分析汇总 接上篇博客Spring Boot 2.1.1(十一)启动流程分析之设置系统属性spring.beaninfo.ignore ...

最新文章

  1. 一次关于DNS服务器的故障排错记录——RNDC故障
  2. Django - - 进阶 - - 同源策略和跨域解决方案
  3. 基于Lucene查询原理分析Elasticsearch的性能
  4. puppet集群之 Nginx and Passenger
  5. React Native开发环境搭建记录
  6. testNg自动化,读取excel的数据
  7. 知乎爆赞干货:顶级大公司的OLAP数据仓库,这是全网最好的总结
  8. java 世界中Annotation
  9. 绝对郁闷到死的NHibernate2.0。。。
  10. HTML转义字符参照表
  11. 仙人掌之歌——权力的游戏(1)
  12. 麦肯锡三部曲_《麦肯锡精英高效阅读法》| 认知的四种境界,来看看自己在哪一层...
  13. 域名注册处更改dns服务器,怎么修改DNS服务器_为什么要修改DNS服务器
  14. 大麦支持选座位、定时等功能
  15. 关于23届大数据岗实习总结
  16. JMX MBean class xxx does not implement DynamicMBean, and neither follows the Standard MBean conventi
  17. SQL分组排序,取每组最新的值
  18. 病毒入侵:全靠分布式 Gossip 协议
  19. 计算机科学与技术专业大学排名及录取分数,计算机科学与技术专业分数线各大学排名(湖南)...
  20. C语言:利用指针编写程序,将一个一维数组a[10]中的最大元素与第一个元素互换、将最小元素与最后一个元素互换

热门文章

  1. 【第5天】善于说话的人一定是个好听众
  2. 如何统计一天24小时每个小时的数据量
  3. 智能家居品牌十大排名、智能洗地机品牌排行榜
  4. JAVA常用API : 匿名对象
  5. 拿什么拯救缺芯又“缺心”的科技产业?
  6. YouTuBe各类优秀频道推荐二音乐舞蹈
  7. FAO Penman-Monteith公式(彭曼公式)计算参考蒸散量ET0的Python代码
  8. vue 跳转新页面并传参
  9. git未提交代码找回
  10. python ——python 浮点数和整数转换