引用

Spring 初始化

Spring 自动配置

  1. @Configuration&与@Bean------>>>基于java代码的bean配置
  2. @Conditional-------->>>>>>设置自动配置条件依赖
  3. @EnableConfigurationProperties与@ConfigurationProperties->读取配置文件转换为bean。
  4. @EnableAutoConfiguration、@AutoConfigurationPackage与@Import->实现bean发现与加载。

Spring Boot 常用条件注入注解

  • @ConditionalOnBean,仅在当前上下文中存在某个bean时,才会实例化这个Bean。
  • @ConditionalOnClass,某个class位于类路径上,才会实例化这个Bean。
  • @ConditionalOnExpression,当表达式为true的时候,才会实例化这个Bean。
  • @ConditionalOnMissingBean,仅在当前上下文中不存在某个bean时,才会实例化这个Bean。
  • @ConditionalOnMissingClass,某个class在类路径上不存在的时候,才会实例化这个Bean。
  • @ConditionalOnNotWebApplication,不是web应用时才会实例化这个Bean。
  • @AutoConfigureAfter,在某个bean完成自动配置后实例化这个bean。
  • @AutoConfigureBefore,在某个bean完成自动配置前实例化这个bean。

Spring 启动流程(SpringApplication(primarySources))

TIM截图20201122215539.png
进入SpringApplication(primarySources)
TIM截图20201122204217.png

  1. 新建一个空的resourceLoader(传入是null)。
this.resourceLoader = resourceLoader;

resourceLoader是用来帮助(策略)加载资源的。所谓的资源就是某一个文件,只是随着加载策略的不同,读取的文件的路径也不同,默认实现是DefaultResourceLoader。常用的有:

  • ClassPathResource:通过 ClassPathResource 以类路径的方式进行访问;
  • FileSystemResource:通过 FileSystemResource 以文件系统绝对路径的方式进行访问;
  • ServletContextResource:通过 ServletContextResource 以相对于Web应用根目录的方式进行访问;
  • UrlResource :通过java.net.URL来访问资源,当然它也支持File格式,如“file:”。
  1. 断言主要加载资源类不能为 null,否则报错
Assert.notNull(primarySources, "PrimarySources must not be null");
  1. 初始化主要加载资源类集合并去重
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
  1. 推断当前 WEB 应用类型
this.webApplicationType = WebApplicationType.deduceFromClasspath();

根据类路径下是否有对应项目类型的类推断出不同的应用类型,WebApplicationType有(NONE,SERVLET,REACTIVE)三种。

  1. 设置应用上下文(ApplicationContext)初始化器
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));

这里详细说一下getSpringFactoriesInstances,这里采用案例代码

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
		ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
		// 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;
	}
  1. 获取当前线程上下文类加载器
  2. 获取 应用上下文初始化器 的实例名称集合并去重
  3. 根据以上类路径创建初始化器实例列表
  4. 初始化器实例列表排序
  5. 返回实例对象

  1. 设置监听器
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

ApplicationListener接口继承了 JDK 的 java.util.EventListener 接口,实现了观察者模式,它一般用来定义感兴趣的事件类型,事件类型限定于 ApplicationEvent的子类,这同样继承了 JDK 的 java.util.EventObject 接口。

  1. 推断主入口应用类
this.mainApplicationClass = deduceMainApplicationClass();

通过构造一个运行时异常,再遍历异常栈中的方法名,获取方法名为 main 的栈帧,从来得到入口类的名字再返回该类。

Spring 启动流程(run(args))

public ConfigurableApplicationContext run(String... args) {
		// 1、创建并启动计时监控类
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		
		// 2、初始化应用上下文和异常报告集合
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		
		// 3、设置系统属性 `java.awt.headless` 的值,默认值为:true
		configureHeadlessProperty();
		
		// 4、创建所有 Spring 运行监听器并发布应用启动事件
		SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.starting();
		try {
			
			// 5、初始化默认应用参数类
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
			
			// 6、根据运行监听器和应用参数来准备 Spring 环境
			ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
			configureIgnoreBeanInfo(environment);
			
			// 7、创建 Banner 打印类
			Banner printedBanner = printBanner(environment);
			
			// 8、创建应用上下文
			context = createApplicationContext();
			
			// 9、准备异常报告器
			exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
			
			// 10、准备应用上下文
			prepareContext(context, environment, listeners, applicationArguments, printedBanner);
			
			// 11、刷新应用上下文
			refreshContext(context);
			
			// 12、应用上下文刷新后置处理
			afterRefresh(context, applicationArguments);
			
			 // 13、停止计时监控类
			stopWatch.stop();
			
			// 14、输出日志记录执行主类名、时间信息
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
			}
			
			// 15、发布应用上下文启动完成事件
			listeners.started(context);
			
			// 16、执行所有 Runner 运行器
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, listeners);
			throw new IllegalStateException(ex);
		}

		try {
			
			// 17、发布应用上下文就绪事件
			listeners.running(context);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, null);
			throw new IllegalStateException(ex);
		}
		// 18、返回应用上下文
		return context;
	}

整理一下

SpringApplication(primarySources)

  1. 资源初始化资源加载器为 null
  2. 断言主要加载资源类不能为 null,否则报错
  3. 初始化主要加载资源类集合并去重
  4. 推断当前 WEB 应用类型
  5. 设置应用上下文初始化器
  6. 设置监听器
  7. 推断主入口应用类

run(args)

  1. 创建并启动计时监控类
  2. 初始化应用上下文和异常报告集合
  3. 设置系统属性 java.awt.headless 的值,默认值为:true
  4. 创建所有 Spring 运行监听器并发布应用启动事件
  5. 初始化默认应用参数类
  6. 根据运行监听器和应用参数来准备 Spring 环境
  7. 创建 Banner 打印类
  8. 创建应用上下文
  9. 准备异常报告器
  10. 准备应用上下文
  11. 刷新应用上下文
  12. 应用上下文刷新后置处理
  13. 停止计时监控类
  14. 输出日志记录执行主类名、时间信息
  15. 发布应用上下文启动完成事件
  16. 执行所有 Runner 运行器
  17. 发布应用上下文就绪事件
  18. 返回应用上下文