Spring boot - SpringApplication().run(args) 启动流程分析

写在文章前

本文干货十足,有任何问题可以加我微信 or 在下方留言
Spring boot 版本 2.0.4.RELEASE

希望大家不要无脑转载,我写这个花了很长时间哈,转自请备注转载地址

预先准备

Spring

1.BeanPostProcessor

如果我们想在Spring容器中完成bean实例化、配置以及其他初始化方法前后要添加一些自己逻辑处理。
我们需要定义一个或多个BeanPostProcessor接口实现类,然后注册到Spring IoC容器中

2.BeanFactoryPostProcessor
注册一个BeanFactoryPostProcessor实例需要定义一个Java类来实现BeanFactoryPostProcessor接口,并重写该接口的postProcessorBeanFactory方法

3.ApplicationContextInitializer

调用该接口的initialize方法在ConfigurableApplicationContext#refresh之前

4.BeanNameAware

#setBeanName() 配置这个Bean的名称

5.InitializingBean

在properties注入完毕后会 调用#afterPropertiesSet()

6.DisposableBean

在Bean销毁时会调用#destroy()方法

7.BeanFactoryAware

获取setBeanFactory 在Bean填充后 初始化之前

Spring Bean 生命周期

copy from:https://www.cnblogs.com/redcool/p/6397398.html

初始化Bean开始
|
|
反射调用Bean的初始化方法实例化Bean
|
|
通过反射注入bean的属性值
|
|
Aware注入 比如BeanNameAware BeanClassLoaderAware
|
|
调用每个BeanPostInitializer接口中postProcessorBefore
|
|
调用初始化方法
|
|
调用每个BeanPostInitializer接口中postProcessorAfter
|
|
注册需要执行销毁方法的Bean


    @Bean(initMethod = "init")
    public MySpringBean mySpringBean(){
        return new MySpringBean();
    }
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.*;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import javax.annotation.PostConstruct;

/**
 * @author <a href="mailto:boommanpro@gmail.com">BoomManPro</a>
 * @date 2019/6/11 15:00
 * @created by BoomManPro
 */
@Slf4j
public class MySpringBean implements BeanNameAware, BeanFactoryAware, InitializingBean, ApplicationContextAware , DisposableBean {

    private ApplicationContext applicationContext;


    @PostConstruct
    public void constructInit(){
        log.info(" constructInit......");

    }

    public MySpringBean() {
        log.info("new MySpringBean......");
    }

    @Override
    public void setApplicationContext(ApplicationContext context) throws BeansException {
        log.info("ApplicationContextAware-setApplicationContext......");
        this.applicationContext = context;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        log.info("InitializingBean-afterPropertiesSet......");
    }

    @Override
    public void setBeanFactory(BeanFactory bf) throws BeansException {
        log.info("BeanFactoryAware-setBeanFactory......");
    }

    @Override
    public void setBeanName(String name) {
        log.info("BeanNameAware-setBeanName......");
    }

    public void init() {
        log.info("init-method......");
    }

    @Override
    public void destroy() throws Exception {
        log.info("DisposableBean-method......");
    }
}

正文源码分析

1.Spring Boot项目启动 最终都是如下方式启动

// org.springframework.boot.SpringApplication 1258
 new SpringApplication(primarySources).run(args);

所以第一步是创建一个 SpringApplication实例 然后执行run方法

2.下面来分析SpringApplication的构造函数

// org.springframework.boot.SpringApplication 263

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
	    //resourceLoader默认传值为null
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
		//利用ClassUtils来判断是否有Reactive相关依赖 最终判断spring程序的类型是什么 最终创建的ApplicatioonContext和这个有关系
		//详见--->org.springframework.boot.SpringApplication#createApplicationContext 576
		this.webApplicationType = deduceWebApplicationType();
		//首先getSpringFactoriesInstances 是获取实例对象 获取实现了ApplicationContextInitializer.class的实例对象
		
		//那么ApplicationContextInitializer接口的作用是什么呢?
		/*
		Callback interface for initializing a Spring {@link ConfigurableApplicationContext}
         prior to being {@linkplain ConfigurableApplicationContext#refresh() refreshed}.
         
         他会在 ConfigurableApplicationContext#refresh()调用之前调用ApplicationContextInitializer#initialize()方法
         
         具体使用方法
         1.通过ConfigurableApplicationContext手动添加
         2.在application.properties || application.yml中配置 context.initializer.classes=cn.boommanpro.MyApplicationContextInitializer
         3.resources/META-INF/spring.factories 中配置 org.springframework.context.ApplicationContextInitializer
         
         感觉第三种方式更优雅些,源码中大家在这么用
         
        
         
         在我这个项目中通过Debug看到加载了6个ApplicationContextInitializer.class实现
         
         通过IDEA快捷键 `Alt`+`F7` 查看他们的Usage
         org/springframework/boot/spring-boot/2.0.4.RELEASE/spring-boot-2.0.4.RELEASE.jar!/META-INF/spring.factories
         org.springframework.context.ApplicationContextInitializer=\
         org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
         org.springframework.boot.context.ContextIdApplicationContextInitializer,\
         org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
         org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
         
         org/springframework/boot/spring-boot-autoconfigure/2.0.4.RELEASE/spring-boot-autoconfigure-2.0.4.RELEASE.jar!/META-INF/spring.factories
         org.springframework.context.ApplicationContextInitializer=\
         org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
         org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
         
         分别是
      
         1.org.springframework.boot.context.config.DelegatingApplicationContextInitializer
         //根据Environment  context.initializer.classes取到 相关实现了ApplicationContextInitializer.class的class 并调用initializer放啊
         2.org.springframework.boot.context.ContextIdApplicationContextInitializer
         //为当前applicationContext设置Id 详细算法查看initialize方法
         3.org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer
         //报告常见的未配置的错误 查看源码默认配置的是 org && org.springframework
         4.org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
         //添加ApplicationListener
         5.org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer
         //配置BeanFactoryPostProcessor原始资源缓存  什么是原始资源  为什么这里要配置
         6.org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
         //条件报告评估记录监听器 Spring新出的应用位为什么启动不了的处理
         
         综上所述 我认为ApplicationContextInitializer.class的作用是为给SpringContext在做一些配置 或者一些在Spring容器还未完全构建时才能做得一些事 他在refresh方法之前调用
         
         总结一般会用到:
         
         1.配置BeanFactoryPostProcessor
         2.配置ConfigurableApplication#addApplicationListener()
         3.根据Environment获取到的一些值做一些事情
		*/
		setInitializers((Collection) getSpringFactoriesInstances(
				ApplicationContextInitializer.class));
		//配置一些监听器 获取到的还是:
		// org/springframework/boot/spring-boot-autoconfigure/2.0.4.RELEASE/spring-boot-autoconfigure-2.0.4.RELEASE.jar!/META-INF/spring.factories
		// org/springframework/boot/spring-boot-autoconfigure/2.0.4.RELEASE/spring-boot-autoconfigure-2.0.4.RELEASE.jar!/META-INF/spring.factories
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		//获取mainApplicationClass
		this.mainApplicationClass = deduceMainApplicationClass();
	}

3.然后我们分析run方法

	/**
	 * 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}
	 * 其实这个注释表明了和这个方法的作用 启动一个SpringApplication 创建和刷新一个新的ApplicationContext 这里传入args作用为命令行传参
	 * 它传入一些参数的优先级和传入值可参考Spring官方文档:
	 * https://docs.spring.io/spring-boot/docs/2.1.0.RELEASE/reference/htmlsingle/#boot-features-external-config
	 * 注意: 之后的会覆盖之前的 哪个文件被最后加载,哪个才具有最高级别,
	 */
	public ConfigurableApplicationContext run(String... args) {
	    //后面会调用 stopWatch.getTotalTimeSeconds() 获取运行时间
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		//定义变量 context
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		//配置是否初始化AWT 我个人认为是只有Swing编程会用到。。 所以Spring默认配置为true headless 如果你的项目用到了swing这里需要填写false,可能是个坑 自己填下吧
		configureHeadlessProperty();
		//获取SpringApplicationRunListener.class实例 貌似默认只有一个 EventPublishingRunListener  Spring的事件驱动模型
		//EventPublishingRunListener 又添加了之前的Listener
		SpringApplicationRunListeners listeners = getRunListeners(args);
		//执行监听 EventPublishingRunListener包含了之前的Listenner,之前的Listener调用了#onApplicationEvent
		listeners.starting();
		try {
		    //此时args派上了用场
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(
					args);
		    //再次调用 Listener调用了#onApplicationEvent
			ConfigurableEnvironment environment = prepareEnvironment(listeners,
					applicationArguments);
			//默认为默认为true  参考类 java.beans.SimpleBeanInfo  使用参考自: https://blog.csdn.net/cuiyaoqiang/article/details/53582382
			configureIgnoreBeanInfo(environment);
			//打印Banner图 可以参考 to: https://docs.spring.io/spring-boot/docs/2.1.0.RELEASE/reference/htmlsingle/#boot-features-banner
			Banner printedBanner = printBanner(environment);
			//正式创建ApplicationContext  根据之前的web类型判断 创建 ConfigurableApplicationContext的实现类
			context = createApplicationContext();
			//根据 ConfigurableApplicationContext 获取到 ConfigurableApplicationContext相关实例
			exceptionReporters = getSpringFactoriesInstances(
					SpringBootExceptionReporter.class,
					new Class[] { Configurable.ApplicationContext.class }, context);
			//准备context  大致做了以下事情   1.为context设置Environment 2.postProcess 我看到没做什么。。。 3.之前的Initializers启动#initialize()方法 4.listeners.contextPrepared 5.listeners.contextLoaded
			prepareContext(context, environment, listeners, applicationArguments,
					printedBanner);
			//核心中的核心啦 ~_~ ...   1.核心调用org.springframework.context.support.AbstractApplicationContext#refresh() 这部分在下面一起看下  2.启动一个hook 不知道干什么用
			//
			refreshContext(context);
			//空方法什么都没有做
			afterRefresh(context, applicationArguments);
			//现在认为Spring完全启动完毕 打印启动时间
			stopWatch.stop();
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass)
						.logStarted(getApplicationLog(), stopWatch);
			}
			//SpringApplicationRunListener#started()
			listeners.started(context);
			//如果有 ApplicationRunner || CommandLineRunner 相关的bean 启动它
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
		    //如果Spring启动失败 打印信息哈
			handleRunFailure(context, ex, exceptionReporters, listeners);
			throw new IllegalStateException(ex);
		}

		try {
		    //SpringApplicationRunListener#running()
			listeners.running(context);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, null);
			throw new IllegalStateException(ex);
		}
		return context;
	}

4.核心 refresh

	/**
    	 * Load or refresh the persistent representation of the configuration,
    	 * which might an XML file, properties file, or relational database schema.
    	 * <p>As this is a startup method, it should destroy already created singletons
    	 * if it fails, to avoid dangling resources. In other words, after invocation
    	 * of that method, either all or no singletons at all should be instantiated.
    	 * @throws BeansException if the bean factory could not be initialized
    	 * @throws IllegalStateException if already initialized and multiple refresh
    	 * attempts are not supported
         *
	     * 加载或者刷新配置持久化标识 ,涉及到 xml properties  数据库
	     * 如果他是一个启动方法,当他启动失败,应该丢弃已经创建的单例对象,避免对资源占用 换句话说,要不全部启动,如失败,全部销毁?
	     * 这里大部分copy from: https://blog.csdn.net/andy_zhang2007/article/details/78492438
	     **/
	@Override
	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// Prepare this context for refreshing.
			// 为刷新工作做一些当前上下文 context 上的准备工作
			//1.实现类可以做一些自己的prepareRefresh
			//2.AbstractApplicationContext自己定义了关于Properties操作的相关模板
			prepareRefresh();

			// Tell the subclass to refresh the internal bean factory.
			// ApplicationContext 实现了 BeanFactory 接口,但是并非直接作为 Bean 容器。
			// ApplicationContext 中真正直接作为 Bean 容器的是一个内部Bean工厂 BeanFactory,
			// 通过其方法 getBeanFactory() 得到,此方法在 AbstractApplicationContext 中
			// 被声明为 abstract, 其实现要求由实现子类提供。下面的语句 obtainFreshBeanFactory()
			// 内部就是通过调用 getBeanFactory() 获得这个内部 Bean 工厂的。
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// Prepare the bean factory for use in this context.
			// 准备当前上下文使用的Bean容器 BeanFactory,设置其标准上下文特征,比如类加载器等
			// 1. BeanFactory 的类加载器设置为当前上下文的类加载器
			// 2. BeanFactory 的Bean表达式解析器设置为 new StandardBeanExpressionResolver()
			// 3. BeanFactory 增加 BeanPostProcessror new ApplicationListenerDetector(this)
	    	// 4.三个单例Bean被注册 : environment,systemProperties,systemEnvironment
			prepareBeanFactory(beanFactory);

			try {
				// Allows post-processing of the bean factory in context subclasses.
				// 在当前上下文使用的Bean容器BeanFactory的标准初始化完成后对其做一些修改。此时
				// 所有的Bean definition都已经加载但是还没有 Bean 被创建。
				// 当前上下文使用的Bean容器 BeanFactory 的 post process
				// 1.当前上下文是 EmbeddedWebApplicationContext 时,
				// 这个步骤中会对 beanFactory 注册一个 BeanPostProcessor :
				// WebApplicationContextServletContextAwareProcessor
				// 2.当前上下文是 AnnotationConfigEmbeddedWebApplicationContext 时,
				// 如果设置了 basePackages,
				// 这里会使用 AnnotatedBeanDefinitionReader扫描basePackages;
				// 如果设置了 annotatedClasses,
				// 这里会使用 ClassPathBeanDefinitionScanner登记annotatedClasses;
				postProcessBeanFactory(beanFactory);

				// Invoke factory processors registered as beans in the context.
				// 在 beanFactory 上调用 BeanFactoryPostProcessors, 
				// 当前上下文可能会有多个 BeanFactoryPostProcessor 需要应用在 beanFactory 上
				// ****************************************************************
				// 这里需要尤其注意区别 BeanFactoryPostProcessor 和 BeanPostProcessor
				// BeanFactoryPostProcessor : 作用在 Bean定义 上,用来定制修改 Bean定义
				// BeanPostProcessor :作用在 Bean实例 上,用来修改或者包装 Bean实例
				// ****************************************************************
				
				// 该方法实际上将实现委托出去 : 
				// PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors()
				invokeBeanFactoryPostProcessors(beanFactory);

				// Register bean processors that intercept bean creation.
				// 注册 BeanPostProcessor
				// 该步骤实际工作委托给工具类 PostProcessorRegistrationDelegate 的静态方法
				// void registerBeanPostProcessors(
				//   ConfigurableListableBeanFactory beanFactory, 
				//   AbstractApplicationContext applicationContext)
				registerBeanPostProcessors(beanFactory);

				// Initialize message source for this context.
				initMessageSource();

				// Initialize event multicaster for this context.
				// 初始化当前上下文ApplicationContext要使用的 事件多播器 
				// ApplicationEventMulticaster applicationEventMulticaster。
				// 
				// 如果容器中已经注册类型为ApplicationEventMulticaster并且名称为
				// applicationEventMulticaster 的Bean,则直接使用;否则,
				// 新建一个SimpleApplicationEventMulticaster实例并注册到
				// Bean容器,Bean名称使用 applicationEventMulticaster。
				initApplicationEventMulticaster();

				// Initialize other special beans in specific context subclasses.
				// AbstractApplicationContext 中 onRefresh() 方法实现为空,其目的就是
				// 留给实现子类一个机会做一些上下文相关的刷新工作。在一些特殊Bean初始化时,单
				// 例 singleton Bean 初始化之前该方法被调用。
				// 1. 当前上下文是 EmbeddedWebApplicationContext 时,该步骤会创建一个
				// 内置的 Servlet 容器, 具体参考 EmbeddedWebApplicationContext 的
				// 方法 void createEmbeddedServletContainer() 
				onRefresh();

				// Check for listener beans and register them.
				// 1. 将外部指定到当前上下文的 ApplicationListener 实例关联到上下文多播器
				//    Q : 什么时候外部给当前上下文指定 ApplicationListener ?
				//    A : 举例说明,Springboot 应用 SpringApplication 的情况下,是在
				//        prepareContext()结尾时SpringApplicationRunListeners的
				//        contextLoaded() 调用中发生的,此时正在广播事件
				//        ApplicationPreparedEvent 
				// 2. 将实现了 ApplicationListener 接口的所有 Bean 关联到上下文多播器
				// 3. 如果上下文属性earlyApplicationEvents中有要通知的事件,广播出去
				registerListeners();

				// Instantiate all remaining (non-lazy-init) singletons.
				// 完成 BeanFactory 的初始化工作
				// 1.BeanFactory冻结所有的Bean定义:不再可以修改或者做post process操作
				// 2.确保所有的non-lazy-init单例Bean被初始化,也包括FactoryBean
				// 3.如果所初始化的单例Bean实现了接口SmartInitializingSingleton,调用
				//   其方法 afterSingletonsInstantiated()
				finishBeanFactoryInitialization(beanFactory);

				// Last step: publish corresponding event.
				// 1. 初始化生命周期处理器 LifecycleProcessor, 使用已经存在的Bean或者
				//    一个新的DefaultLifecycleProcessor实例;
				// 2. 生命周期处理器 LifecycleProcessor 上传播 refresh 事件
				// 3. 发布事件 ContextRefreshedEvent
				// 4. 如果存在 LiveBeansView MBean 的话,关联到当前上下文
				// 当前上下文是EmbeddedWebApplicationContext的情况下,还会:
				// 5. 启动EmbeddedServletContainer,比如启动内置 tomcat容器
				// 6. 发布事件 EmbeddedServletContainerInitializedEvent
				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();
			}
		}
	}

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

总结

SpringApplication启动分为两步
1.准备阶段
2.启动阶段

准备又分为构造准备与其他Build配置

2.启动阶段也有部分准备配置

大致准备了哪些东西呢

1.Environment相关参数 值 配置文件等
2.Initializers
3.Listener
4.PostProcess

3.核心Refresh启动

SpringApplication的run方法的实现

该方法的主要流程大体可以归纳如下:

1) 如果我们使用的是SpringApplication的静态run方法,那么,这个方法里面首先要创建一个SpringApplication对象实例,然后调用这个创建好的SpringApplication的实例方法。在SpringApplication实例初始化的时候,它会提前做几件事情:

①根据classpath里面是否存在某个特征类(org.springframework.web.context.ConfigurableWebApplicationContext)来决定是否应该创建一个为Web应用使用的ApplicationContext类型。

②使用SpringFactoriesLoader在应用的classpath中查找并加载所有可用的ApplicationContextInitializer。

③使用SpringFactoriesLoader在应用的classpath中查找并加载所有可用的ApplicationListener。

④推断并设置main方法的定义类。

2) SpringApplication实例初始化完成并且完成设置后,就开始执行run方法的逻辑了,方法执行伊始,首先遍历执行所有通过SpringFactoriesLoader可以查找到并加载的SpringApplicationRunListener。调用它们的started()方法,告诉这些SpringApplicationRunListener,“嘿,SpringBoot应用要开始执行咯!”。

3) 创建并配置当前Spring Boot应用将要使用的Environment(包括配置要使用的PropertySource以及Profile)。

4) 遍历调用所有SpringApplicationRunListener的environmentPrepared()的方法,告诉他们:“当前SpringBoot应用使用的Environment准备好了咯!”。

5) 如果SpringApplication的showBanner属性被设置为true,则打印banner。

6) 根据用户是否明确设置了applicationContextClass类型以及初始化阶段的推断结果,决定该为当前SpringBoot应用创建什么类型的ApplicationContext并创建完成,然后根据条件决定是否添加ShutdownHook,决定是否使用自定义的BeanNameGenerator,决定是否使用自定义的ResourceLoader,当然,最重要的,将之前准备好的Environment设置给创建好的ApplicationContext使用。

7) ApplicationContext创建好之后,SpringApplication会再次借助Spring-FactoriesLoader,查找并加载classpath中所有可用的ApplicationContext-Initializer,然后遍历调用这些ApplicationContextInitializer的initialize(applicationContext)方法来对已经创建好的ApplicationContext进行进一步的处理。

8) 遍历调用所有SpringApplicationRunListener的contextPrepared()方法。

9) 最核心的一步,将之前通过@EnableAutoConfiguration获取的所有配置以及其他形式的IoC容器配置加载到已经准备完毕的ApplicationContext。

10) 遍历调用所有SpringApplicationRunListener的contextLoaded()方法。

11) 调用ApplicationContext的refresh()方法,完成IoC容器可用的最后一道工序。

12) 查找当前ApplicationContext中是否注册有CommandLineRunner,如果有,则遍历执行它们。

13) 正常情况下,遍历执行SpringApplicationRunListener的finished()方法、(如果整个过程出现异常,则依然调用所有SpringApplicationRunListener的finished()方法,只不过这种情况下会将异常信息一并传入处理)

单词学习

hierarchical 英 /haɪə’rɑːkɪk(ə)l/ adj. 分层的;等级体系的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值