zl程序教程

您现在的位置是:首页 >  后端

当前栏目

Spring Boot(二)SpringBoot是如何启动Spring容器源码

2023-09-11 14:16:29 时间

SpringApplication run

调用SpringApplication.run启动springboot应用

1 SpringApplication.run(Application.class, args);

2:使用自定义SpringApplication进行启动

public static ConfigurableApplicationContext run(Class<?>[]  primarySources, String[] args) { 
      return new SpringApplication(primarySources).run(args); 
 }

创建SpringApplication new SpringApplication(primarySources)

	public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
		//将启动类放入primarySources
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
		// 根据classpath 下的类,推算当前web应用类型(webFlux, servlet)
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
		// 就是去spring.factories 中去获取所有key:org.springframework.context.ApplicationContextInitializer
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
		//就是去spring.factories 中去获取所有key: org.springframework.context.ApplicationListener
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
	// 根据main方法推算出mainApplicationClass
		this.mainApplicationClass = deduceMainApplicationClass();
	}
	private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
		ClassLoader classLoader = getClassLoader();
		// Use names and ensure unique to protect against duplicates
		Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}

org.springframework.context.ApplicationContextInitializer
在这里插入图片描述
org.springframework.context.ApplicationListener
在这里插入图片描述

总结:1. 获取启动类:根据启动类加载ioc容器2.获取web应用类型
3.spring.factories读取了对外扩展的ApplicationContextInitializer ,ApplicationListener 对外扩展, 对类解耦(比如全局配置文件、热部署插件)
4. 根据main推算出所在的类
就是去初始化了一些信息

SpringBoot 事件监听器发布顺序

1.ApplicationStartingEvent在运行开始时发送,但在进行任何处理之前(侦听器和初始化程序的注册除外)发送。
2.在创建上下文之前,将发送ApplicationEnvironmentPreparedEvent。
3.准备ApplicationContext并调用ApplicationContextInitializers之后,将发送ApplicationContextInitializedEvent。
4.读取完配置类后发送ApplicationPreparedEvent。
5.在刷新上下文之后但在调用任何应用程序和命令行运行程序之前,将发送ApplicationStartedEvent。
6.紧随其后发送带有LivenessState.CORRECT的AvailabilityChangeEvent,以指示该应用程序被视为处于活动状态。
7.在调用任何应用程序和命令行运行程序之后,将发送ApplicationReadyEvent。
8.紧随其后发送ReadabilityState.ACCEPTING_TRAFFIC的AvailabilityChangeEvent,以指示应用程序已准备就绪,可以处理请求。
如果启动时发生异常,则发送ApplicationFailedEvent。

1.ApplicationStartingEvent
在这里插入图片描述
2.ApplicationEnvironmentPreparedEvent
在这里插入图片描述

run方法

这个方法是启动springboot最核心的逻辑

public ConfigurableApplicationContext run(String... args) {
    // 用来记录当前springboot启动耗时
   StopWatch stopWatch = new StopWatch();
   // 就是记录了启动开始时间
   stopWatch.start();
   // 它是任何spring上下文的接口, 所以可以接收任何ApplicationContext实现
   ConfigurableApplicationContext context = null;
   Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
   // 开启了Headless模式:
   configureHeadlessProperty();
   // 去spring.factroies中读取了SpringApplicationRunListener 的组件,  就是用来发布事件或者运行监听器
   SpringApplicationRunListeners listeners = getRunListeners(args);
   // 发布1.ApplicationStartingEvent事件,在运行开始时发送
   listeners.starting();
   try {
       // 根据命令行参数 实例化一个ApplicationArguments 
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
      // 预初始化环境: 读取环境变量,读取配置文件信息(基于监听器)
      ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
      // 忽略beaninfo的bean
      configureIgnoreBeanInfo(environment);
      // 打印Banner 横幅
      Banner printedBanner = printBanner(environment);
      // 根据webApplicationType创建Spring上下文  
      context = createApplicationContext();
      exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
            new Class[] { ConfigurableApplicationContext.class }, context);
      //预初始化spring上下文
      prepareContext(context, environment, listeners, applicationArguments, printedBanner);
      // 加载spring ioc 容器  相当重要 由于是使用AnnotationConfigServletWebServerApplicationContext 启动的spring容器所以springboot对它做了扩展:
      //  加载自动配置类:invokeBeanFactoryPostProcessors ,  创建servlet容器onRefresh
      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;
}

准备环境 prepareEnvironment

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
      ApplicationArguments applicationArguments) {
   // 根据webApplicationType 创建Environment  创建就会读取: java环境变量和系统环境变量
   ConfigurableEnvironment environment = getOrCreateEnvironment();
   // 将命令行参数读取环境变量中
   configureEnvironment(environment, applicationArguments.getSourceArgs());
   // 将@PropertieSource的配置信息 放在第一位, 因为读取配置文件@PropertieSource优先级是最低的
   ConfigurationPropertySources.attach(environment);
   // 发布了ApplicationEnvironmentPreparedEvent 的监听器  读取了全局配置文件
   listeners.environmentPrepared(environment);
   // 将所有spring.main 开头的配置信息绑定SpringApplication
   bindToSpringApplication(environment);
   if (!this.isCustomEnvironment) {
      environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
            deduceEnvironmentClass());
   }
   //更新PropertySources
   ConfigurationPropertySources.attach(environment);
   return environment;
}

预初始化上下文 prepareContext

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
   context.setEnvironment(environment);
   postProcessApplicationContext(context);
   // 拿到之前读取到所有ApplicationContextInitializer的组件, 循环调用initialize方法
   applyInitializers(context);
   // 发布了ApplicationContextInitializedEvent
   listeners.contextPrepared(context);
   if (this.logStartupInfo) {
      logStartupInfo(context.getParent() == null);
      logStartupProfileInfo(context);
   }
   // 获取当前spring上下文beanFactory (负责创建bean)
   ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
   beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
   if (printedBanner != null) {
      beanFactory.registerSingleton("springBootBanner", printedBanner);
   }
   // 在Spring下 如果出现2个重名的bean, 则后读取到的会覆盖前面
   // 在SpringBoot 在这里设置了不允许覆盖, 当出现2个重名的bean 会抛出异常
   if (beanFactory instanceof DefaultListableBeanFactory) {
      ((DefaultListableBeanFactory) beanFactory)
            .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
   }
   // 设置当前spring容器是不是要将所有的bean设置为懒加载
   if (this.lazyInitialization) {
      context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
   }
   // Load the sources
   Set<Object> sources = getAllSources();
   Assert.notEmpty(sources, "Sources must not be empty");
   // 读取主启动类,将它注册为BD、就像我们以前register(启动类);一个意思 (因为后续要根据配置类解析配置的所有bean)
   load(context, sources.toArray(new Object[0]));
   //4.读取完配置类后发送ApplicationPreparedEvent。
   listeners.contextLoaded(context);
}

刷新 调用ServletWebServerApplicationContext refresh 之后就会调用AbstractApplicationContext的refresh方法

	private void refreshContext(ConfigurableApplicationContext context) {
		refresh((ApplicationContext) context);
		if (this.registerShutdownHook) {
			try {
				context.registerShutdownHook();
			}
			catch (AccessControlException ex) {
				// Not allowed in some environments.
			}
		}
	}

总结: 1. 初始化SpringApplication 从spring.factories 读取 listener ApplicationContextInitializer 。
2.运行run方法
3.读取 环境变量 配置信息…
4. 创建springApplication上下文:ServletWebServerApplicationContext
5. 预初始化上下文 : 读取启动类
6.调用refresh 加载ioc容器
加载所有的自动配置类
创建servlet容器

在这个过程中springboot会调用很多监听器对外进行扩展