深入Spring Boot:编写兼容Spring Boot1和Spring Boot2的Starter

为什么选择starter同时兼容spring boot 1和spring boot 2

  • 从用户角度来看

如果不在一个starter里兼容,比如用版本号来区分,spring boot 1的用户使用1.,spring boot 2用户使用2.,这样用户升级会有很大困扰。

另外,我们的starter是以日期为版本号的,如果再分化,则就会出现2018-06-stable-boot1,2018-06-stable-boot2,这样子很丑陋。

  • 从开发者角度来看

要同时维护两个分支,修改代码时要合到两个分支上,发版本时要同时两个。如果有统一的bom文件,也需要维护两份。工作量翻倍,而且很容易出错。

因此,我们决定在同一个代码分支里,同时支持spring boot 1/2。减少开发维护成本,减少用户使用困扰。

编写兼容的starter的难点

spring boot starter的代码入口都是在各种@Configuration类里,这为我们编写兼容starter提供了条件。

但还是有一些难点:

  • 某些类不兼容,比如在spring boot 2里删除掉了
  • 代码模块,maven依赖怎样组织
  • 怎样保证starter在spring boot 1/2里都能正常工作

通过ASM分析现有的starter里不兼容的类

springboot-classchecker可以从jar包里扫描出哪些类在spring boot 2里不存在的。

工作原理:springboot-classchecker自身在pom.xml里依赖的是spring boot 2,扫描jar包里通过ASM分析到所有的String,提取出类名之后,再尝试在ClassLoader里加载,如果加载不到,则说明这个类在spring boot 2里不存在。

例如扫描demo-springboot1-starter.jar:

mvn clean package
java -jar target/classchecker-0.0.1-SNAPSHOT.jar demo-springboot1-starter.jar

结果是:

path: demo-springboot1-starter.jar
org.springframework.boot.actuate.autoconfigure.ConditionalOnEnabledHealthIndicator
org.springframework.boot.actuate.autoconfigure.EndpointAutoConfiguration
org.springframework.boot.actuate.autoconfigure.HealthIndicatorAutoConfiguration

那么这些类在spring boot 2在哪里了?
实际上是改了package:

org.springframework.boot.actuate.autoconfigure.health.ConditionalOnEnabledHealthIndicator
org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration
org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration

通过扫描20多个starter jar包,发现不兼容的类有:

  • org.springframework.boot.env.PropertySourcesLoader
  • org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder
  • org.springframework.boot.bind.RelaxedDataBinder
  • Endpoint/HealthIndicator 相关的类

可以总结:

  • spring boot核心的类,autoconfigure相关的没有改动
  • 大部分修改的是Endpoint/HealthIndicator 相关的类

spring-boot-utils兼容工具类

BinderUtils

在spring boot 1里,注入环境变量有时需要用到RelaxedDataBinder:

MyProperties myProperties = new MyProperties();
MutablePropertySources propertySources = environment.getPropertySources();
new RelaxedDataBinder(myProperties, "spring.my").bind(new PropertySourcesPropertyValues(propertySources));

在spring boot 2里,RelaxedDataBinder删除掉了,新的写法是用Binder:

Binder binder = Binder.get(environment);
MyProperties myProperties = binder.bind("spring.my", MyProperties.class).get();

通过BinderUtils,则可以同时支持spring boot1/2:

MyProperties myProperties = BinderUtils.bind(environment, "spring.my", MyProperties.class);

Starter代码模块组织

下面以实际的一个starter来说明。

spring boot web应用的mappings信息,可以在/mappings endpoint查询到。但是这么多endpoint,它们都提供了哪些url?
endpoints-spring-boot-starter的功能是展示所有endpoints的url mappings信息

endpoints-spring-boot-starter里需要给spring boot 1/2同时提供endpoint功能,代码模块如下:

endpoints-spring-boot-starter
|__ endpoints-spring-boot-autoconfigure1
|__ endpoints-spring-boot-autoconfigure2
  • endpoints-spring-boot-autoconfigure1模块在pom.xml里依赖的是spring boot 1相关的jar,并且都设置为<optional>true</optional>
  • endpoints-spring-boot-autoconfigure2的配置类似
  • endpoints-spring-boot-starter依赖autoconfigure1 和 autoconfigure2
  • 如果有公共的逻辑,可以增加一个commons模块

Endpoint兼容

以endpoints-spring-boot-autoconfigure1模块为例说明怎样处理。

  • EndPointsEndPoint类继承自spring boot 1的AbstractMvcEndpoint:
@ConfigurationProperties("endpoints.endpoints")
public class EndPointsEndPoint extends AbstractMvcEndpoint {
  • 通过@ManagementContextConfiguration引入
@ManagementContextConfiguration
public class EndPointsEndPointManagementContextConfiguration {

  @Bean
  @ConditionalOnMissingBean
  @ConditionalOnEnabledEndpoint("endpoints")
  public EndPointsEndPoint EndPointsEndPoint() {
      EndPointsEndPoint endPointsEndPoint = new EndPointsEndPoint();
      return endPointsEndPoint;
  }

}
  • 在META-INF/resources/spring.factories里配置
org.springframework.boot.actuate.autoconfigure.ManagementContextConfiguration=\
io.github.hengyunabc.endpoints.autoconfigure1.EndPointsEndPointManagementContextConfiguration

因为org.springframework.boot.actuate.autoconfigure.ManagementContextConfiguration是只在spring boot 1里,在spring boot 2的应用里不会加载它,所以autoconfigure1模块天然兼容spring boot 2。

那么类似的,autoconfigure2模块里在**META-INF/resources/spring.factories**配置的是
org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration=\
io.github.hengyunabc.endpoints.autoconfigure2.ManagementApplicationcontextHolderConfiguration

仔细对比,可以发现是spring boot 2下面修改了ManagementContextConfiguration的包名,所以对于Endpoint天然是兼容的,不同的模块自己编绎就可以了。

HealthIndicator的兼容

类似Endpoint的处理,spring boot 1/2的代码分别放不同的autoconfigure模块里,然后各自的@Configuration类分别使用@ConditionalOnSpringBoot1/@ConditionalOnSpringBoot2来判断。

通过集成测试保证兼容性

还是以endpoints-spring-boot-autoconfigure1模块为例。

这个模块是为spring boot 1准备的,则它的集成测试要配置为spring boot 2。

参考相关的代码:查看

  • springboot2demo/pom.xml里依赖spring boot 2
  • verify.groovy里检测应用是否启动成功

总结

  • 通过ASM分析现有的starter里不兼容的类
  • 配置注入通过BinderUtils解决
  • 各自的@Configuration类分别用@ConditionalOnSpringBoot1/@ConditionalOnSpringBoot2来判断
  • 代码分模块:commons放公共逻辑, autoconfigure1/autoconfigure2 对应 spring boot 1/2的自动装配,starter给应用依赖
  • Endpoint的Configuration入口是ManagementContextConfiguration,因为spring boot 2里修改了package,所以直接在spring.factories里配置即可
  • 通过集成测试保证兼容性
  • 如果某一天,不再需要支持spring boot 1了,则直接把autoconfigure1模块去掉即可
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容