SpringBoot2 升级 Starter 影响
背景
升级 SpringBoot2,对于 starter 是否有不兼容的地方
升级指南
Spring Boot 2.0 Migration Guide · spring-projects/spring-boot Wiki
官方给出的迁移指南,这里选取一些常见的、可能存在影响的改动点陈列
依赖版本
SpringBoot2 的依赖表 Appendix F. Dependency versions
其中 Spring 框架的版本升级到了 5.x,所以对于 Java 6 和 7 不再支持了
Maven 插件相关的命名和 Surefire 使用方式
以下库的最低支持版本已经更改
- Elasticsearch 5.6
- Gradle 4
- Hibernate 5.2
- Jetty 9.4
- Spring Framework 5
- Spring Security 5
- Tomcat 8.5
特性
动态代理
默认使用 CGLIB 实现,如果需要使用 JDK 基于接口的动态代理实现
设置配置 spring.aop.proxy-target-class
为
false
事件
新增了事件
ApplicationStartedEvent
在上下文刷新后,但在调用任何应用程序和命令行运行器之前
ApplicationReadyEvent
在任何应用程序和命令行运行器被调用后
ApplicationRunner 和 CommandLineRunner
当需要执行特殊逻辑在 Application 启动(started)后,可以实现
ApplicationRunner
或 CommandLineRunner
接口,提供的 run
方法会在
SpringApplication.run(…)
完成之前被调用
非常适合在应用程序启动后但在开始接收流量之前运行的任务
1 | import org.springframework.boot.CommandLineRunner; |
外部配置
绑定收紧
有关宽松绑定的规则变得更加严格,假设有一个
acme.my-project.my-name
属性
- 前缀必须是烤串命名;
acme.myProject
或acme.my_project
是无效的 - 属性名可以使用烤串、驼峰或者蛇形命名
- 环境属性必须使用常规的大写下划线格式;例如
ACME_MYPROJECT_MYNAME
概括起来是 prefix 严格,属性宽松
@ConfigurationProperties 校验
如果要打开校验,需要在 @ConfigurationProperties
对象上添加注解 @Validated
Web 应用
Jackson
修改了 Jackson 配置默认值,将 JSR-310 日期写成 ISO-8601 字符串
设置
pring.jackson.serialization.write-dates-as-timestamps
为
true
可以使用旧配置
spring-boot-starter-json
这个新的 starter 整合了读写
JSON 的必要部分,提供了 Jackson
使用的相关模块依赖,如果之前手动依赖这些模块,现在可以改为依赖这个新的
starter
Spring MVC 路径匹配
禁用了后缀匹配
GET /projects/spring-boot.json
不会匹配到
@GetMapping("/projects/spring-boot")
测试
Mockito 1.x
Mockito 1.x 不再支持 @MockBean
和 @SpyBean
了
如果没有使用 spring-boot-starter-test
管理依赖,需要升级到 Mockito 2.x
EnvironmentTestUtils
EnvironmentTestUtils
弃用了,使用
TestPropertyValues
提供更强大的能力
1 | TestPropertyValues.of("acme.first=1", "acme.second=2") |
自定义 Auto-configuration
@ConditionalOnBean 语意变更
@ConditionalOnBean
不再表达候选 bean 或
的含义,而是 与
的含义,即多个条件满足才生效
如果需要保持存在任何目标 bean 的条件,可以考虑使用如以下示例所示的
AnyNestedCondition
1 | class ThisOrThatCondition extends AnyNestedCondition { |
文档
从升级指南来看,涉及 starter 调整的只有
@ConditionalOnBean
语意变化一点,所以还是回归开发文档,看下相关章节
理解自动装配 bean
实现自动配置的类使用 @AutoConfiguration
(SpringBoot2
才有)进行注解,这个注解本身又被 @Configuration
进行元注解,使得自动配置成为标准的 @Configuration
类;额外的 @Conditional
注解用于约束自动配置何时应用
定位自动装配候选
SpringBoot 会检查已发布的 jar 包中是否存在一个名为
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
的文件
该文件应当列出你的配置类,每行一个类名,如下例所示
1 | com.mycorp.libx.autoconfigure.LibXAutoConfiguration |
如果自动装配类需要特定顺序应用,可以使用
@AutoConfiguration
注解的before
, beforeName
, after
and afterName
属性,或者专有的 @AutoConfigureBefore
和
@AutoConfigureAfter
注解;举个例子,如果希望提供 Web
特殊的装配,那么这个类可能需要在 WebMvcAutoConfiguration
之后被应用(applied)
如果想要对某些不相互直接了解的自动配置进行排序,也可以使用@AutoConfigureOrder
;该注解与常规的
@Order
注解具有相同的语义,但为自动配置类提供了专用的排序
和标准的 @Configuration
类一样,顺序只影响 beans
之间的应用,不影响其创建的顺序;创建的顺序由每个 bean 的依赖关系和任何
@DependsOn
关系决定
条件注解
对自动装配生效的规则控制
- Class Conditions:ASM 解析运行中的类
- @ConditionalOnClass:存在类
- @ConditionalOnMissingClass:不存在类
- Bean Conditions:扫描 beans,可以使用
search
参数指定目标层级- @ConditionalOnBean:存在 bean
- @ConditionalOnMissingBean:不存在 bean
- Property Conditions
- @ConditionalOnProperty:检查属性;更多规则使用
havingValue
和matchIfMissing
参数
- @ConditionalOnProperty:检查属性;更多规则使用
- Resource Conditions
- @ConditionalOnResource:资源可以使用常见的
Spring 约定来指定,如以下示例所示
file:/home/user/test.dat
- @ConditionalOnResource:资源可以使用常见的
Spring 约定来指定,如以下示例所示
- Web Application Conditions
- @ConditionalOnWebApplication:Web 应用
- @ConditionalOnNotWebApplication:非 Web 应用
- SpEL Expression Conditions
- @ConditionalOnExpression:使用 SpEL 检查配置
测试自动装配
自动装配会受到很多因素影响,比如配置文件、条件环境(依赖库)等等因素;所以通常来说,每个测试应该创建一个完整定义的
ApplicationContext
,它代表了这些定制的组合,ApplicationContextRunner
提供了一种良好的实现方式
1 | private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() |
每个测试都可以使用运行器来表示特定的用例。例如,下面的示例调用用户配置(UserConfiguration
)并检查自动配置是否正确回退
调用 run
提供了一个回调上下文,可以与
Assert
一起使用
1 |
|
自定义 Environment
1 |
|
使用 FilteredClassLoader
过滤类路径来测试类相关条件
1 |
|
自定义自动装配
命名
不要以 spring-boot
开头,因为未来可能被官方收录
应该以业务名称开头,例如 acme-spring-boot-starter
配置键
不要使用官方的命名空间避免冲突,应该使用业务命名例如
acme
确保通过为每个属性添加字段 JavaDoc 来记录配置键
1 |
|
一些建议
- 不要以 The 或 A 开头来写描述
- 对于
boolean
类型描述为 “是否” 或者 “启用” - 对于集合类型,以 “逗号分隔列表” 为描述开头
- 使用
java.time.Duration
而不是long
类型,并在默认单位与毫秒不同时描述该默认单位,例如 “如果未指定持续时间后缀,则将使用秒” - 除非在运行时必须确定默认值,否则不要在描述中提供默认值
自动装配 Module
SpringBoot
使用一个注解处理器在元数据文件(META-INF/spring-autoconfigure-metadata.properties
)中收集自动配置上的条件,如果该文件存在,它将用于快速过滤不匹配的自动配置,这将提高启动时间
使用 Maven 进行构建时,建议在包含自动配置的模块中添加以下依赖项
1 | <dependency> |
如果直接在项目中定义了自动装配,确保配置了
spring-boot-maven-plugin
防止repackage
目标将依赖项添加到 fat jar 中(避免把条件中的 class 打入 jar)
1 | <project> |
Starter Module
- SpringBoot starter 是一个空 jar(不包含业务逻辑),用于提供一组默认依赖
- 应该包含所有必要依赖,但避免包含可选依赖;不能预设使用方的依赖情况
- 必须直接或间接依赖
spring-boot-starter
,以确保 SpringBoot 核心功能可用 - 通常与自动配置结合使用,提供开箱即用的功能
源码
spring-boot 2.7.x
org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#getCandidateConfigurations 的 Doc 直接就标明了
返回应被考虑的自动配置类名称。默认情况下,此方法将使用
ImportCandidates
和getSpringFactoriesLoaderFactoryClass()
加载候选类。为了向后兼容,它还会考虑SpringFactoriesLoader
和getSpringFactoriesLoaderFactoryClass()
1 | protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { |
参考
Spring Boot 2.0 Migration Guide · spring-projects/spring-boot Wiki