聊聊springboot自動裝配出現的TypeNotPresentExceptionProxy異常排查

發表於2023-09-21

前言

正文開始前,我們做個小測試,假設我們封裝了一個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屬性。其次如果有提供元件給其他業務團隊使用,要特別關注版本問題,以及做好向下相容,不然指不定又掉坑了。

相關文章