springboot自动配置原理
概述
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 进行自动装载。
自动配置原理
首先要从@SpringBootApplication注解入手,该注解是一个复合注解。
看@SpringBootApplication注解上的@SpringBootConfiguration注解。发现该注解与@configuration注解一样的效果,类似@Service与@Component之间的关系,为了使得语义更清晰,可读性更强。
看@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 {};
}
- 看@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元数据文件来实现条件加载自动配置类。
- 看@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));}}
- 看@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的自动配置原理大体已经阐述完毕。
自定义的自动配置
- 新建一个spring项目,编写一个配置类和一个业务类。
@Configuration
public class DemoConfig {@Beanpublic DemoService demoService(){return new DemoService();}
}public class DemoService {public DemoService(){System.out.println("DemoService create");}
}
- 对该类使用maven打包到仓库。
- 新建一个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文件定义自动配置该配置类。
- 在spring项目的类路径下新增META-INF/spring.factories指定加载DemoConfig配置类,并重新打包到仓库,再到springboot项目中重新测试。
运行结果:
自动配置成功。
- 测试spring-autoconfigure-metadata.properties文件定义元数据。该定义表名只有类路径下有com.springboot.springbootstudy.EnableDemoConfig类才加载DemoConfig配置类。
打包,重新测试。
结果:又没有加载了。
- 最后一步,在springboot项目中创建一个com.springboot.springbootstudy.EnableDemoConfig再测试。
结果:
又成功了。
其实这里就能算是一个spring boot Starter了,Starter的本质就是spring或者spring boot项目加jar包加Bean配置加spring.factories 加 spring-autoconfigure-metadata.properties文件(可有可无)。
至此,完毕。
springboot自动配置原理相关推荐
- 这样讲 SpringBoot 自动配置原理,你应该能明白了吧
点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 作者:你在我家门口 juejin.im/post/5ce5effb ...
- SpringBoot | 自动配置原理
微信公众号:一个优秀的废人.如有问题,请后台留言,反正我也不会听. 前言 这个月过去两天了,这篇文章才跟大家见面,最近比较累,大家见谅下.下班后闲着无聊看了下 SpringBoot 中的自动配置,把我 ...
- 【详解】面试必问:SpringBoot自动配置原理
前言 SpringBoot框架是开发中的一大利器,其简化了spring的xml的配置,遵循了"约定大于配置"的原则,使用注解对常用的配置做默认配置,减少使用xml配置模式.Spri ...
- SpringBoot 自动配置原理(超级无敌详细)-2
SpringBoot 自动配置原理(超级无敌详细)-1 2.自动配置的实现 刚刚我们整体的过了一下主配置文件是如何实现的,但我们还没深入的研究如何实现自动装配功能.我们回到这个文件下,找一个具体的自动 ...
- SpringBoot实战之SpringBoot自动配置原理
www.cnblogs.com/leihuazhe/p- SpringBoot 自动配置主要通过 @EnableAutoConfiguration, @Conditional, @EnableConf ...
- springboot自动配置的原理_SpringBoot实战:详解SpringBoot自动配置原理
SpringBoot 自动配置主要通过 @EnableAutoConfiguration, @Conditional, @EnableConfigurationProperties 或者 @Confi ...
- SpringBoot 自动配置原理
创建项目 通过Spring Initialize创建SpringBoot项目 而接下来要说的是关于配置文件的事情.关乎配置文件可以参考官方文档. 对于配置文件来说到底在配置文件里面可以进行配置那些内容 ...
- springboot自动配置原理_今日份学习之Spring Boot自动配置实现原理
通过前面章节的学习,我们掌握了使用Spring Boot框架进行实际应用开发的方法.在使用Spring Boot 的过程中,我们时常会为一些看似简单,但实际上蕴藏了强大功能的实现而惊呼,下面就让我们来 ...
- 笑死,面试官又问我SpringBoot自动配置原理
面试官:好久没见,甚是想念.今天来聊聊SpringBoot的自动配置吧? 候选者:嗯,SpringBoot的自动配置我觉得是SpringBoot很重要的"特性"了.众所周知,Spr ...
最新文章
- oracle linux 5.8安装oracle 11g rac环境之grid安装
- 【转】子网划分实例与讲解
- 图书馆可以借到的书目
- js dom node.children与node.childNodes区别
- CSS 小结笔记之清除浮动
- MySQL 快速创建千万级测试数据
- toad如何查看表字段备注(表字段的说明)
- javascript window.navigator
- TP5整合的阿里云短信接口
- awr报告 解读_Oracle Awr报告_awr报告解读_基础简要信息
- 平均随机一致性指标表MATLAB,AHP法中平均随机一致性指标的算法及MATLAB实现.pdf...
- Python实现电影抢票系统需要几行代码?猜对有奖。
- 解决VMware 虚拟机中的网络连接出现”受限制或无连接“问题的方法
- fpga 级联fifo(VHDL)
- 机器学习基石 5.3 Effective Number of Hypotheses
- c语言数字的写法田字格,数字100田字格怎么写
- 基于QT搭建的网易云音乐
- 洛谷P1478 陶陶摘苹果(升级版)视频题解
- onlyoffice 安装、测试、打包、部署
- echart堆叠柱状图,顶部显示堆叠柱总数的技巧
热门文章
- 北京Loft投资分析
- i3-10110U和i5 10210u 哪个好
- Windows下安装JDK11(详细)
- java.sql.SQLSyntaxErrorException Unknown column····
- 哈希表及哈希函数研究综述
- Oracle取小数点部分
- 一个程序员,写在告别程序员生涯时说的话
- 当你启动服务器出现系统出错。 发生系统错误 1067。 进程意外终止。
- iOS开发之iOS10简单适配
- “3亿”风暴席卷昆明 搜狗全国移动峰会即将开幕