概述

Springboot的基本认识

对于Spring框架,我们接触得比较多的是Spring mvc,Spring IOC、AOP、DI。而这框架如果在使用过程中,随着项目越来越大,引入的技术越来越多,会导致配置越来越复杂。因为要手动配置Bean,配置文件、配置类等,十分繁琐。

而spring boot框架的出现就是为了解决这一个问题,能够帮助Spring框架使用者快速构建一个基于Spring框架以及Spring生态体系的应用解决方案。他是对约定优于配置这个理念下的最佳实践,因此他是一个服务于框架的框架,服务的范围是简化配置开发。

约定优于配置是指约定好一些规范(默认规则),然后如果开发者遵守这个约定的规范,就可以不用多余的配置,直接可以达到使用效果。

约定优于配置的一些例子体现:

  • maven的目录结构就是约定优于配置的一个很好的体现,默认有 resources 文件夹存放配置文件,默认打包方式为jar。
  • spring-boot-starter-web 中默认包含 spring mvc 相关依赖以及内置的 tomcat 容器,使得构建一个 web 应用更加简单。
  • 默认提供 application.properties/yml 文件。
  • 默认通过 spring.profiles.active 属性来决定运行环境时读取的配置文件。
  • EnableAutoConfiguration 默认对于依赖的 starter 进行自动装载。

自动配置原理

  1. 首先要从@SpringBootApplication注解入手,该注解是一个复合注解。

  2. 看@SpringBootApplication注解上的@SpringBootConfiguration注解。发现该注解与@configuration注解一样的效果,类似@Service与@Component之间的关系,为了使得语义更清晰,可读性更强。

  3. 看@EnableAutoConfiguration,这个是Springboot自动配置的核心。也是一个复合注解。

@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 {};
}
  1. 看@EnableAutoConfiguration上的@Import({AutoConfigurationImportSelector.class}),该注解的作用的导入一个AutoConfigurationImportSelector自动配置选择组件。

主要看selectImports方法,该方法是实现ImportSelector接口的方法,作用是自定义一些规则,返回Spring要加载的类的全限定名的数组。

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {private static final AutoConfigurationImportSelector.AutoConfigurationEntry EMPTY_ENTRY = new AutoConfigurationImportSelector.AutoConfigurationEntry();private static final String[] NO_IMPORTS = new String[0];private static final Log logger = LogFactory.getLog(AutoConfigurationImportSelector.class);private static final String PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE = "spring.autoconfigure.exclude";private ConfigurableListableBeanFactory beanFactory;private Environment environment;private ClassLoader beanClassLoader;private ResourceLoader resourceLoader;public AutoConfigurationImportSelector() {}public String[] selectImports(AnnotationMetadata annotationMetadata) {if (!this.isEnabled(annotationMetadata)) {//首先,判断是否启动通过这个Selector来实现自动配置,isEnabled方法在下面。return NO_IMPORTS;} else {//这段逻辑分支就是加载指定的类。//加载元数据,方法定义在下面AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);//这里解释真正加载自动配置类了。自动配置类实际上就是一个配置类。方法定义在下面AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);//转成数组返回。return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());}}
}protected boolean isEnabled(AnnotationMetadata metadata) {//一个判断,首先判断当前类是不是AutoConfigurationImportSelector类,如果不是,就返回True,表示启动。
//如果不是AutoConfigurationImportSelector类,
//那么就获取环境变量spring.boot.enableautoconfiguration的值来判断是否启动,如果没有配置该环境变量,默认使用默认值true。return this.getClass() == AutoConfigurationImportSelector.class ? (Boolean)this.getEnvironment().getProperty("spring.boot.enableautoconfiguration", Boolean.class, true) : true;}public static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) {//加载类路径classpath下的META-INF/spring-autoconfigure-metadata.properties元数据文件。下面讲解 该文件,现在直接跳到该文件解析更佳。return loadMetadata(classLoader, "META-INF/spring-autoconfigure-metadata.properties");}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);//删除重复值,可能多个spring.factories文件会定义同样的自动配置类,在这里去重。configurations = this.removeDuplicates(configurations);//排除一些指定排除的类。会根据注解的excluded属性配置。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) {//返回要加载的自动配置类,这里使用的是一个SPI扩展机制实现。方法定义在下面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 loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());}public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {MultiValueMap<String, String> result = cache.get(classLoader);if (result != null) {return result;}try {//加载FACTORIES_RESOURCE_LOCATION代表的路径的文件,生成URL集合//FACTORIES_RESOURCE_LOCATION 的值为"META-INF/spring.factories",//代表会加载类路径下的META-INF/spring.factories文件//该文件的定义语法在下面Enumeration<URL> urls = (classLoader != null ?classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));result = new LinkedMultiValueMap<>();while (urls.hasMoreElements()) {//遍历URL url = urls.nextElement();UrlResource resource = new UrlResource(url);//加载文件形成k v 集合Properties properties = PropertiesLoaderUtils.loadProperties(resource);for (Map.Entry<?, ?> entry : properties.entrySet()) {//遍历kv集合,生成要加载的类的集合String factoryClassName = ((String) entry.getKey()).trim();for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {result.add(factoryClassName, factoryName.trim());}}}cache.put(classLoader, result);//返回return result;}catch (IOException ex) {throw new IllegalArgumentException("Unable to load factories from location [" +FACTORIES_RESOURCE_LOCATION + "]", ex);}}

META-INF/spring-autoconfigure-metadata.properties文件的说明

org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration.AutoConfigureAfter=org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration
org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration.Configuration=
org.springframework.boot.autoconfigure.data.neo4j.Neo4jBookmarkManagementConfiguration.Configuration=

上面是spring boot Autoconfiguration模块的自动配置元数据的部分配置,主要是用于配置一些自动配置类加载的一些条件,只有符合该条件才加载该自动配置类,以防止加载无用的没必要的自动配置类。

语法:
自动配置类全路径.条件类型(注解)= 条件的值
比如

org.springframework.boot.autoconfigure.influx.InfluxDbAutoConfiguration.ConditionalOnClass=org.influxdb.InfluxDB#org.springframework.boot.autoconfigure.influx.InfluxDbAutoConfiguration 这个是要加载的自动配置类。
#ConditionalOnClass 这个是加载条件,表示某个类在类路径存在时才加载该自动配置类。
#org.influxdb.InfluxDB 条件的值
#这个的意思是,只有我的项目类路径中存在org.influxdb.InfluxDB类,才加载InfluxDbAutoConfiguration 配置类。

还有其他很多条件类型,比如:AutoConfigureAfter、AutoConfigureBefore、ConditionalOnSingleCandidate等。如果等于号右边没有值,代表该自动配置类不设置条件。

其实这些条件类型就是注解,那为什么不直接在配置类上加这些注解呢,因为这些注解是spring boot的注解,而我们要自动配置的工程不一定是spring boot工程,也有可能是spring工程

META-INF/spring.factories文件语法:
该文件是配置要被springboot加载的配置类:

# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer# Auto Configuration Import Listeners
org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\

上面是Spring boot autoconfig模块的spring.factories的部分配置。
语法是:
注解全限定名=类全限定名1,类全限定名2…表示要使用该类来加载值配置类,比如:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\#表示EnableAutoConfiguration加载SpringApplicationAdminJmxAutoConfiguration和AopAutoConfiguration配置。

总结:AutoConfigurationImportSelector就是选择类路径下的META-INF/spring.factories文件配置的配置类,再配合类路径下的META-INF/spring-autoconfigure-metadata.properties元数据文件来实现条件加载自动配置类。

  1. 看@EnableAutoConfiguration上的@AutoConfigurationPackage注解。
@Import({Registrar.class})
public @interface AutoConfigurationPackage {}

在这里,如果你对@Import注解和ImportSelector和ImportBeanDefinitionRegistrar不懂的,要去补下功课再回来,不然下面就看不懂了。
该注解的作用是加载一个注册器Registrar.class。

@Order(-2147483648)static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {Registrar() {}//ImportSelector是返回要加载的bean数组,而ImportBeanDefinitionRegistrar是直接就注册。//这个Registrar 的作用是根据注解的属性basePackages等属性用于路径扫描注册Bean。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));}}
  1. 看@SpringBootApplication注解上的@ComponentScan
    这个注解设置了两个属性,一个是excludeFilters,值为TypeExcludeFilter,表示如果TypeExcludeFilter的match方法返回true就排除该Bean,返回false就不排除。

TypeExcludeFilter:

//下面是TypeExcludeFilter的match方法
//这个类TypeExcludeFilter相当于一个总类,委托类,他的逻辑就是获取容器中所有它的子类(被委托类),进行排除,我们编写自定义的TypeExcludeFilter加入容器中就是给这里使用的。
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {if (this.beanFactory instanceof ListableBeanFactory && this.getClass() == TypeExcludeFilter.class) {//只有beanFactory 是ListableBeanFactory 的子类并且这个类是TypeExcludeFilter才进来,//否则总是返回false。//获取容器中的所有TypeExcludeFilter的子类组件Collection<TypeExcludeFilter> delegates = ((ListableBeanFactory)this.beanFactory).getBeansOfType(TypeExcludeFilter.class).values();Iterator var4 = delegates.iterator();while(var4.hasNext()) {//遍历,使用每个TypeExcludeFilter组件进行排除TypeExcludeFilter delegate = (TypeExcludeFilter)var4.next();if (delegate.match(metadataReader, metadataReaderFactory)) {return true;}}}return false;}

第二个属性是Filter,,值是AutoConfigurationExcludeFilter。表示AutoConfigurationExcludeFilter的match方法返回true就加载Bean,否则不加载。
AutoConfigurationExcludeFilter:

public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {//同时符合this.isConfiguration(metadataReader)和this.isAutoConfiguration(metadataReader);才返回truereturn this.isConfiguration(metadataReader) && this.isAutoConfiguration(metadataReader);}private boolean isConfiguration(MetadataReader metadataReader) {//要加载的类要被Configuration注解注解才返回true。return metadataReader.getAnnotationMetadata().isAnnotated(Configuration.class.getName());}private boolean isAutoConfiguration(MetadataReader metadataReader) {//要加载的类要在上面的AutoConfigurationImportSelector选中的类里面才加载。return this.getAutoConfigurations().contains(metadataReader.getClassMetadata().getClassName());}

至此,Springboot的自动配置原理大体已经阐述完毕。

自定义的自动配置
  1. 新建一个spring项目,编写一个配置类和一个业务类。
@Configuration
public class DemoConfig {@Beanpublic DemoService demoService(){return new DemoService();}
}public class DemoService {public DemoService(){System.out.println("DemoService create");}
}
  1. 对该类使用maven打包到仓库。
  2. 新建一个springboot项目,引入刚刚打包的spring项目依赖,并尝试访问DemoService Bean是否存在。
public class DemoTest {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringbootstudyApplication.class);DemoService bean = context.getBean(DemoService.class);System.out.println(bean);}
}

结果:

结果不存在,原因springboot根本就不知道要加载他,原因是我们没有使用spring.factories文件定义自动配置该配置类。

  1. 在spring项目的类路径下新增META-INF/spring.factories指定加载DemoConfig配置类,并重新打包到仓库,再到springboot项目中重新测试。

运行结果:

自动配置成功。

  1. 测试spring-autoconfigure-metadata.properties文件定义元数据。该定义表名只有类路径下有com.springboot.springbootstudy.EnableDemoConfig类才加载DemoConfig配置类。

打包,重新测试。
结果:又没有加载了。

  1. 最后一步,在springboot项目中创建一个com.springboot.springbootstudy.EnableDemoConfig再测试。
    结果:

    又成功了。

其实这里就能算是一个spring boot Starter了,Starter的本质就是spring或者spring boot项目加jar包加Bean配置加spring.factories 加 spring-autoconfigure-metadata.properties文件(可有可无)。

至此,完毕。

springboot自动配置原理相关推荐

  1. 这样讲 SpringBoot 自动配置原理,你应该能明白了吧

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 作者:你在我家门口 juejin.im/post/5ce5effb ...

  2. SpringBoot | 自动配置原理

    微信公众号:一个优秀的废人.如有问题,请后台留言,反正我也不会听. 前言 这个月过去两天了,这篇文章才跟大家见面,最近比较累,大家见谅下.下班后闲着无聊看了下 SpringBoot 中的自动配置,把我 ...

  3. 【详解】面试必问:SpringBoot自动配置原理

    前言 SpringBoot框架是开发中的一大利器,其简化了spring的xml的配置,遵循了"约定大于配置"的原则,使用注解对常用的配置做默认配置,减少使用xml配置模式.Spri ...

  4. SpringBoot 自动配置原理(超级无敌详细)-2

    SpringBoot 自动配置原理(超级无敌详细)-1 2.自动配置的实现 刚刚我们整体的过了一下主配置文件是如何实现的,但我们还没深入的研究如何实现自动装配功能.我们回到这个文件下,找一个具体的自动 ...

  5. SpringBoot实战之SpringBoot自动配置原理

    www.cnblogs.com/leihuazhe/p- SpringBoot 自动配置主要通过 @EnableAutoConfiguration, @Conditional, @EnableConf ...

  6. springboot自动配置的原理_SpringBoot实战:详解SpringBoot自动配置原理

    SpringBoot 自动配置主要通过 @EnableAutoConfiguration, @Conditional, @EnableConfigurationProperties 或者 @Confi ...

  7. SpringBoot 自动配置原理

    创建项目 通过Spring Initialize创建SpringBoot项目 而接下来要说的是关于配置文件的事情.关乎配置文件可以参考官方文档. 对于配置文件来说到底在配置文件里面可以进行配置那些内容 ...

  8. springboot自动配置原理_今日份学习之Spring Boot自动配置实现原理

    通过前面章节的学习,我们掌握了使用Spring Boot框架进行实际应用开发的方法.在使用Spring Boot 的过程中,我们时常会为一些看似简单,但实际上蕴藏了强大功能的实现而惊呼,下面就让我们来 ...

  9. 笑死,面试官又问我SpringBoot自动配置原理

    面试官:好久没见,甚是想念.今天来聊聊SpringBoot的自动配置吧? 候选者:嗯,SpringBoot的自动配置我觉得是SpringBoot很重要的"特性"了.众所周知,Spr ...

最新文章

  1. oracle linux 5.8安装oracle 11g rac环境之grid安装
  2. 【转】子网划分实例与讲解
  3. 图书馆可以借到的书目
  4. js dom node.children与node.childNodes区别
  5. CSS 小结笔记之清除浮动
  6. MySQL 快速创建千万级测试数据
  7. toad如何查看表字段备注(表字段的说明)
  8. javascript window.navigator
  9. TP5整合的阿里云短信接口
  10. awr报告 解读_Oracle Awr报告_awr报告解读_基础简要信息
  11. 平均随机一致性指标表MATLAB,AHP法中平均随机一致性指标的算法及MATLAB实现.pdf...
  12. Python实现电影抢票系统需要几行代码?猜对有奖。
  13. 解决VMware 虚拟机中的网络连接出现”受限制或无连接“问题的方法
  14. fpga 级联fifo(VHDL)
  15. 机器学习基石 5.3 Effective Number of Hypotheses
  16. c语言数字的写法田字格,数字100田字格怎么写
  17. 基于QT搭建的网易云音乐
  18. 洛谷P1478 陶陶摘苹果(升级版)视频题解
  19. onlyoffice 安装、测试、打包、部署
  20. echart堆叠柱状图,顶部显示堆叠柱总数的技巧

热门文章

  1. 北京Loft投资分析
  2. i3-10110U和i5 10210u 哪个好
  3. Windows下安装JDK11(详细)
  4. java.sql.SQLSyntaxErrorException Unknown column····
  5. 哈希表及哈希函数研究综述
  6. Oracle取小数点部分
  7. 一个程序员,写在告别程序员生涯时说的话
  8. 当你启动服务器出现系统出错。 发生系统错误 1067。 进程意外终止。
  9. iOS开发之iOS10简单适配
  10. “3亿”风暴席卷昆明 搜狗全国移动峰会即将开幕