Spring Boot 自动化配置

自动化配置绝对是Spring 4.0最重要的新特性,没有之一。正是基于自动化配置,SpringBoot才实现了开箱即用,大幅提升了开发效率。

自动化配置原理解析

  1. 自动化配置是通过引入@EnableAutoConfiguration注解实现的
  2. @SpringBootApplication中已经包含了该注解,因此SpringBoot应用自带自动化配置特性
  3. 在 spring-boot-autoconfigure-x.x.x.RELEASE.jar/META-INF/spring.factories 文件的 org.springframework.boot.autoconfigure.EnableAutoConfiguration 配置项中定义了候选配置类。之所以说是候选,是因为还要根据配置条件来决定
  4. 配置条件定义在 spring-boot-autoconfigure-x.x.x.RELEASE.jar/META-INF/spring-autoconfigure-metadata.properties 文件中
  5. 读取所有的配置条件,加上配置类中声明的条件,去重,配置项排序:先按字典顺序,再按@AutoConfigureOrder,再考虑@AutoConfigureBefore和@AutoConfigureAfter
  6. 根据顺序加载候选配置类,最终满足条件的候选配置加载到spring上下文

自动化配置的加载时机

自动化配置是在SpringBoot生命周期的started阶段完成的,代码入口是ConfigurationClassParser.parse(),这里会解析所有配置类,当解析到注解中包含@Import(XXXImportSelector.class)时,就会调用相应的ImportSelector.selectImports()来获取具体需要自动化配置的类。

再看@EnableAutoConfiguration注解,里面包含了EnableAutoConfigurationImportSelector,自动化配置的核心代码都在这里。

关于ImportSelector和DeferredImportSelector

  • ImportSelector的作用是筛选出所有需要进行配置的类
  • DeferredImportSelector继承了ImportSelector,可以实现延迟配置

这里所谓的“延迟”是指延迟到其他所有的bean都注册完毕(相关代码在ConfigurationClassParser.parse())。

EnableAutoConfigurationImportSelector实现了DeferredImportSelector,因此自动化配置的bean一定是在最后加载。

条件化配置

在 org.springframework.boot.autoconfigure.condition 包中定义了许多与条件相关的注解,举例如下:

注解含义
@ConditionalOnProperty当某个Property存在并等于某个值时配置
@ConditionalOnJava当Java版本满足条件时配置
@ConditionalOnBean当Spring上下文中存在某个bean时才进行配置
@ConditionalOnClass当classpath中存在某个类时才进行配置

在 org.springframework.boot.autoconfigure 包中定义了一些与配置顺序相关的注解,举例如下:

注解含义
@AutoConfigureBefore表示该配置类在指定的类之前配置
@AutoConfigureAfter表示该配置类在指定的类之后配置
@AutoConfigureOrder自动配置顺序

条件可以写在@Configuration、@Bean或 META-INF/spring-autoconfigure-metadata.properties 文件中。

举个栗子🌰

// WebMVC的自动化配置类,这个类有在spring.factories中被定义
@Configuration(proxyBeanMethods = false)
// 只有在应用类型是Servlet时才配置
@ConditionalOnWebApplication(type = Type.SERVLET)
// 当classpath中存在以下几个类时进行配置
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
// 当BeanFactory中不存在WebMvcConfigurationSupport时配置(这个类用于覆盖默认web配置)
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
// 定义配置顺序
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
// 定义配置顺序
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
        ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {
    // ......
}

条件化配置与profile的区别

两者都可以实现在满足一定条件下对bean进行配置,但区别如下:

  1. profile在spring 3.0就支持了,条件化配置在spring 4.0才支持(公司现在还在使用spring3,所以别无选择😭)
  2. profile相当于给bean打标签,开关也是基于标签的,灵活度较低,当标签数量较多时,还是需要相当的维护成本
  3. 条件化配置更加灵活,支持多种条件,且条件直接写在配置类上,代码可读性上会更好
  4. 在使用场景上,条件化配置更多适用于技术框架等,需要把jar包提供给其他人使用时,条件化配置可以有效减少使用方的配置;而profile更多适用于项目内部,例如对不同的运行环境打上标签,每个环境设置不同的数据库连接配置等。
  5. profile可以在启动脚本中设置,也就是在运行时才决定,当然也可以在编译时决定。但是条件化配置好像只能在编译时决定。

扩展阅读

其实 @Conditional 注解才是真正的核心,当spring提供的自动化配置注解都无法满足需求时,不妨可以考虑使用 @Conditional。

Last Updated 2024/11/24 20:50:53