引用
- https://juejin.cn/post/6844903886990344200
- https://juejin.cn/post/6844903893839642637
- https://juejin.cn/post/6844903902186323981
Spring 初始化
Spring 自动配置
- @Configuration&与@Bean------>>>基于java代码的bean配置
- @Conditional-------->>>>>>设置自动配置条件依赖
- @EnableConfigurationProperties与@ConfigurationProperties->读取配置文件转换为bean。
- @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))
进入SpringApplication(primarySources)
- 新建一个空的resourceLoader(传入是null)。
this.resourceLoader = resourceLoader;
resourceLoader是用来帮助(策略)加载资源的。所谓的资源就是某一个文件,只是随着加载策略的不同,读取的文件的路径也不同,默认实现是DefaultResourceLoader。常用的有:
- ClassPathResource:通过 ClassPathResource 以类路径的方式进行访问;
- FileSystemResource:通过 FileSystemResource 以文件系统绝对路径的方式进行访问;
- ServletContextResource:通过 ServletContextResource 以相对于Web应用根目录的方式进行访问;
- UrlResource :通过java.net.URL来访问资源,当然它也支持File格式,如“file:”。
- 断言主要加载资源类不能为 null,否则报错
Assert.notNull(primarySources, "PrimarySources must not be null");
- 初始化主要加载资源类集合并去重
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
- 推断当前 WEB 应用类型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
根据类路径下是否有对应项目类型的类推断出不同的应用类型,WebApplicationType有(NONE,SERVLET,REACTIVE)三种。
- 设置应用上下文(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;
}
- 获取当前线程上下文类加载器
- 获取 应用上下文初始化器 的实例名称集合并去重
- 根据以上类路径创建初始化器实例列表
- 初始化器实例列表排序
- 返回实例对象
- 设置监听器
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
ApplicationListener接口继承了 JDK 的 java.util.EventListener 接口,实现了观察者模式,它一般用来定义感兴趣的事件类型,事件类型限定于 ApplicationEvent的子类,这同样继承了 JDK 的 java.util.EventObject 接口。
- 推断主入口应用类
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)
- 资源初始化资源加载器为 null
- 断言主要加载资源类不能为 null,否则报错
- 初始化主要加载资源类集合并去重
- 推断当前 WEB 应用类型
- 设置应用上下文初始化器
- 设置监听器
- 推断主入口应用类
run(args)
- 创建并启动计时监控类
- 初始化应用上下文和异常报告集合
- 设置系统属性
java.awt.headless
的值,默认值为:true - 创建所有 Spring 运行监听器并发布应用启动事件
- 初始化默认应用参数类
- 根据运行监听器和应用参数来准备 Spring 环境
- 创建 Banner 打印类
- 创建应用上下文
- 准备异常报告器
- 准备应用上下文
- 刷新应用上下文
- 应用上下文刷新后置处理
- 停止计时监控类
- 输出日志记录执行主类名、时间信息
- 发布应用上下文启动完成事件
- 执行所有 Runner 运行器
- 发布应用上下文就绪事件
- 返回应用上下文