博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring Boot 自动配置原理
阅读量:7231 次
发布时间:2019-06-29

本文共 5296 字,大约阅读时间需要 17 分钟。

Spring Boot 自动配置原理

  官方说明:

  Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置(applicationContext.xml)。其中自动配置可以说是Spring Boot中最令人愉悦的功能之一,它使得开发人员远离了繁琐配置(任何东西多了都会变得繁琐)。   

1、实现思路

  Spring Boot提供了自动配置机制,它是通过 类路径系统环境外部配置参数逻辑表达式spring上下文 等角度去判断当前系统是否需要启用某个技术框架,如果需要则进行自动配置。也就是说还是得配置,只不过Spring Boot把这个配置过程变得智能了一点。这里需要说明的是Spring Boot只是提供了自动配置机制,而具体要什么类需要加入到Spring容器是各个框架需要自己实现的(),当然Spring Boot内部有一部分实现。

  我们先看一个非Spring Boot项目环境中要使用Mybatis作为项目里的orm框架,我们需要在applicationContext.xml里配置Mybatis的相关的类(SqlSessionFactoryBean),这一过程是我们主动告知Spring我需要配置什么类(Spring Boot也是主动告知,只不过告知这个动作是代码帮我们完成的)。

  如果使用Spring Boot的话,主动告知这一过程将是Spring Boot自动配置机制帮我们完成,这里实际上是mybatis-spring-boot-starter实现的

2、基于什么实现

(一)Java配置

  Spring Boot是基于Spring3.0以上版本中的特性来实现的,具体使用规则这里不作说明,如不清楚可以先了解相关知识。使我们可以用Java类来代替Xml配置文件,于是我们可以想假如把applicationContext.xml里的所有配置都通过来实现的话,那就实现了我们消灭配置的第一步,如下图:

Xml配置 Java配置

(二)关键的类和接口

  Spring3.x中提供的一系列类、接口、注解构成了自动配置的基石,我们甚至可以利用它们自己搞一个Spring Boot出来,先来看下主要的类:

名称 说明
ImoprtSelector 是一个接口,提供了一个方法用于收集需要导入的配置类
@Conditional 可以根据条件Condition 装载不同的Bean
Condition 是一个接口,提供一个方法用于返回是否匹配,如果匹配则配置,反之
@Import 导入配置类、普通Java类、实现ImoprtSelector的类
@SpringFactoriesloader 用于加载spring.factories中配置的内容

3、源码分析

(一)@SpringBootApplication注解

  先从Spring Boot应用的入口开始,即@SpringBootApplication注解,如图

  可以看到@SpringBootApplication上添加了@EnableAutoConfiguration,这个注解从名称上看就知其意 (激活自动配置),点进@EnableAutoConfiguration,如图

  在@EnableAutoConfiguration上添加了@Import,前文中提过@Import的功能是导入配置类、普通Java类、实现ImoprtSelector的类,这里导入的是EnableAutoConfigurationImportSelector类,它继承AutoConfigurationImportSelector类,而AutoConfigurationImportSelector实现了ImoprtSelector接口,但EnableAutoConfigurationImportSelector已经添加了@Deprecated说明不推荐使用了。如图


  • ImoprtSelector

  这里简单说明下实现ImoprtSelector接口的作用,ImoprtSelector有个selectImports方法(这两个名称简直让人头晕)如图

  可以看到selectImports()返回值为应该导入的配置类类名集合,到底应该导入哪些配置类?你自己去决定和实现啦。方法的参数是AnnotationMetadata,它是访问注解元数据的接口,意思是什么类上添加了@Import(ImoprtSelectorImpl.class)注解,那AnnotationMetadata里封装的就是那个类的所有注解信息。


(二)AutoConfigurationImportSelector

  AutoConfigurationImportSelector中方法selectImports()的源码如下:

  首先调用AutoConfigurationMetadataLoader.loadMetadata()加载自动配置元数据,它加载了 spring-boot-autoconfigure-1.5.7.RELEASE.jar 下的META-INF/spring-autoconfigure-metadata.properties文件,最后返回了AutoConfigurationMetadata(把它当作一个properties),文件的内容如下:

  我们发现这个properties文件的key比较复杂,它格式是一个完整类名(F)+另一个简单类名(S),这样做的目的从源码可以推断应该是想达到命名空间的效果,其实就是为了方便取值。等号右边配置的东西具体用于什么地方需要根据(S)来确定,就上图中ConditionOnClass等号右边值配的是类名,以,号隔开,用于判断 是否存在于类路径

  接着调用了getCandidateConfigurations()方法

  getCandidateConfigurations()方法会去获取所有自动配置类的名称,源码如下:

  利用SpringFactoriesLoader加载 spring-boot-autoconfigure-1.5.7.RELEASE.jar  下的META-INF/spring.factories文件,文件是properties格式如图:

  可以看到配在org.springframework.boot.autoconfigure.EnableAutoConfiguration下的全是自动配置类,这些类以字符串数组的形式返回,也就是getCandidateConfigurations()的方法返回值。

  继续看方法selectImports()中的代码

  得到自动配置类名称集合后调用了removeDuplicates()(删除重复的配置类),接着sort()(排序),接着去掉使用exclude或者excludeName所排除的类,然后调用filter(),最后触发所有注册的回调。其中filter()方法是自动配置的关键,源码如下:

  上面代码中调用getAutoConfigurationImportFilters()方法返回了一个AutoConfigurationImportFilter接口,真实运行时它是实现类OnClassCondition,然后调用match()方法,该方法的返回值是boolean[],如果是flase说明不匹配则跳过自动配置。细节将在下一节说明 。

(三)OnClassCondition

  上节中提到是根据
match()方法返回的
boolean[]来判断到底需不需要导入自动配置(注意
boolean[]值的意义是是否需要导入自动配置,而不是开始配置),那返回
boolean[]到底有何意义?我们看方法
match()的源码:

  方法getOutcomes()返回的是是否匹配的结果,这个是否匹配需要结合META-INF/spring.factoriesMETA-INF/spring-autoconfigure-metadata.properties来看,前者指定了需要自动配置的类,后者指定了以自动配置类为key多个类名为值,通过这些信息就可以判断是否匹配了。ConditionOutcome类则匹配结果的封装,它包含了是否匹配、条件消息等信息,可以把它当作领域对象。方法getOutcomes()的源码如下:

多个类名:这里的类是用于判断  是否存在于类路径

  将需要自动配置的类二分处理,一半开启线程进行处理,另一半标准处理,最后合并结果。有意思的地方是源码作者在注释中写到 //More threads make things worse 更多的线程会变得更糟(为什么不是分3次或者4次呢?疑问脸)。

  最后我们忽略中间的层层封装直达底层,究竟OnClassCondition是怎么判断需不需要导入自动配置类,看如下源码:

  通过上面代码可以知道它是通过当前系统类路径下是否存在className来判断的,如果存在就导入这个自动配置类,反之忽略。到此Spring Boot帮我们从类路径的角度去判断了需不需要启用自动配置。

className:META-INF/spring-autoconfigure-metadata.properties中等号右边的值

(四)其他角度

  Spring Boot提供了自动配置机制,它是通过 类路径系统环境外部配置参数逻辑表达式spring上下文 等角度去判断当前系统是否需要启用某个技术框架,目前我们分析了从 类路径 的角度去判断的源码,其他的角度思路一致,只是具体实现不一样,它们都继承自SpringBootCondition

  SpringBootCondition中的抽象方法getMatchOutcome()便是子类去实现是否匹配的,那OnClassCondition 中为什么使用的match()方法去实现的?实际上OnClassCondition 有两套实现,一个是match(),另一个就是getMatchOutcome(),前者是Spring Boot强制的通过类路径去推断了一次,后者是使用@ConditionalOnClass注解的时候调用

  以此类推其他的@ConditionalOn*都是通过@Conditional注解来实现的,下面列举了常用的条件判断注解

@Conditional扩展 作用
@ConditionalOnJava 系统中的jdk版本是否符合要求
@ConditionalOnExpression 满足SpEL表达式
@ConditionalOnBean Spring容器中存在指定的Bean
@ConditionalOnMissingBean Spring容器中不存在指定的Bean
@ConditionalOnClass 某个类在类路径中
@ConditionalOnMissingClass 某个类不在类路径中
@ConditionalOnJndi 存在JNDI路径
@ConditionalOnNotWebApplication 当前系统不是Web环境
@ConditionalOnWebApplication 当前系统是Web环境
@ConditionalOnProperty 指定的属性是否有指定的值
@ConditionalOnResource 类路径下是否存在指定的资源文件

(五)一个例子

  我们以MybatisAutoConfiguration自动配置类为例,源码如下:

  可以看到类上添加了@ConditionalOnClass({SqlSessionFactory.class,SqlSessionFactoryBean.class})注解,说明当存在SqlSessionFactorySqlSessionFactoryBean类的时候启动自动配置,接着添加了@ConditionalOnBean(DataSource.class),说明当存在DataSource实例时会启动自动配置。

  sqlSessionFactory()方法上添加了@ConditionalOnMissingBean注解,说明当不存在SqlSessionFactory实例的时候会创建一个SqlSessionFactory实例。

  @EnableConfigurationProperties(MybatisProperties.class)作用是指定一个类用来接收外部配置文件参数.

3、总结

  Spring Boot自动配置原理总体还是基于Spring提供的特性,牢固掌握Spring核心的重要性不言而喻。

转载地址:http://srpfm.baihongyu.com/

你可能感兴趣的文章
linux的基本指令--第三节
查看>>
设置tomcat为https访问
查看>>
我的友情链接
查看>>
oracle的触发器和应用
查看>>
Docker实践(五):Docker Compose
查看>>
我的友情链接
查看>>
介绍几个jsp页面传对象的方法
查看>>
我的Jakarta+Commons
查看>>
Bootstrap的使用
查看>>
python设置文字输出颜色
查看>>
WARNING:tornado.access:403 GET /websocket (::1) 1.00ms
查看>>
cocos creator游戏适配这事
查看>>
AngularJS - contorller app module
查看>>
CF666E. Forensic Examination
查看>>
apue第16章笔记
查看>>
Nvidia Driver
查看>>
NIO 相关解析
查看>>
Loj #2304. 「NOI2017」泳池
查看>>
面试技巧,如何通过索引说数据库优化能力,内容来自Java web轻量级开发面试教程...
查看>>
Python实现:某个用户登录后,查看自己拥有所有权限
查看>>