详解 Spring Boot 自动配置原理和应用

开发 前端
我们详细阐述了 Spring Boot 自动配置机制的实现原理,从源码角度分析了为什么 Spring Boot 能够做到自动配置,并结合 MyBatis 框架分析了它在开源框架中的具体应用。

我们知道,基于 Spring Boot,开发人员只需要在类路径中引入一组第三方框架的 starter 组件,就能在 Spring 容器中使用这些框架所提供的各项功能。这在当下的开发过程中已是常态,但在 Spring Boot 还没有诞生之前却是不可想象的。如果我们使用传统的 Spring 框架,那就需要添加各种繁杂的配置信息才能启动容器。那么,Spring Boot 是通过什么样的机制来做到这一点的呢?这就是今天我们要讨论的内容——Spring Boot 的自动配置机制。

可以说,Spring Boot 的自动配置机制应用非常广泛。在目前主流的开源框架中,都提供了各自的 starter 组件。例如,MyBatis 的 starter 组件为 mybatis-spring-boot-starter。而从扩展性上讲,这也是 Spring Boot 为开发人员提供的一整套扩展机制,我们可以基于这套扩展机制实现自定义的 starter 组件。

Spring Boot 自动配置机制原理

Spring Boot 的自动配置功能非常强大,但也有一定的复杂度,让我们先来深入理解其背后的实现原理。

@EnableAutoConfiguration 注解

我们通过查看@SpringBootApplication 注解的定义,发现该注解实际上是一个复合注解,由@SpringBootConfiguration、@ComponentScan 和@EnableAutoConfiguration 这三个独立注解所组成。

图 1 @SpringBootApplication 注解的组成结构图 1 @SpringBootApplication 注解的组成结构

我们知道@ComponentScan 是传统 Spring 框架中对内置的注解,而@SpringBootConfiguration 注解也很简单,实际上只是对 Spring 框架中另一个常用注解@Configuration 的一种包装,本身没有定义任何内容。所以,我们接下来重点剖析@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 {};
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

可以看到,这里出现了一个新的注解,即@AutoConfigurationPackage。从命名上讲, @AutoConfigurationPackage 注解的作用就是自动对某一个代码包进行配置。

另一方面,我们还看到这里通过@Import 注解引入了一个 AutoConfigurationImportSelector 类。从命名上,我们也不难理解该类的作用是完成对导入的配置信息的自动选择。AutoConfigurationImportSelector 类的核心方法 getCandidateConfigurations 实现了这一目标。

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
       List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
       …
       return configurations;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

这里引出了在 Spring Boot 中真正负责加载配置信息的 SpringFactoriesLoader 类。这些类之间的交互关系如图 2 所示:

图 2 AutoConfigurationImportSelector 类层结构图图 2 AutoConfigurationImportSelector 类层结构图

显然,想要完成配置信息的自动选择,我们首先需要执行配置文件的加载操作,这部分功能是由 SpringFactoriesLoader 来完成的。SpringFactoriesLoader 也是 Spring Boot 自动配置得以实现的关键组件,我们来一起看一下。

SpringFactoriesLoader

SpringFactoriesLoader 类似 JDK 实现 SPI 机制时所使用的 ServiceLoader 类,区别只是配置文件的存放位置和配置项对应的键值定义不同。在 SpringFactoriesLoader 中,我们需要通过 META-INF/spring.factories 文件目录,来获取服务定义文件,并通过 EnableAutoConfiguration 这个配置键来获取具体的配置信息。图 3 展示了 SpringFactoriesLoader 和 ServiceLoader 之间的区别。

图 3 SpringFactoriesLoader 和 ServiceLoader 区别示意图图 3 SpringFactoriesLoader 和 ServiceLoader 区别示意图

图 3 SpringFactoriesLoader 和 ServiceLoader 区别示意图

SpringFactoriesLoader 基于图 3 指定的配置文件名和配置键获取对应的配置信息,然后基于这些配置信息来实例化配置类,这里 Spring Boot 用到的是反射机制。SpringFactoriesLoader 类中的 loadSpringFactories 方法展示了这一过程。

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
       //从缓存中获取配置内容,如果存在则直接返回
       try {
              //基于 ClassLoader 从 META-INF/spring.factories 获取配置文件资源地址 URL
              while (urls.hasMoreElements()) {
                     //获取配置文件资源,加载配置项
                     for (Map.Entry<?, ?> entry : properties.entrySet()) {
                     //组装配置项 Key-Value
                     }
              }
              //把配置信息放入缓存
              //返回结果
       }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.

同时,我们在 spring-boot-autoconfigure 工程的 spring.factories 配置文件中找到了如下所示配置项。

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoCnotallow=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
…
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.

可以看到,EnableAutoConfiguration 配置项中,定义了各种以-AutoConfiguration 结尾的配置类。通过 SpringFactoriesLoader,Spring Boot 就能做到在服务启动的时候把它们加载到容器中并实现自动化配置。

MyBatis Spring Boot Starter

介绍完 Spring Boot 中应用程序的自动配置机制之后,我们来做一些实践,通过剖析 MyBatis Spring Boot Starter 的启动过程来加深对内容的理解。

在 mybatis-spring-boot-starter 中存在几个代码工程,我们重点关注 mybatis-spring-boot-autoconfigure 工程。而在这个代码工程中,最重要的显然就是 MybatisAutoConfiguration 类。对于 Spring Boot 中的 AutoConfiguration 类,我们首先需要重点关注的是类定义上的注解。

@org.springframework.context.annotation.Configuration
@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })
@ConditionalOnSingleCandidate(DataSource.class)
@EnableConfigurationProperties(MybatisProperties.class)
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
public class MybatisAutoConfiguration implements InitializingBean {
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

我们看到这里用到了@ConditionalOnClass 和@ConditionalOnSingleCandidate 这两个注解,它们就是 Spring Boot 中的条件注解。在介绍 MybatisAutoConfiguration 之前,有必要对这些注解做一定展开。

@ConditionalOn 系列条件注解

我们在前面的内容中已经了解到以-AutoConfiguration 结尾的自动配置类数量会很多,在一个应用程序的开发过程中,我们通常不会全部用到它们。这时候就需要引入一种机制来对这些自动配置类进行过滤。为此,Spring Boot 提供了一组@ConditionalOn 系列条件注解。通过这些注解,我们就可以基于特定的条件有选择性地加载某些配置类。在 Spring Boot 中常见的条件注解可以参考图 4。

图 4 常见@ConditionalOn 系列注解及其作用图 4 常见@ConditionalOn 系列注解及其作用

在前面介绍的 MybatisAutoConfiguration 类的时候,出现了@ConditionalOnClass 和@ConditionalOnSingleCandidate 这两个条件注解。基于这两个条件注解,我们可以明确 MybatisAutoConfiguration 能够实例化的前提有两个:一是类路径中存在 SqlSessionFactory 和 SqlSessionFactoryBean;另一个则是容器中只存在一个 DataSource 实例。两者缺一不可,这是一种常用的自动配置控制技巧。

然后,我们在 MybatisAutoConfiguration 类上看到了一个@EnableConfigurationProperties 注解。通过这个注解,所有添加了@ConfigurationProperties 注解的配置类就会自动生效。这里的@EnableConfigurationProperties 注解中指定的是 MybatisProperties 类,该类定义了 MyBatis 运行时所需要的各种配置信息,而我们在 MybatisProperties 类上确实也发现了@ConfigurationProperties 注解,并设置了 prefix 为“mybatis”。

@ConfigurationProperties(
     prefix = "mybatis"
)
public class MybatisProperties {
       ...
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

最后,在 MybatisAutoConfiguration 类上还存在一个@AutoConfigureAfter 注解,这个注解可以根据字面意思进行理解,即在完成某一个类的自动配置之后再执行当前类的自动配置。这个需要提前装配的类指的就是 DataSourceAutoConfiguration。

MybatisAutoConfiguration

理解了@ConditionalOnXXX、@EnableConfigurationProperties 和@AutoConfigureAfter 等一系列注解之后,我们回过头来再看 MybatisAutoConfiguration 类的代码结构就显得比较简单了。MybatisAutoConfiguration 类中的一个核心方法就是如下所示的 sqlSessionFactory 方法。

@Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
    SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
    factory.setDataSource(dataSource);
    factory.setVfs(SpringBootVFS.class);
    if (StringUtils.hasText(this.properties.getConfigLocation())) {
      factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
    }
    applyConfiguration(factory);
    //省略一系列配置项设置方法
    return factory.getObject();
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.

显然,这里基于前面介绍的 SqlSessionFactoryBean 构建了 SqlSessionFactory 实例。注意到在该方法上同样添加了一个@ConditionalOnMissingBean 注解,标明只有在当前上下文中不存在 SqlSessionFactoryBean 对象时才会执行上述方法。

同样,添加了@ConditionalOnMissingBean 注解的,还有如下所示的 sqlSessionTemplate 方法。

@Bean
@ConditionalOnMissingBean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
    ExecutorType executorType = this.properties.getExecutorType();
    if (executorType != null) {
      return new SqlSessionTemplate(sqlSessionFactory, executorType);
    } else {
      return new SqlSessionTemplate(sqlSessionFactory);
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.

该方法用于构建一个 SqlSessionTemplate 对象实例。在 MyBatis 中,SqlSessionTemplate 实现了 SqlSession 接口,相当于是全局唯一的 SqlSession 实例。

接下来,我们需要在 META-INF/spring.factories 文件中明确所指定的自动配置类。根据 Spring Boot 自动配置机制的原理,对于 mybatis-spring-boot-autoconfigure 工程而言,这个配置项内容应该如下所示。

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoCnotallow=\
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
  • 1.
  • 2.
  • 3.

至此,整个 MyBatis Spring Boot Starter 的介绍就告一段落。作为总结,我们可以把创建一个 Spring Boot Starter 的过程抽象成三个步骤。

图 5 创建 Spring Boot Starter 的三个步骤图 5 创建 Spring Boot Starter 的三个步骤

在日常开发过程中,我们就可以基于这三大步骤来实现一个自定义的 Spring Boot Starter。

总结

今天,我们详细阐述了 Spring Boot 自动配置机制的实现原理,从源码角度分析了为什么 Spring Boot 能够做到自动配置,并结合 MyBatis 框架分析了它在开源框架中的具体应用。同时,我们在本讲结尾部分还总结了开发一个 Spring Boot Starter 的三大步骤。实现一个自定义 Spring Boot Starter 也是日常开发的常见需求,我们在开发过程中可以基于本讲的内容加深对其实现原理的理解。

最后,我想给你留一道思考题:你能简要描述实现一个自定义 Spring Boot Starter 需要哪些开发步骤吗?

责任编辑:武晓燕 来源: 程序猿技术充电站
相关推荐

2023-10-18 08:12:34

Spring自动配置

2024-12-25 16:01:01

2021-08-06 08:04:14

Spring Boot自动配置

2017-03-23 09:29:06

2021-02-11 08:08:09

Spring Boot配置架构

2024-01-04 08:16:34

Spring国际化标准

2017-04-26 11:00:34

Spring BootHelloWorld详解

2024-04-23 14:13:38

开发配置文件

2023-12-27 18:05:13

2024-12-16 08:10:00

Spring开发

2019-09-03 15:36:58

ApacheTomcat配置

2020-07-08 13:46:27

ApacheTomcat配置

2025-02-28 08:14:53

2017-09-20 09:46:38

Spring BootSpring Clou内存

2020-11-02 07:00:29

Spring Boo注解自动化

2025-02-13 07:45:26

APISpringHTTP

2024-11-21 14:42:31

2024-10-14 17:18:27

2025-01-15 08:19:12

SpringBootRedis开源

2017-06-26 08:47:05

Spring Boot自动化配置
点赞
收藏

51CTO技术栈公众号