Posted in Spring Boot, 技术

Spring Boot 2.0 自动配置原理浅析

本章内容

  • 外化配置和自动配置
  • 自动配置原理浅析
  • Starter 组件浅析
  • 小结

阅读时间:6 分钟

摘录:至简,记住你即将死去

1. 外化配置和自动配置

Spring Boot 配置,包括自动配置和外化配置。

比如常见的,将属性外化配置在 application.properties 应用配置文件,然后在工程中获取该属性值。Spring Boot 可以将配置外部化,这种模式叫做 “外化配置”。将配置从代码中分离外置,最明显的作用是只要简单地修改下外化配置文件,就可以在不同环境中,可以运行相同的应用代码。具体见《Spring Boot 配置文件 – 在坑中实践》:
http://www.spring4all.com/article/267

那自动配置呢

Spring Boot spring-boot-autoconfigure 依赖做了很多默认的配置项,即应用默认值。这种模式叫做 “自动配置”。Spring Boot 自动配置会根据添加的依赖,自动加载依赖相关的配置属性并启动依赖。例如默认用的内嵌式容器是 Tomcat ,端口默认设置为 8080。

为什么需要自动配置?顾名思义,自动配置的意义是利用这种模式代替了配置 XML 繁琐模式。以前使用 Spring MVC ,需要进行配置组件扫描、调度器、视图解析器等,使用 Spring Boot 自动配置后,只需要添加 MVC 组件即可自动配置所需要的 Bean。所有自动配置的实现都在 spring-boot-autoconfigure 依赖中,包括 Spring MVC 、Data 和其它框架的自动配置。

经过和 DD 讨论,DD 原话:
1. External Configuration指的不是把配置内容分离到properties文件里,而是配置存储在classpath之外,比如spring cloud config的服务器中
2. 自动化配置本身包含了两块内容:@Configuration的定义和properties属性的定义,外部化配置是跟加载过程相关的。

感谢DD~

2. 自动配置原理浅析

spring-boot-autoconfigure 依赖

spring-boot-autoconfigure 依赖,是 Spring Boot 实现自动配置的核心 Starter 组件。如图是它的依赖包目录:

可以看出很多常用框架的自动配置包目录:org.springframework.boot.autoconfigure.thymeleaforg.springframework.boot.autoconfigure.data.jpa

那进一步打开包下对应的自动配置类,可以看到有:Jpa 自动配置类 JpaRepositoriesAutoConfiguration、Thymeleaf 自动配置类 ThymeleafAutoConfiguration

spring-boot-autoconfigure 依赖的工作原理很简单,通过 @EnableAutoConfiguration 核心注解初始化,并扫描 ClassPath 目录中自动配置类对应依赖。比如工程中有木有添加 Thymeleaf 的 Starter 组件依赖。如果有,就按按一定规则获取默认配置并自动初始化所需要的 Bean。

那具体 @EnableAutoConfiguration 核心注解的工作原理是咋样的呢?

@EnableAutoConfiguration 注解

@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 {};
}

@EnableAutoConfiguration 注解核心点是 @Import 的自动配置导入选择器类 AutoConfigurationImportSelector 。其代码部分如下:

    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        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;
    }

总结可得具体流程如下:

  • AutoConfigurationImportSelector 通过 SpringFactoriesLoader.loadFactoryNames() 核心方法读取 ClassPath 目录下面的 META-INF/spring.factories 文件。
  • spring.factories 文件中配置的 Spring Boot 自动配置类,例如常见的Jpa 自动配置类 JpaRepositoriesAutoConfiguration、Thymeleaf 自动配置类 ThymeleafAutoConfigurationWebMvcAutoConfiguration Web MVC 自动配置类和ServletWebServerFactoryAutoConfiguration 容器自动配置类等 。
  • spring.factories 文件和 application.properties 文件都属于配置文件,配置的格式均为键值对。里面配置的每个自动配置类都会定义相关 Bean 的实例配置,也会定义什么条件下自动配置和哪些 Bean 被实例化。
  • 当 pom.xml 添加某 Starter 依赖组件的时候,就会自动触发该依赖的默认配置。

具体 Starter 组件依赖是如何触发它的默认配置的呢?

3. Starter 组件浅析

Starter 组件

Spring Boot 提供了很多 “开箱即用” 的 Starter 组件。Starter 组件是可被加载在应用中的 Maven 依赖项。只需要在 Maven 配置中添加对应的依赖配置,即可使用对应的 Starter 组件。例如,添加 spring-boot-starter-web 依赖,就可用于构建 REST API 服务,其包含了 Spring MVC 和 Tomcat 内嵌容器等。

一个完整的 Starter 组件包括以下两点:

  • 提供自动配置功能的自动配置模块。
  • 提供依赖关系管理功能的组件模块,即封装了组件所有功能,开箱即用。

spring-boot-starter-web 依赖源码

当我们添加 spring-boot-starter-web 依赖,并启动应用会触发容器自动配置类。容器自动配置类 ServletWebServerFactoryAutoConfiguration 的部分代码如下:

package org.springframework.boot.autoconfigure.web.servlet;

@Configuration
@ConditionalOnClass({ServletRequest.class})
@ConditionalOnWebApplication(
    type = Type.SERVLET
)
@EnableConfigurationProperties({ServerProperties.class})
@Import({ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class})
public class ServletWebServerFactoryAutoConfiguration {
... 省略
}

上面代码源码浅析下:

  • @ConditionalOnClass 注解表示对应的 ServletRequest 类在 ClassPath 目录下面存在,并且 @ConditionalOnWebApplication 注解表示该应用是 Servlet Web 应用时,才会去启动容器默认配置
  • 通过 ServerProperties 类默认设置了端口为 8080
  • Type.SERVLET 枚举代表 Servlet Web 应用,Type.REACTIVE 枚举代表响应式 WebFlux 应用。

@ConditionalOnClass 注解类似功能的还有 @ConditionalOnMissingBean@ConditionalOnProperty等注解。这里不一一列出解释。

4. 小结

自动配置,是一把双刃剑。用好了就像,天下武功唯快不破一样;用不好就需要注意一些自动化配置造成的问题。常见的问题常见有:

  • Spring Boot 工程添加某些 Starter 组件依赖,但不想触发组件自动配置
  • Spring Boot 配置多个不同数据源配置时,比如使用 XML 配置多数据源,但其默认数据源配置会触发自动配置出现问题。

类似场景下,解决方式是排除不需要的特定自动配置类。通过 exclude 属性指定并排除自动配置类,代码如下:

@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})

也等价于配置在 @EnableAutoConfiguration 注解,代码如下:

@SpringBootApplication
@EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class})

自动配置会最大的智能化,当配置了 exclude 属性时,Spring Boot 优先初始化用户定义的 Bean ,然后在进行自动化配置。

### 关注即可得系列教程文章



发表评论

电子邮件地址不会被公开。 必填项已用*标注