SpringBoot - 自动配置(简洁版)
本文参考黑马程序员SpringBoot视频
一、自动配置思想
1.1、自动配置原理
-
收集Spring开发者的编程习惯,整理开发过程使用的常用技术列表——》技术集A
-
收集常用技术(技术集A)的使用参数,整理开发过程中每个技术的常用设置列表——》设置集B
-
初始化SpringBoot基础环境,加载用户自定义的bean和导入的其他坐标,形成初始化环境
-
将技术集A包含的所有技术都定义出来,在Spring/SpringBoot启动时默认全部加载
-
将技术集A中具有使用条件的技术约定出来,设置成按条件加载,由开发者决定是否使用该技术(与初始环境做对比)
-
将设置集B作为默认配置加载(约定大于配置),减少开发者配置工作
-
开放设置集B的配置覆盖接口,由开发者根据自身需要决定是否覆盖默认配置
1.2、@SpringBootConfiguration
-
@SpringBootConfiguration
@Configuration
@Component
@Indexed
-
@SpringBootConfiguration
@AutoConfigurationPackage
@Import({Registrar.class})
@Import({AutoConfigurationImportSelector.class})
-
@ComponentScan( excludeFilters = {@Filter(type = FilterType.CUSTOM, classes = {TypeExcludeFilter.class}), @Filter(type = FilterType.CUSTOM,classes = {AutoConfigurationExcludeFilter.class})})
@Repeatable(ComponentScans.class)
@SpringBootConfiguration
注解是由上面哪些注解组合而成,而上面哪些注解,最主要的有两个:@Import({Registrar.class})
和 @Import({AutoConfigurationImportSelector.class})
-
@Import({Registrar.class})
:设置当前配置所在包作为扫描包,后续要针对当前的包进行扫描 -
@Import({AutoConfigurationImportSelector.class})
:加载出配置集A
二、SpringBoot启动流程
1.1、启动流程思想
- 初始化各种属性,加载成对象
- 读取环境属性 - Environment
- 系统配置 - spring.factories
- 参数 - Arguments、application.properties
- 创建Spring容器对象ApplicationContext,加载各种配置
- 在容器创建前,通过监听器机制,应对不同阶段加载数据、更新数据的需求
- 容器初始化过程中追加各种功能,例如统计时间、输出日志等
1.2、工作流程
SpringBoot项目的启动类中都是由SpringApplication.run(MuciApplication.class, args)
启动的,那么就由这个类开始分析。
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return (new SpringApplication(primarySources)).run(args);
}
由上面源码可知,最终分成两部分:一是SpringApplication
对象的初始化,二是SpringApplication
对象调用run
方法。
1.2.1、SpringApplication
的构造
该方式主要作用是加载各种配置信息、初始化各种配置对象
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.sources = new LinkedHashSet();
this.bannerMode = Mode.CONSOLE;
this.logStartupInfo = true;
this.addCommandLineProperties = true;
this.addConversionService = true;
this.headless = true;
this.registerShutdownHook = true;
this.additionalProfiles = Collections.emptySet();
this.isCustomEnvironment = false;
this.lazyInitialization = false;
this.applicationContextFactory = ApplicationContextFactory.DEFAULT;
this.applicationStartup = ApplicationStartup.DEFAULT;
//传入类加载器,这种设计就是扩大该属性的使用范围,简单来说,这样设置之后,其他方法使用就不需要通过参数传递了。
this.resourceLoader = resourceLoader;
//就是简单判断传递的主类不是空
Assert.notNull(primarySources, "PrimarySources must not be null");
//将primarySources转成一个集合(去重)
this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
//推断当前容器加载的类型,根据是否存在某个类进行判断
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//获取系统配置引导信息
this.bootstrapRegistryInitializers = this.getBootstrapRegistryInitializersFromSpringFactories();
//获取ApplicationContextInitializer.class对应的实例
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
//初始化监听器,对初始化过程及运行过程进行干预
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
//初始化引导类类名信息
this.mainApplicationClass = this.deduceMainApplicationClass();
}
1.2.2、SpringApplication
的 run 方法
初始化容器,获取
ConfigurableApplicationContext
对象
public ConfigurableApplicationContext run(String... args) {
//相当于一个计时器
StopWatch stopWatch = new StopWatch();
//计时开始
stopWatch.start();
//系统引导信息对应的上下文对象
DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();
ConfigurableApplicationContext context = null;
//模拟输入输出信号,避免因缺少外设导致的信号传输失败,进而引发错误(模拟显示器,键盘,鼠标...)
//java.awt.headless=true
this.configureHeadlessProperty();
//获取当前注册的可运行的监听器
SpringApplicationRunListeners listeners = this.getRunListeners(args);
//监听器执行了对应的操作步骤
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
// 获取参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//将前期读取的数据加载成了一个环境对象,用来描述信息
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);
//做了一个配置 `spring.beaninfo.ignore` 备用
this.configureIgnoreBeanInfo(environment);
//控制打印banner
Banner printedBanner = this.printBanner(environment);
//创建容器对象,根据前期的应用类型判定
context = this.createApplicationContext();
//设置启动模式
context.setApplicationStartup(this.applicationStartup);
//对容器进行设置,参数来自于前期的
this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
//刷新容器环境
this.refreshContext(context);
//刷新完毕后做的后处理
this.afterRefresh(context, applicationArguments);
//计时结束
stopWatch.stop();
//判定是否记录启动时间的日志
if (this.logStartupInfo) {
//创建日志对应的对象,输出日志信息,包含启动时间
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
listeners.started(context);
this.callRunners(context, applicationArguments);
} catch (Throwable var10) {
this.handleRunFailure(context, var10, listeners);
throw new IllegalStateException(var10);
}
try {
listeners.running(context);
return context;
} catch (Throwable var9) {
this.handleRunFailure(context, var9, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var9);
}
}