• 从 SpringApplication 认识 Spring 应用启动过程


    一、SpringApplication 是什么?

    Spring 应用的启动类。

    二、SpringApplication 执行了什么?

    • 创建 ApplicationContext 实例

      ApplicationContext 就是我们所说的容器实例。

    • 注册 CommandLinePropertySource

      CommandLinePropertySource 的作用是将命令行参数输出为 Spring 属性。

    • 刷新 ApplicationContext

      这一步骤包括诸多操作,并且会加载所有的单例 bean。

    • 触发 CommandLineRunner bean

      CommandLineRunner 是一个接口,它只有一个 run() 方法。

      凡是实现了此接口的类,如果被加载进容器,就会执行其 run() 方法。

      容器中可以包含多个实现 CommandLineRunner 的 bean,执行顺序可以遵从 Ordered 接口或者 @Order 注解设置。

    三、bean 加载源

    SpringApplication 有诸多 bean 加载源:

    • AnnotatedBeanDefinitionReader

      顾名思义,注解 bean 定义读取。

    • XmlBeanDefinitionReader

      xml 配置资源读取。

    • ClassPathBeanDefinitionScanner

      classpath 路径扫描。

    • GroovyBeanDefinitionReader

      ... ...

    四、SpringApplication 创建

    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();
    	this.bootstrapRegistryInitializers = new ArrayList<>(
    			getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
    	setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    	setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    	this.mainApplicationClass = deduceMainApplicationClass();
    }
    
    

    1、resourceLoader

    参数可以为 null,为 null 时,使用默认:

    (this.resourceLoader != null) ? this.resourceLoader: new DefaultResourceLoader(null);
    

    2、primarySources

    主要的 bean 定义来源。

    3、webApplicationType

    web 应用类型判断:

    • NONE:应用不会以 web 应用运行,且不会启动内嵌 web 服务器。

    • SERVLET:基于 servlet web 应用,运行于内嵌 web 服务器。

    • REACTIVE:响应式 web 应用,运行于内嵌 web 服务器。

    4、bootstrapRegistryInitializers

    BootstrapRegistryInitializer:回调接口,用于 BootstrapRegistry 初始化。

    BootstrapRegistry:对象注册器,作用期间为从应用启动,Environment 处理直到 ApplicationContext 完备。

    5、setInitializers

    ApplicationContextInitializer 列表设置。

    ApplicationContextInitializer:回调接口,用于 Spring ConfigurableApplicationContext 初始化。

    通常用于 web 应用 ApplicationContext 自定义初始化。如注册 property source、激活 profile 等。

    6、setListeners

    ApplicationListener 列表设置。

    ApplicationListener:应用事件监听接口,基于标准的 EventListener 接口,观察者模式实现。

    7、mainApplicationClass

    main class

    五、SpringApplication.run() 逻辑

    	public ConfigurableApplicationContext run(String... args) {
    		long startTime = System.nanoTime();
    		DefaultBootstrapContext bootstrapContext = createBootstrapContext();
    		ConfigurableApplicationContext context = null;
    		configureHeadlessProperty();
    		SpringApplicationRunListeners listeners = getRunListeners(args);
    		listeners.starting(bootstrapContext, this.mainApplicationClass);
    		try {
    			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
    			ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
    			configureIgnoreBeanInfo(environment);
    			Banner printedBanner = printBanner(environment);
    			context = createApplicationContext();
    			context.setApplicationStartup(this.applicationStartup);
    			prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
    			refreshContext(context);
    			afterRefresh(context, applicationArguments);
    			Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
    			if (this.logStartupInfo) {
    				new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
    			}
    			listeners.started(context, timeTakenToStartup);
    			callRunners(context, applicationArguments);
    		}
    		catch (Throwable ex) {
    			handleRunFailure(context, ex, listeners);
    			throw new IllegalStateException(ex);
    		}
    		try {
    			Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
    			listeners.ready(context, timeTakenToReady);
    		}
    		catch (Throwable ex) {
    			handleRunFailure(context, ex, null);
    			throw new IllegalStateException(ex);
    		}
    		return context;
    	}
    

    创建,刷新 ApplicationContext 并运行 Spring 应用。

    1、startTime

    使用 System.nanoTime(),计算耗时间隔更精确。不可用于获取具体时刻。

    2、创建启动上下文

    DefaultBootstrapContext = createBootstrapContext();

    BootstrapContext:启动上下文,生命周期同 BootstrapRegistry。

    DefaultBootstrapContext 继承了 BootstrapContext、BootstrapRegistry。

    用于 BootstrapRegistry 初始化。

    3、ConfigurableApplicationContext

    可配置的 ApplicationContext。

    4、获取事件监听器

    SpringApplicationRunListeners = getRunListeners()。

    Spring 应用运行期间事件监听。

    5、starting 事件

    listeners.starting():starting step。

    6、启动参数处理

    ApplicationArguments:提供 SpringApplication 启动参数访问。

    7、应用环境配置

    ConfigurableEnvironment = prepareEnvironment()

    	private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
    			DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
    		// Create and configure the environment
    		ConfigurableEnvironment environment = getOrCreateEnvironment();
    		configureEnvironment(environment, applicationArguments.getSourceArgs());
    		ConfigurationPropertySources.attach(environment);
    		listeners.environmentPrepared(bootstrapContext, environment);
    		DefaultPropertiesPropertySource.moveToEnd(environment);
    		Assert.state(!environment.containsProperty("spring.main.environment-prefix"),
    				"Environment prefix cannot be set via properties.");
    		bindToSpringApplication(environment);
    		if (!this.isCustomEnvironment) {
    			EnvironmentConverter environmentConverter = new EnvironmentConverter(getClassLoader());
    			environment = environmentConverter.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
    		}
    		ConfigurationPropertySources.attach(environment);
    		return environment;
    	}
    
    • configureEnvironment() 模板方法,代理执行 configurePropertySources() 及 configureProfiles() 方法。

      configurePropertySources():PropertySource 配置,用于添加、移除或者调序 PropertySource 资源。CommandLinePropertySource 在这一步处理。

      configureProfiles():应用 profile 设置。

    • ConfigurationPropertySources.attach(environment)

      ConfigurationPropertySources:提供对 ConfigurationPropertySource 的访问。

      attach(environment):就是将这个功能提供给 environment。

    • listeners.environmentPrepared(bootstrapContext, environment)

      environment-prepared step。

    • DefaultPropertiesPropertySource.moveToEnd(environment)

      DefaultPropertiesPropertySource:是一个 MapPropertySource,包含 SpringApplication 可以使用的一些默认属性。为了使用方便,默认会置于尾序。

    • bindToSpringApplication(environment)

      将 environment 绑定到 SpringApplication。

      Binder:用于对象绑定的容器。

    8、configureIgnoreBeanInfo()

    9、打印 Banner

    printBanner()。

    10、创建 ApplicationContext

    createApplicationContext()。

    内部通过 ApplicationContextFactory 创建。

    ApplicationContextFactory:策略接口,默认实现为 DefaultApplicationContextFactory。

    11、ApplicationStartup 设置

    为容器设置 ApplicationStartup,用于记录启动过程性能指标。

    12、ApplicationContext 准备

    prepareContext()

    private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
    			ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
    			ApplicationArguments applicationArguments, Banner printedBanner) {
    		context.setEnvironment(environment);
    		postProcessApplicationContext(context);
    		applyInitializers(context);
    		listeners.contextPrepared(context);
    		bootstrapContext.close(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 AbstractAutowireCapableBeanFactory) {
    			((AbstractAutowireCapableBeanFactory) beanFactory).setAllowCircularReferences(this.allowCircularReferences);
    			if (beanFactory instanceof DefaultListableBeanFactory) {
    				((DefaultListableBeanFactory) beanFactory)
    						.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
    			}
    		}
    		if (this.lazyInitialization) {
    			context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
    		}
    		context.addBeanFactoryPostProcessor(new PropertySourceOrderingBeanFactoryPostProcessor(context));
    		// Load the sources
    		Set sources = getAllSources();
    		Assert.notEmpty(sources, "Sources must not be empty");
    		load(context, sources.toArray(new Object[0]));
    		listeners.contextLoaded(context);
    	}
    
    
    • 设置环境

    • postProcessApplicationContext() 前置处理

      beanNameGenerator 设置,用于 bean 名称生成。

      resourceLoader 设置,用于资源加载。

      addConversionService:ConversionService 类型转换 Service。

    • applyInitializers()

      ApplicationContextInitializer 应用

    • contextPrepared 事件

      【spring.boot.application.context-prepared】step

    • BootstrapContext 关闭

    • 注册 springApplicationArguments bean

    • 注册 springBootBanner bean

    • AbstractAutowireCapableBeanFactory

      设置是否允许 bean 之间的循环依赖,并自动处理,默认为 true。

      设置是否允许 bean 定义覆盖,默认为 true。

    • lazyInitialization 懒加载

      设置 LazyInitializationBeanFactoryPostProcessor post-processor。

    • PropertySource 重排序

      设置 PropertySourceOrderingBeanFactoryPostProcessor post-processor。

    • getAllSources() bean 定义源加载

    • load() bean 定义加载,BeanDefinitionLoader

      用于从底层资源加载 bean 定义信息,包括 xml、JavaConfig。

      是基于 AnnotatedBeanDefinitionReader、XmlBeanDefinitionReader、ClassPathBeanDefinitionScanner 的门面模式。

      beanNameGenerator、resourceLoader、environment 设置。

      资源加载:

      private void load(Object source) {
        	Assert.notNull(source, "Source must not be null");
        	if (source instanceof Class) {
        		load((Class) source);
        		return;
        	}
        	if (source instanceof Resource) {
        		load((Resource) source);
        		return;
        	}
        	if (source instanceof Package) {
        		load((Package) source);
        		return;
        	}
        	if (source instanceof CharSequence) {
        		load((CharSequence) source);
        		return;
        	}
        	throw new IllegalArgumentException("Invalid source type " + source.getClass());
        }
      
    • contextLoaded() contextLoaded 事件

      【spring.boot.application.context-loaded】step。

    13、ApplicationContext 刷新

    refreshContext()

    注册 shutdownHook。

    Runtime.getRuntime().addShutdownHook(new Thread(this, "SpringApplicationShutdownHook"));
    

    刷新操作:加载或刷新

    AbstractApplicationContext::refresh()
    
    public void refresh() throws BeansException, IllegalStateException {
    		synchronized (this.startupShutdownMonitor) {
    			StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
    
    			// 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);
    
    				StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
    				// Invoke factory processors registered as beans in the context.
    				invokeBeanFactoryPostProcessors(beanFactory);
    
    				// Register bean processors that intercept bean creation.
    				registerBeanPostProcessors(beanFactory);
    				beanPostProcess.end();
    
    				// 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();
    				contextRefresh.end();
    			}
    		}
    	}
    

    作为启动方法,如果失败,则必须销毁所有已创建的单例bean。

    • StartupStep【spring.context.refresh】

    • 准备刷新 prepareRefresh()

      设置启动日期。

      设置 active 标志。

      initPropertySources():子类实现 PropertySource 初始化。

      validateRequiredProperties():校验 ConfigurablePropertyResolver#setRequiredProperties 设置的必需属性。

      obtainFreshBeanFactory():通过子类获取最新的内部 bean factory。如果存在旧的则先销毁,然后再创建新的返回。

    • prepareBeanFactory() 准备 bean factory

      setBeanClassLoader():默认为线程上下文类加载器,用于 bean 定义加载。

      setBeanExpressionResolver() spel 表达式解析设置:StandardBeanExpressionResolver。

      addPropertyEditorRegistrar():ResourceEditorRegistrar 用于 bean 创建过程。

      添加 ApplicationContextAwareProcessor post-processor。

      注册依赖:BeanFactory、ResourceLoader、ApplicationEventPublisher、ApplicationContext。

      添加 ApplicationListenerDetector post-processor:用于检测发现实现了 ApplicationListener 的 bean。

      LoadTimeWeaver 处理。

      environment、systemProperties、systemEnvironment、applicationStartup 注册。

    • postProcessBeanFactory():用于子类实现,修改内部 bean factory。

      这一时期,所有的 bean 定义都已被加载,但还未实例化。

    • StartupStep【spring.context.beans.post-process】

    • invokeBeanFactoryPostProcessors() 触发所有已注册的 BeanFactoryPostProcessor

    • registerBeanPostProcessors() 注册 bean post-processor

    • StartupStep【spring.context.beans.post-process】 结束

    • initMessageSource() MessageSource 初始化

      容器内 bean 名称:messageSource。

      存在则检查并设置 ParentMessageSource。

      不存在则创建默认 DelegatingMessageSource,设置 ParentMessageSource 并注册。

    • initApplicationEventMulticaster() 事件分发初始化

      容器 bean:applicationEventMulticaster。ApplicationEventMulticaster 接口,用于管理 ApplicationListener,并执行事件分发。

      不存在则创建并注册 SimpleApplicationEventMulticaster 对象。

    • onRefresh()

      用于子类初始化一些特有的 bean。

      模板方法,用于重写实现刷新逻辑。

    • registerListeners() 监听器注册

      将实现了 ApplicationListener 接口的 bean 注册到容器。

    • finishBeanFactoryInitialization() 实例化所有余下的单例 bean。

      conversionService。

      注册内嵌值(${...})解析器。

      初始化 LoadTimeWeaverAware。

      停用类型匹配 ClassLoader。

      freezeConfiguration() 冻结所有的 bean 定义。所有注册的 bean 定义都不允许再有变更。

      preInstantiateSingletons() 实例化所有余下的单例 bean。

    14、afterRefresh()

    ApplicationContext 刷新完毕后调用。

    15、StartupInfoLogger

    记录应用启动信息。

    16、started 事件

    listeners.started()

    17、Runner 调用

    包括 ApplicationRunner 和 CommandLineRunner。

    18 ready 事件

    listeners.ready()

  • 相关阅读:
    中文编程工具开发软件实际案例:酒店饭店餐饮点餐管理系统软件编程实例
    数据库的基本操作(3)
    【无标题】
    Vue的devtools安装教程
    【flask入门系列】Cookie与Session
    educoder_python:4-1-逻辑控制(if)第2关:求解一元二次方程组
    车载以太网物理层SerDes
    LeetCode通关:连刷十四题,回溯算法完全攻略
    RocketMQ的架构及概念
    点云目标检测——pointpillars环境配置与训练
  • 原文地址:https://www.cnblogs.com/niejunlei/p/17479268.html