毋庸置疑,Auto-configuration是Spring Boot的核心特性,其约定大于配置的思想,赋予了Spring Boot开箱即用的强大能力。本文从诞生背景、使用方式、实现原理这几个方面详细介绍这一特性。

1. 诞生背景

一直以来,应用程序模块之间的依赖及相应的配置管理,都是件繁琐的事情;maven和gradle的出现,基本解决了依赖管理,使依赖管理变得优雅起来。但与之相对的,繁琐的配置工作仍然让开发者头疼不已,我们来看一段Spring集成Hibernate的配置代码:

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <property name="driverClassName" value="com.mysql.jdbc.Driver" />
    <property name="url" value="jdbc:mysql://localhost/test" />
    <property name="username" value="root"></property>
    <property name="password" value="123456"></property>
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean" lazy-init="false">
    <!-- 注入datasource,给sessionfactoryBean内setdatasource提供数据源 -->
    <property name="dataSource" ref="dataSource" />
    <property name="configLocation" value="classpath:hibernate.cfg.xml"></property>
    <!-- //加载实体类的映射文件位置及名称 -->
    <property name="mappingLocations" value="classpath:com/demo/entities/*.hbm.xml"></property>
</bean>

<!-- 配置Spring声明式事务 -->
<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory"></property>
</bean>

这些代码很多开发者已经非常熟悉了,但是每次创建新项目,总是要带着它们(即使使用Java Config的形式,这些bean的也需要一个个声明出来)。有鉴于此,Spring团队提出了Auto-configuration机制,力图解决重复、复杂的配置问题。

2. 定义

顾名思义,Spring Boot Auto-configuration尝试根据项目的jar依赖关系,自动配置Spring应用程序。

例如,如果HSQLDB在项目的classpath中,并且没有手动配置任何数据库链接相关的Bean,那么Spring Boot将自动配置一个的内存数据库。通过这种方式,大大减轻了配置的工作量,开发者通常只需要调整部分配置即可。为帮助理解,HSQLDB自动配置的流程如下:

auto

3. 使用方式

笔者按照如下两个维度介绍使用方式,以求尽可能清晰明了:

  1. 按具体配置方式分类
  2. 按操作目的分类

3.1 按具体配置方式分类

按照具体配置方式分类,入口有这几种:相关注解、spring.factories、application.properties/application.yml。

3.1.1 相关注解

@EnableAutoConfiguration和@SpringBootApplication都有开启Spring Boot Auto-configuration能力。除了这两个注解之外,Spring Boot Auto-configuration没有其它面向用户的注解了。

@SpringBootApplication vs @EnableAutoConfiguration

@SpringBootApplication的作用等同于一起使用这三个注解:@Configuration、@EnableAutoConfiguration、和@ComponentScan

3.1.2 spring.factories文件

spring.factories文件指定了*AutoConfiguration类列表,只有在列表中的自动配置才会被检索到。Spring会检测classpath下所有的META-INF/spring.factories文件;若要引入自定义的自动配置,需要将自定义的*AutoConfiguration类添加到spring.factories文件中。

另外,该文件不是Spring Boot新增的特性,而是spring-core包中的SpringFactoriesLoader类负责检索并解析META-INF/spring.factories文件

3.1.3 application.properties/application.yml

Spring会检测classpath中是否存在这application.properties或application.yml,若存在将获取其中配置作用到Auto-configuration上,覆盖默认配置。

点击properties列表可以查看所有Spring Boot内置的属性配置示例。

3.2 按操作目的分类

按照操作目的划分,通常有这几种:定制部分配置、排除、全局开启/关闭、新增(相对复杂一些,会在另外一篇文章中单独介绍)。

3.2.1 定制部分配置

自动配置开启后,特定组件的配置变量名称是固定的(即做好了“约定”),将特定配置写入application.properties或application.yml即可覆盖已有配置。

以DataSourceAutoConfiguration为例,常用的配置为:

spring.datasource.url=jdbc:mysql://localhost:3306/test?characterEncoding=utf8&useSSL=true
spring.datasource.username=root
spring.datasource.password=1234
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

3.2.2 排除

分为两种排除方式:自动排除特定Bean、手动排除指定的AutoConfiguration。

自动排除特定Bean

当开发者在项目中手动声明了某个组件的特定类,Spring Boot Auto-configuration通过@Condition机制实现了自动避让;即Spring探测到用户自己手动声明了某些特定类后,对应的Auto-configuration就不再加载了。

如何查看所谓“特定类”

AutoConfiguration类中,被@ConditionalOnMissingBean或@ConditionalOnMissingClass的value值,例如org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration:
jdbc

手动排除指定的AutoConfiguration

使用@SpringBootApplication或@EnableAutoConfiguration中的exclude和excludeName属性可以排除指定的AutoConfiguration,属性值通常是以*AutoConfiguration结尾的类。

另外,在application.properties或application.yml中使用spring.autoconfigure.exclude也可以排除指定的AutoConfiguration。

示例如下:

@EnableAutoConfiguration(exclude = FreeMarkerAutoConfiguration.class)
private class EnableAutoConfigurationWithClassExclusions {

}

@EnableAutoConfiguration(excludeName = "org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration")
private class EnableAutoConfigurationWithClassNameExclusions {

}

@EnableAutoConfiguration(exclude = MustacheAutoConfiguration.class, excludeName = "org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration")
private class EnableAutoConfigurationWithClassAndClassNameExclusions {

}
spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration

如何找到可选的*AutoConfiguration

方法1:添加启动参数-Ddebug,在日志中可以看到已经加载的AutoConfiguration
方法2:spring-boot-autoconfigure包中存在spring.factories文件,文件中org.springframework.boot.autoconfigure.EnableAutoConfiguration变量的值中,列出了所有Spring Boot内置的
AutoConfiguration类

3.2.3 全局开启/关闭自动配置

全局开启:添加@SpringBootApplication或@EnableAutoConfiguration到配置类上,且spring.boot.enableautoconfiguration属性设置为true(默认为true)。

全局关闭:不使用@SpringBootApplication或@EnableAutoConfiguration,或spring.boot.enableautoconfiguration属性设置为false。

4. 实现原理

得益于spring-context中的扩展机制@Import(ImportSelector)和@Conditional(Condition),Spring Boot Auto-configuration自然地扩展选择器和过滤器,从而筛选出符合条件的配置组件。下面分两部分介绍其原理:Spring Framework的扩展点、调用流程。

4.1 Spring Framework的扩展点

@Import(ImportSelector)的使用,使配置可以委托到外部来处理,为Auto-configuration提供了的接入点。在接入点切入后,再使用各种自定义的@Conditional(Condition)筛选预先定义好的自动配置类和相关的Bean。

可以说,@Conditional(Condition)是Spring Boot实现自动配置的关键。

@Import

@Import是Spring 3.0提供在spring-context中的注解,提供与Spring XML中的元素等效的功能。允许导入@Configuration类、ImportSelector实现、ImportBeanDefinitionRegistrar实现,以及常规的@Component类。

ImportSelector接口

ImportSelector接口是Spring 3.1提供在spring-context中的接口。
接口方法:String[] selectImports(AnnotationMetadata importingClassMetadata)
提供根据传入的@Import类的元数据,返回需要加载的@Configuration类。

@Conditional

@Conditional是Spring 4.0提供在spring-context中的注解。
代表component只有在所有指定条件匹配时才有会被注册,允许指定一个Condition的实现类,以定义匹配规则。

Condition接口

Condition接口是Spring 4.0提供在spring-context中的接口。
接口方法:boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata)
根据传入的@Conditional类的元数据,返回是否加载该类。

4.2 调用流程

下图描述了Spring Boot Auto-configuration启动加载的基本流程。

flow

  1. 调用SpringApplication#run,开始启动
  2. 调用AbstractApplicationContext#refresh,进入容器启动的生命周期
  3. 进入BeanFactoryPostProcessors
  4. 解析@Import获得AutoConfigurationImportSelector类,该类是自动配置的核心,对配置列表的筛选、排序等操作在这里完成;同时该类实现了DeferredImportSelector接口,它时延迟执行的
  5. 在需要的时候,读取META-INF/spring.factories文件中配置的AutoConfigurationImportFilter列表、EnableAutoConfiguration列表、AutoConfigurationImportListener列表
  6. 根据用户注解中的exclue配置,排除指定的AutoConfiguration
  7. 调用AutoConfigurationImportFilter#match,以spring.factories文件中的EnableAutoConfiguration列表作为参数,进行筛选
  8. 对自动配置结果进行排序
  9. 根据@Condition类的条件对Bean进行筛选
  10. 启动完成

AutoConfigurationImportFilter
只有实现了该接口的Condition,才会在AutoConfigurationImportSelector被调用,Spring默认只配置了三个:OnBeanCondition、OnClassCondition、OnWebApplicationCondition

@Condition类
@Condition注解是spring-context的扩展,@Condition类的实现其实是在离开spring-boot-autoconfigure之后,在spring-context中调用的

注意
本文的代码分析,基于Spring Boot 2.1.2.BUILD-SNAPSHOT

5. 小结

自动配置特性使开发者节省了很多配置工作量,从重复繁琐的配置中解脱了出来。本文从诞生背景、使用方式、实现原理这几个方面详细介绍了Auto-configuration这一特性。
它的核心配置方式有三个:相关注解、spring.factories、application.properties/application.yml。
原理上,得益于Spring Framework强大的扩展能力,使用@Import(ImportSelector)和@Conditional(Condition)后,Spring Boot优雅地实现了自动配置特性。


文章作者: 沉迷思考的鱼
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 沉迷思考的鱼 !
评论
 上一篇
Spring Boot 自定义Auto-configuration Spring Boot 自定义Auto-configuration
Spring Boot的Auto-configuration以其易用性和实用性,得到了开发者们的广泛认可;但与此同时,Spring Boot内置的Auto-configuration仅能满足基本需求,对于企业级的应用生态来说是不够的,所以自
2018-12-12
下一篇 
Spring Boot Staters 详解 Spring Boot Staters 详解
Maven和Gradle为Java开发者解决了依赖管理的心头大患,已然成为事实上的依赖管理标准。但贪心的开发者还想更“懒”,有没有办法使依赖管理更简单、更快捷? 答案是肯定的,Spring Boot项目中引入了Starter的概念。
2018-11-29
  目录