【Spring框架家族】SpringBoot自动配置原理源码跟进
继承springboot父工程依赖pox.xml
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.1.8.RELEASE</version></dependency>
spring-boot-starter-parent继承了spring-boot-dependencies
- 这个依赖中定义了常用依赖的版本,以及锁定了其版本,所以我们在导入starter时,无需定义版本
- 若没有在这里定义版本,则需要自己导入相应的版本信息,如mysql的jdbc驱动
starter【web】启动器
<!-- 导入了web模块正常运行所依赖的组件,DispatcherServlet,内置tomcat容器等等--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>
spring-boot-starter-web:
<!-- 这是springboot的基础依赖,基本所有的启动器都依赖于spring-boot-starter。其依赖内容包括自动配置,日志,注解,spring核心,yaml等。 导入了相应的starter后,springboot就会自动配置其运行组件springboot官方定义的启动器依赖都是以 spring-boot-starter 开头的。【spring-boot-starter-xxx】如果官方没有定义,其他技术需要集成springboot,则需要自己定义启动器依赖,如 mybatis 【mybatis-spring-boot-starter】--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId><version>2.1.8.RELEASE</version><scope>compile</scope></dependency>
@SpringBootApplication注解
// 标记当前是一个启动引导类,是springboot应用的入口。// 本身是一个组合注解。@SpringBootConfiguration // 点击@EnableAutoConfiguration // 点击// 包扫描器,不包含excludeFilters以下过滤注解@ComponentScan(excludeFilters = {@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),@Filter(type = FilterType.CUSTOM,classes = AutoConfigurationExcludeFilter.class) })public @interface SpringBootApplication {//...............}
@SpringBootConfiguration
@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Configuration //点击public @interface SpringBootConfiguration {}@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Component // 表示该注解是一个配置类注解,并且自己是容器中的一个组件public @interface Configuration { @AliasFor( annotation = Component.class ) String value() default "";}
@EnableAutoConfiguration
@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@AutoConfigurationPackage // 点击@Import({AutoConfigurationImportSelector.class})public @interface EnableAutoConfiguration {String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";Class<?>[] exclude() default {};String[] excludeName() default {};}
@AutoConfigurationPackage
@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited// 自定义化配置包扫描,指定要扫描的包@Import({Registrar.class}) // 点击public @interface AutoConfigurationPackage {}static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {Registrar() {}public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {AutoConfigurationPackages.register(registry, (new AutoConfigurationPackages.PackageImport(metadata)).getPackageName());}public Set<Object> determineImports(AnnotationMetadata metadata) {return Collections.singleton(new AutoConfigurationPackages.PackageImport(metadata));}}private final String packageName;public String getPackageName() {// 包名:就是Spring容器需要去扫描能够识别的注解。其实就是启动引导类所在的包及其子包return this.packageName;}
@AutoConfigurationImportSelector
public String[] selectImports(AnnotationMetadata annotationMetadata) {if (!this.isEnabled(annotationMetadata)) {return NO_IMPORTS;} else {// 获取自动化配置的元数据AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);// 通过配置的元数据和注解的元数据,得到一个自动化配置的entry键值对AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());}}
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {if (!this.isEnabled(annotationMetadata)) {return EMPTY_ENTRY;} else {// 获取注解的属性AnnotationAttributes attributes = this.getAttributes(annotationMetadata);// 通过注解元数据和属性获取真正意义上的集合List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);configurations = this.removeDuplicates(configurations);Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);this.checkExcludedClasses(configurations, exclusions);configurations.removeAll(exclusions);configurations = this.filter(configurations, autoConfigurationMetadata);this.fireAutoConfigurationImportEvents(configurations, exclusions);return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);}}
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");return configurations;}public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {String factoryClassName = factoryClass.getName();return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());}private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);if (result != null) {return result;} else {try {// 加载配置文件中的信息Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");LinkedMultiValueMap result = new LinkedMultiValueMap();while(urls.hasMoreElements()) {URL url = (URL)urls.nextElement();UrlResource resource = new UrlResource(url);// 遍历配置文件中的信息,转换为Properties对象,放入到信息中Properties properties = PropertiesLoaderUtils.loadProperties(resource);Iterator var6 = properties.entrySet().iterator();// 遍历while(var6.hasNext()) {Entry<?, ?> entry = (Entry)var6.next();// 获取工厂类名称String factoryClassName = ((String)entry.getKey()).trim();String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());int var10 = var9.length;for(int var11 = 0; var11 < var10; ++var11) {String factoryName = var9[var11];result.add(factoryClassName, factoryName.trim());}}}// 添加缓存cache.put(classLoader, result);return result;} catch (IOException var13) {throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);}}}
META-INF/spring.factories
@Configuration// 是否自动配置的条件@ConditionalOnWebApplication(type = Type.SERVLET)// 判断是否有下面三个的配置的@ConditionalOnClass({Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class})@ConditionalOnMissingBean({WebMvcConfigurationSupport.class})@AutoConfigureOrder(-2147483638)@AutoConfigureAfter({DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class})public class WebMvcAutoConfiguration {
SpringApplication.run(XXX.class, args)
// public static ConfigurableApplicationContext run()返回一个Spring容器对象public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {return (new SpringApplication(primarySources)).run(args);}
创建SpringApplication对象
public SpringApplication(Class... primarySources) {this((ResourceLoader)null, primarySources);}
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 = new HashSet();this.isCustomEnvironment = false;this.resourceLoader = resourceLoader;Assert.notNull(primarySources, "PrimarySources must not be null");this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));// 判断当前是否是web环境this.webApplicationType = WebApplicationType.deduceFromClasspath(); // 从类路径下找到META‐INF/spring.factories配置的所有ApplicationContextInitializer;// 然后封装成一个Map,转成Set,遍历Set,通过反射创建对象,// 赋值给SpringApplication的initializers属性this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));// 从类路径下找到META‐INF/spring.factories配置的所有ApplicationListener;// 然后封装成一个Map,转成Set,遍历Set,通过反射创建对象,// 赋值给SpringApplication的listeners属性this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));// 从多个配置类中找到有main方法的主配置类this.mainApplicationClass = this.deduceMainApplicationClass();}
执行run方法
public ConfigurableApplicationContext run(String... args) {// 创建StopWatch对象,执行start方法,开始计时。StopWatch用来计算run方法的执行时间StopWatch stopWatch = new StopWatch();stopWatch.start();ConfigurableApplicationContext context = null;Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();this.configureHeadlessProperty();// 从类路径下META‐INF/spring.factories中获取SpringApplicationRunListeners,// 反射创建对象,并回调start方法;SpringApplicationRunListeners listeners = this.getRunListeners(args);listeners.starting();Collection exceptionReporters;try {// 准备命令行参数,就是传入的argsApplicationArguments applicationArguments = new DefaultApplicationArguments(args);// 准备环境,完成后,回调listener的environmentPrepared方法。ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);this.configureIgnoreBeanInfo(environment);// 打印SpringBoot的logoBanner printedBanner = this.printBanner(environment);// 创建Spring容器,通过判断,决定是web的Spring容器还是普通的Spring容器context = this.createApplicationContext();exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);// 准备上下文环境this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);// 刷新容器,加载所有的组件【如果是web环境,会启动tomcat】this.refreshContext(context);this.afterRefresh(context, applicationArguments);// 停止计时,stopWatch.stop();if (this.logStartupInfo) {(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);}// 回调所有的SpringApplicationRunListener的started();listeners.started(context);// 从ioc容器中获取所有的ApplicationRunner和CommandLineRunner进行回调// 【ApplicationRunner先回调,CommandLineRunner再回调】this.callRunners(context, applicationArguments);} catch (Throwable var10) {this.handleRunFailure(context, var10, exceptionReporters, listeners);throw new IllegalStateException(var10);}try {listeners.running(context);return context;} catch (Throwable var9) {this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);throw new IllegalStateException(var9);}}
【Spring框架家族】SpringBoot自动配置原理源码跟进相关推荐
- SpringBoot自动配置【源码分析】-初始加载自动配置类
@Import(AutoConfigurationImportSelector.class) 1.利用getAutoConfigurationEntry(annotationMetadata);给容器 ...
- 【Spring框架家族】SpringBoot自动配置基本实现
SpringBoot自动配置-Condition_1 Condition是Spring4.0后引入的条件化配置接口,通过实现Condition接口可以完成有条件的加载相应的Bean @Conditio ...
- 深入解析SpringBoot核心运行原理和运作原理源码
SpringBoot核心运行原理 Spring Boot 最核心的功能就是自动配置,第 1 章中我们已经提到,功能的实现都是基于"约定优于配置"的原则.那么 Spring Boot ...
- SpringBoot实战之SpringBoot自动配置原理
www.cnblogs.com/leihuazhe/p- SpringBoot 自动配置主要通过 @EnableAutoConfiguration, @Conditional, @EnableConf ...
- SpringBoot(2.4.0)自动配置原理(源码)
一.从@SpringBootApplication讲起 源码 @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Docu ...
- SpringBoot自动配置原理流程
前言 新公司太忙了,都没啥空更新博客,就随便记录一下以前的学习笔记吧.SpringBoot是基于Spring上的衍生框架,只要看懂了Spring的话,学这个就比较简单了:SpringBoot也是在当前 ...
- springboot自动配置的原理_SpringBoot实战:详解SpringBoot自动配置原理
SpringBoot 自动配置主要通过 @EnableAutoConfiguration, @Conditional, @EnableConfigurationProperties 或者 @Confi ...
- springboot自动配置_揭秘SpringBoot自动化配置
花絮# 几年前接触过SpringBoot,跑过Demo,当时刚入行,连Spring都没搞明白,更别说SpringBoot了,就是觉得,哇塞,好厉害,然后一脸懵逼. 工作中没有用到,又没有去主动学习它. ...
- 【详解】面试必问:SpringBoot自动配置原理
前言 SpringBoot框架是开发中的一大利器,其简化了spring的xml的配置,遵循了"约定大于配置"的原则,使用注解对常用的配置做默认配置,减少使用xml配置模式.Spri ...
最新文章
- Linux下基于密钥的安全验证实现方法
- 3.IT-解决方案-3-Backup-Sql
- 当SRS遇到K8s:如何构建海量推流源站?
- 使用TPU的注意事项
- 《深入理解分布式事务》第三章 Spring 事务的实现原理
- paip.jdbc 连接自动释放的测试
- 奋战聊天机器人(二)语料和词汇资源
- 基于Matlab使用激光雷达从点云到跟踪列表跟踪车辆仿真(附源码)
- (九)DFI接口时序
- 为什么设置了面容ID,仍然需要输入密码解锁iPhone?
- css如何将图片调成合适大小,如何利用CSS自动调整图片的大小
- 隐私公链背景的FAIRY SWAP,让DEX更进一步
- 为知笔记(PC端) 康奈尔模板各栏间距调整
- android 服务开机启动慢,Android App启动慢原因
- AI自己写代码让智能体进化!OpenAI的大模型有“人类思想”那味了
- 《寓言中的经济学》简明纪要 - Part 1
- 单元测试:通过读取csv/xml数据并且结合使用allure展示测试报告,验证开发中的add()和reduct()操作(在@allure.story分别实现相加减)
- Q1月活大涨70%,后浪会成B站的流量萌新吗?
- L1正则化与L2正则化
- JAVA的GUI编程02——事件监听(ActionListener)、TextField事件监听、(组合、内部类)