前言
正文開始前,我們做個小測試,假設我們封裝了一個springboot starter,其自動裝配類形如下內容
@Configuration
@EnableConfigurationProperties({ApolloRefreshProperties.class})
public class ApolloRefreshAutoConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnClass({ConfigService.class})
public ApolloRefreshService apolloRefreshService(ApolloRefreshProperties properties) {
return new ApolloRefreshService(properties);
}
}
該starter的pom引入的apollo gav是optional
<dependency>
<groupId>com.ctrip.framework.apollo</groupId>
<artifactId>apollo-client</artifactId>
<version>${apollo-client.version}</version>
<optional>true</optional>
</dependency>
我的問題是
在執行環境為jdk8的springboot專案引入上述的starter,是否會有問題?
我們執行一下,發現會出現
然後我們不改任何一行程式碼,把JDK調成11或者以上版本,再執行
專案成功執行。那我們的修復的第一直覺是不是把JDK8的版本提高。
我們團隊的小夥伴第一時間也是這麼幹的,他去和業務團隊的技術經理溝通,看他們能不能把JDK8調整成JDK11,然後得到了業務團隊技術經理的高度否定,因為他們大部分業務都跑在jdk8,冒然升級成jdk11,也不知道會不會因為了解決一個問題,而引入其他問題
問題排查
因為這個starter的自動裝配配置的內容相對簡單,基於老司機的第六感,問題大機率是出現在@ConditionalOnClass這注解上,於是點開@ConditionalOnClass,他的註解上有如下提示
他的大意是,可以在@Configuration classes上安全地指定value(),因為在載入類之前會使用ASM解析註釋後設資料。當放置在@Bean方法上時,需要格外小心,請考慮在單獨的Configuration類中隔離條件,特別是當方法的返回型別與條件的目標匹配時。如果非要用方法註解,建議使用ConditionalOnClass裡面的name欄位
於是我們聽官方的建議,將starter調整如下
@Configuration
@EnableConfigurationProperties({ApolloRefreshProperties.class})
public class ApolloRefreshAutoConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnClass(name = "com.ctrip.framework.apollo.ConfigService")
public ApolloRefreshService apolloRefreshService(ApolloRefreshProperties properties) {
return new ApolloRefreshService(properties);
}
}
再次執行,果然不再報錯。具體問題原因,我就不班門弄斧了,可以檢視官方的issue
https://github.com/spring-projects/spring-boot/issues/27846
https://github.com/spring-projects/spring-boot/issues/17282
總結
首先如果用 @ConditionalOnClass註解,強烈建議使用name屬性,而不要用value屬性。其次如果有提供元件給其他業務團隊使用,要特別關注版本問題,以及做好向下相容,不然指不定又掉坑了。