电竞比分网-中国电竞赛事及体育赛事平台

分享

Spring Boot 2.0 的配置綁定類Bindable居然如此強大

 小世界的野孩子 2021-05-24

1. 前言

在開發(fā)Spring Boot應(yīng)用時會用到根據(jù)條件來向Spring IoC容器注入Bean。比如配置文件存在了某個配置屬性才注入Bean

根據(jù)配置屬性來動態(tài)注入Bean

圖中紅色的部分是說,只有ali.pay.v1.app-id存在于Spring的環(huán)境配置中時這個@Configuration標(biāo)記的類才能注入Spring IoC。

這里面的@ConditionalOnProperty就是條件注解系列的一種。它還有很多種來滿足各種場景的條件注解:

條件注解家族

其實數(shù)量遠不止截圖中這幾個,在Spring 家族的其它框架中也有實現(xiàn)。

這里扯得有點遠了,今天不是來講這些條件控制注解的用法的,只是我發(fā)現(xiàn)了一個使用條件注解@ConditionalOnProperty無法解決的問題。

條件注入?yún)⒖纪冢?a target="_blank">Spring Boot 2 實戰(zhàn):使用 @Condition 注解來根據(jù)條件注入 Bean

2. 配置文件存在Map結(jié)構(gòu)的場景

下面是一段配置文件:

app:
 v1:
  foo:
    name: 
    description: 碼農(nóng)小胖哥
  bar:
    name: ooxx.cn
    description: xxxxxx

對應(yīng)配置類:

@Data
@ConfigurationProperties("app")
public class AppProperties {
    /**
     *  
     */
    private Map<String, V1> v1 = new HashMap<>();

    /**
     *  
     *
     * @author 
     * @since 1.0.0.RELEASE
     */
    @Data
    public static class V1 {
        /**
         * name
         */
        private String name;
        /**
         * description
         */
        private String description;

    }
}

特殊之處來了,yml配置里的 foo、bar其實是作為Map中的key來標(biāo)識V1的,和其它配置參數(shù)不同,這個key用戶可以隨意定義一個String來標(biāo)識,可能是foo,可能是bar,完全根據(jù)開發(fā)者的喜好進行主觀定義。這個時候你想根據(jù)app.v1.*.name(暫時用通配符*)來進行@ConditionalOnProperty判斷是行不通的,因為你不確定*的值,該怎么辦呢?

3. 解決方案

這里我花了一天的時間去摸索,最開始我認為Spring提供通配符(app.v1.*.name)甚至是SpringEL表達式可以拿到,但是搞了半天無功而返。

突然我想到之前看Spring Security OAuth2源碼中有類似的邏輯。用過Spring Security OAuth2相關(guān)的都知道Spring Security OAuth2也要求用戶自定義一個key來標(biāo)識自己的OAuth2客戶端。比如我用Gitee的:

spring:
  security:
    oauth2:
      client:
        registration:
          gitee:
            client-id: xxxxxx
            client-secret: xxxxx

這里的key就是gitee,當(dāng)然這根據(jù)開發(fā)者心情決定,甚至你用zhangshan作為key都可以。

Spring Security OAuth2 提供了相關(guān)的條件注入思路,下面是其條件注入判斷的核心類:

public class ClientsConfiguredCondition extends SpringBootCondition {

   private static final Bindable<Map<String, OAuth2ClientProperties.Registration>> STRING_REGISTRATION_MAP = Bindable
         .mapOf(String.class, OAuth2ClientProperties.Registration.class);

   @Override
   public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
      ConditionMessage.Builder message = ConditionMessage.forCondition("OAuth2 Clients Configured Condition");
      Map<String, OAuth2ClientProperties.Registration> registrations = getRegistrations(context.getEnvironment());
      if (!registrations.isEmpty()) {
         return ConditionOutcome.match(message.foundExactly("registered clients " + registrations.values().stream()               .map(OAuth2ClientProperties.Registration::getClientId).collect(Collectors.joining(", "))));
      }
      return ConditionOutcome.noMatch(message.notAvailable("registered clients"));
   }

   private Map<String, OAuth2ClientProperties.Registration> getRegistrations(Environment environment) {
      return Binder.get(environment).bind("spring.security.oauth2.client.registration", STRING_REGISTRATION_MAP)
            .orElse(Collections.emptyMap());
   }

}

顯然OAuth2ClientProperties的結(jié)構(gòu)和我們要驗證的AppProperties結(jié)構(gòu)是一樣的。所以上面的邏輯是可以抄過來的,它可以將環(huán)境配置中的帶有不確定key的配置綁定到我們的配置類AppProperties中。核心的綁定邏輯是這一段:

Binder.get(environment).bind("spring.security.oauth2.client.registration", STRING_REGISTRATION_MAP)

首先通過Bindable來聲明一個可綁定的數(shù)據(jù)結(jié)構(gòu),這里調(diào)用了mapOf方法聲明了一個Map的數(shù)據(jù)綁定結(jié)構(gòu)。然后通過綁定的具體操作對象Binder從配置環(huán)境接口Environment中提取了spring.security.oauth2.client.registration開頭的配置屬性并注入到Map中去。既然我們能夠獲取到了Map,根據(jù)什么策略判斷就完全掌握在我們手中了。

Bindable為Spring Boot 2.0提供的數(shù)據(jù)綁定新特性,有興趣可從spring.io獲取更多信息。

接下來不用我說了吧,照葫蘆畫瓢還有誰不會呢?配合@Conditional注解就能實現(xiàn)根據(jù)app.v1下參數(shù)的實際情況來動態(tài)的進行Bean注入。

4. 總結(jié)

今天利用Spring Boot 2.0的數(shù)據(jù)綁定特性解決了一個實際需求,花了不少時間。當(dāng)我們解決問題陷入困境時,首先要去想想有沒有類似場景以及對應(yīng)的解決方案。這同樣說明平時的積累很重要,很多粉絲的問題其實公眾號都有講過,所以處處留心解釋學(xué)問。多多留意:碼農(nóng)小胖哥,共同學(xué)習(xí),共同進步。

關(guān)注公眾號:Felordcn 獲取更多資訊

個人博客:https://

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多