SpringBoot启动过程深度解析——概述

SpringBoot启动过程深度解析–概述


文章使用 spring 相关版本信息:
spring-boot 2.3.4.RELEASE
spring-core 5.2.9.RELEASE
spring-context 5.2.9.RELEASE
spring-web 5.2.9.RELEASE


前言

spring和springboot有什么关系?
springBoot是基于spring框架开发的,springBoot主要用spring的IOC的流程进行扩展开发

spring框架解决的问题:
	1.IOC 依赖注入和控制反转(把相互依赖的Bean对象,通过注解/xml的配置依赖关系,交给spring帮我们创建、注入和管理这些Bean)
	2.在spring处理Bean对象的各个环节中,提供了很多扩展方法,方便在Bean创建过程执行一些自定义的操作(代理,类型转换,解析自定义注解等等)
	3.是一个运行和部署简单的框架
	
springBoog框架解决的问题:
	1.自动装配 (通过@Configuration / spring.factories文件,都可以加载配置类,用于注册自定义满足条件的Bean)
	2.快速引入依赖,快速使用依赖( 引入对应依赖 spring-boot-starter-XXXX包,一般就能使用了,也是用自动装配的功能实现的)
	3.丰富的依赖包选择

spring和springboot启动的几种方式
spring启动的方式:
	1.通过XXXXXApplicationContext.refresh()启动
	
springBoot启动方式:
	1.通过XXXXXApplicationContext.refresh()启动
	2.通过启动

springboot如何开启自动装配的方式
	1.在启动类/配置类 使用注解@EnableAutoConfiguration(通常通过组合注解使用@SpringBootApplication)

一、SpringBoot启动过程深度解析

接下来的文章就以SpringApplication.run的启动方式讲解
@SpringBootApplication
public class Main {
    public static void main(String[] args) {
        new SpringApplication(Main.class).run();
    }
}

SpringApplication.run启动的主要脉络流程图:

SpringApplication.run启动的主要脉络流程图

1.SpringApplication的创建:
SpringApplication对象创建过程,只加载一些简单的配置:
	1.根据环境计算当前应该创建的Web类型
	2.从"META-INF/spring.factories"读取ApplicationContextInitializer类的实例
	3.从"META-INF/spring.factories"读取ApplicationListener类的实例
	4.通过当前调用栈,获取Main方法所在类
/**
	可以通过此类快速启动一个spring应用,可以传入一些加载的配置类来引导启动
**/
public class 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));
		
		//根据环境计算当前应该创建的Web类型
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
		
        /**
        	 解析这些ApplicationContextInitializer的作用请看【详细链接:】
	         设置应用上线文初始化器,从"META-INF/spring.factories"读取ApplicationContextInitializer类的实例。(默认一共7个)
	         org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer
	         org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
	         org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer
	         org.springframework.boot.context.ContextIdApplicationContextInitializer
	         org.springframework.boot.context.config.DelegatingApplicationContextInitializer
	         org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer
	         org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
        **/
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
		
        /**
            解析这些ApplicationListener的作用请看【详细链接:】
            设置监听器,从"META-INF/spring.factories"读取ApplicationListener类的实例。(一共11个)
            org.springframework.boot.autoconfigure.BackgroundPreinitializer
            org.springframework.boot.ClearCachesApplicationListener
            org.springframework.boot.builder.ParentContextCloserApplicationListener
            org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor
            org.springframework.boot.context.FileEncodingApplicationListener
            org.springframework.boot.context.config.AnsiOutputApplicationListener
            org.springframework.boot.context.config.ConfigFileApplicationListener
            org.springframework.boot.context.config.DelegatingApplicationListener
            org.springframework.boot.context.logging.ClasspathLoggingApplicationListener
            org.springframework.boot.context.logging.LoggingApplicationListener
            org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
        **/
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		
		//推断主入口应用类,通过当前调用栈,获取Main方法所在类,并赋值给mainApplicationClass
		this.mainApplicationClass = deduceMainApplicationClass();
	}
	
}

2.SpringApplication.run的执行:
1.创建并启动计时监控StopWatch
2.设置系统属性“java.awt.headless”的值【建议在缺少显示屏、键盘或者鼠标的应用开启】
3.从SpringFactories文件加载SpringApplicationRunListener实现类
4.发布事件:ApplicationStartingEvent
5.解析run方法入参命令【类似于 --foo=bar 】
6.准备环境变量,发布事件:ApplicationEnvironmentPreparedEvent
7.设置参数spring.beaninfo.ignore默认TRUE
8.打印Banner
9.根据不同的应用类型初始化不同的上下文类Context
10.从SpringFactories文件加载SpringBootExceptionReporter异常报告器
11.加载BeanDefinition【包括自动装配的扫描】
12.执行spring的标准初始化过程【AbstractApplicationContext.refresh() 】
13.应用上下文刷新后置处理,做一些扩展功能
14.停止计时监控
15.发布应用上下文启动监听事件 ApplicationStartedEvent
16.执行所有的Runner运行器
17.发布应用上下文就绪事件 ApplicationReadyEvent
public ConfigurableApplicationContext run(String... args) {
    //开启记录启动过程的秒表器
    //允许对多个任务进行计时,公开每个命名任务的总运行时间和运行时间,从Spring Framework 5.2开始,运行时间以纳秒为单位进行跟踪和报告
  	StopWatch stopWatch = new StopWatch();

    //秒表器开始计时
  	stopWatch.start();

    //声明spring容器
  	ConfigurableApplicationContext context = null;
    //声明spring异常报告器
  	Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();

    // 设置java.awt.headless变量
    // 1、如果设置headless模式为true,则程序是无显示器模式
    // 2、为了提高计算效率和适配性我们可以使用这种模式,关闭图形显示等功能可以大大节省设备的计算能力
    // 3、通常B/S型Web应用运行于无显示设备、驱动的服务器端环境中,但是有使用AWT绘图接口的可能(例如:用Images、Fonts接口生成图片验证码)
    //    所以给Tomcat、Weblogic附加参数-Djava.awt.headless=true,强制使用Headless版本的AWT实现类,就能避免图形环境缺失所导致的程序出错
  	configureHeadlessProperty();
  
    //读取META-INF/spring.factories接口SPI的实现类配置,初始化SpringApplicationRunListener实现类
    //SpringApplicationRunListener是springApplication启动过程的监听器
  	SpringApplicationRunListeners listeners = getRunListeners(args);

    //SpringApplicationRunListener发布开始启动事件
  	listeners.starting();
  	try {
      //解析启动命令封装到 applicationArguments对象
  		ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);

      //初始化所有环境变量,SpringApplicationRunListener发布环境准备完成事件【详细链接...】
  		ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);

      /**设置spring.beaninfo.ignore
          1.如果启动过程不需要对Bean信息的重复访问(Introspector.getBeanInfo(Class)调用),请设置为true,否则启动过程将变得昂贵**/
  		configureIgnoreBeanInfo(environment);

      //打印Banner【详细链接...】
  		Banner printedBanner = printBanner(environment);

      /**根据web类型创建对应的ConfigurableApplicationContext
      !!!父类AnnotationConfigServletWebServerApplicationContext无参构造会做以下两件事
        ** 1.创建一个注解BeanDefinition读取器AnnotatedBeanDefinitionReader并且注册一些用于注册Bean的自带增强器到BeanDefinitionRegistry中
            -- 1.DefaultListableBeanFactory设置依赖顺序比较器:AnnotationAwareOrderComparator
            -- 2.DefaultListableBeanFactory设置是否能注入当前Bean的判断器:ContextAnnotationAutowireCandidateResolver
            -- 3.注入Bean:ConfigurationClassPostProcessor
            -- 4.注入Bean:AutowiredAnnotationBeanPostProcessor
            -- 5.如果满足JSR-250规范的话(判断是否存在javax.annotation.Resource),注入Bean:CommonAnnotationBeanPostProcessor
            -- 6.如果满足(判断是否存在javax.persistence.EntityManagerFactory),注入Bean:PersistenceAnnotationBeanPostProcessor
            -- 7.注入Bean:EventListenerMethodProcessor
            -- 8.注入Bean:DefaultEventListenerFactory
        ** 2.创建一个类路径BeanDefinition扫描器ClassPathBeanDefinitionScanner并且添加一些需要被创建Bean的注解到ClassPathBeanDefinitionScanner的需要的过滤器中
            -- 本步骤主要是添加过滤器,对 includeFilters 赋值。 注册过滤器 @Component,@Controller @Service、 @Repository 也会被添加进去。也会注册添加 JSR-250 的 @ManagedBean 和 JSR-330 的 @Named 注解。**/
  		context = createApplicationContext();

      //通过META-INF/spring.factories初始化SpringBootExceptionReporter实现类
  		exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
  				new Class[] { ConfigurableApplicationContext.class }, context);

      /**准备和填充ApplicationContext上下文属性【详细链接...】
      1. 设置环境对象
      2. 增强上下文
         ** 1.如果有配置,则注册BeanName构造器Bean对象
         ** 2.如果有配置,则设置资源加载器resourceLoader对象
         ** 3.如果有配置,则设置属性ConversionService
      3. 执行所有ApplicationContextInitializer实现类的initialize方法【详细链接...】
      4. SpringApplicationRunListener发布上下文准备完成事件
      5. 打印profile环境
      6. 注册单例Bean:springApplicationArguments
      7. 注册单例Bean:springBootBanner
      8. 如果是DefaultListableBeanFactory,设置是否允许BeanDefinition重写
      9. 是否懒加载,是的话注册一个BeanFactory增强处理器LazyInitializationBeanFactoryPostProcessor
      10. 获取应用程序源【应用程序源:用于创建应用程序上下文的其他源。源可以是:类名、包名或 XML 资源位置】
      11. 根据应用程序源, 用BeanDefinitionLoader进行加载程序源的BeanDefinition【详细链接...】
          ** 程序源是类: AnnotatedBeanDefinitionReader#doRegisterBean通过注解进行加载BeanDefinition
          ** 1.根据程序源类@Conditional 判断是否跳过此程序源的扫描
          ** 2.程序源的abd设置InstanceSupplier(创建Bean实例的回调)
          ** 3.程序源的abd设置程序源类@Scope配置的Bean作用域,代理模式(默认单例模式+无代理)
          ** 4.扫描程序源类上的直接注解,按照注解设置Bean别名
          ** 5.处理程序源类上的公共注解AnnotationConfigUtils.processCommonDefinitionAnnotations(abd)
              -- 1.是否懒加载@Lazy
              -- 2.是否@Primary
              -- 3.是否依赖加载@DependsOn
              -- 4.设置角色@Role
              -- 5.设置描述@Description
          ** 6.执行BeanDefinitionCustomizer实现类的customize方法(用于自定义给定Bean定义的回调,用于扩展)
          ** 7.包装abd成BeanDefinitionHolder对象 //带有名称和别名的 BeanDefinition 的持有者
          ** 8.根据@Scope代理方式,进行BeanDefinition的修改定义
          ** 9.注册程序源的BeanDefinition到BeanDefinitionRegistry
      12. SpringApplicationRunListener发布上下文加载完成事件**/
  		prepareContext(context, environment, listeners, applicationArguments, printedBanner);

      /** 刷新容器refreshContext【Spring容器启动公共逻辑!!重要】【详细链接...】
          1.此处额外执行操作:  将创建一个钩子线程SpringContextShutdownHook,注册到Java虚拟机的关闭挂钩线程集合中,用于java虚拟机关闭时调用执行关闭spring容器方法
          (org.springframework.context.support.AbstractApplicationContext#doClose)**/
  		refreshContext(context);

      //刷新容器之后执行的扩展方法afterRefresh【详细链接...】
  		afterRefresh(context, applicationArguments);

      //stopWatch秒表停止记录spring启动过程
  		stopWatch.stop();
    
  		if (this.logStartupInfo) {
      //打印spring已经启动的日志包含启动事件
  			new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
  		}

    //SpringApplicationRunListener发布spring已经启动完成的事件
  		listeners.started(context);

    //执行一些运行器: ApplicationRunner实现类和 CommandLineRunner实现类【扩展点】
  		callRunners(context, applicationArguments);
  	}
  	catch (Throwable ex) {
  		handleRunFailure(context, ex, exceptionReporters, listeners);
  		throw new IllegalStateException(ex);
  	}

  	try {
    //SpringApplicationRunListener发布spring正在运行的事件
  		listeners.running(context);
  	}
  	catch (Throwable ex) {
  		handleRunFailure(context, ex, exceptionReporters, null);
  		throw new IllegalStateException(ex);
  	}
  	return context;
  }

3.AbstractApplicationContext.refresh的执行:
1.容器刷新前的准备工作(记录启动时间、容器状态、环境变量对象、准备监听器和事件的集合对象)
2.创建容器对象:DefaultListableBeanFactory
3.beanFactory的准备工作,对各种属性进行填充
4.beanFactory扩展工作
5.调用各种beanFactory处理器
6.注册bean处理器
7.为上下文初始化message源,即不同语言的消息体,国际化处理
8.初始化事件监听多路广播器
【Web容器的创建】
9.在所有注册的bean中查找listener bean,注册到消息广播器中
10.初始化剩下的单实例(非懒加载的)
11.完成刷新过程,通知生命周期处理器lifecycleProcessor刷新过程,同时发出ContextRefreshEvent事件
12.最终清除一些缓存
		public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// Prepare this context for refreshing.
			/**
			 * 容器刷新前的准备工作(记录启动时间、容器状态、环境变量对象、准备监听器和事件的集合对象)
			 */
			prepareRefresh();

			// Tell the subclass to refresh the internal bean factory.
			// 创建容器对象:DefaultListableBeanFactory
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// Prepare the bean factory for use in this context.
			// beanFactory的准备工作,对beanFactory各种属性进行填充
			prepareBeanFactory(beanFactory);

			try {
				// Allows post-processing of the bean factory in context subclasses.
				// 子类覆盖方法做额外的处理,子类扩展方法
				postProcessBeanFactory(beanFactory);

				// Invoke factory processors registered as beans in the context.
				// 调用各种beanFactory处理器
				invokeBeanFactoryPostProcessors(beanFactory);

				// Register bean processors that intercept bean creation.
				// 注册bean处理器,这里只是注册功能,真正调用的是getBean方法
				registerBeanPostProcessors(beanFactory);

				// Initialize message source for this context.
				// 为上下文初始化message源,即不同语言的消息体,国际化处理
				initMessageSource();

				// Initialize event multicaster for this context.
				// 初始化事件监听多路广播器
				initApplicationEventMulticaster();

				// Initialize other special beans in specific context subclasses.
				// 留给子类来初始化其他的bean
				// 【Web容器的创建】
				onRefresh();

				// Check for listener beans and register them.
				// 在所有注册的bean中查找listener bean,注册到消息广播器中
				registerListeners();

				// Instantiate all remaining (non-lazy-init) singletons.
				// 初始化剩下的单实例(非懒加载的)
				finishBeanFactoryInitialization(beanFactory);

				// Last step: publish corresponding event.
				// 完成刷新过程,通知生命周期处理器lifecycleProcessor刷新过程,同时发出ContextRefreshEvent通知别人
				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.
				// 为防止bean资源占用,在异常处理中,销毁已经在前面过程中生成的单件bean
				destroyBeans();

				// Reset 'active' flag.
				// 重置active标志
				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();
			}
		}
	}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值