溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Springboot1.x和2.x 通過(guò)@ConfigurationProperties對(duì)bean刷新自定義屬性的實(shí)現(xiàn)方法和用法區(qū)別

發(fā)布時(shí)間:2021-09-07 09:20:46 來(lái)源:億速云 閱讀:162 作者:chen 欄目:大數(shù)據(jù)

這篇文章主要介紹“Springboot1.x和2.x 通過(guò)@ConfigurationProperties對(duì)bean刷新自定義屬性的實(shí)現(xiàn)方法和用法區(qū)別”,在日常操作中,相信很多人在Springboot1.x和2.x 通過(guò)@ConfigurationProperties對(duì)bean刷新自定義屬性的實(shí)現(xiàn)方法和用法區(qū)別問(wèn)題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”Springboot1.x和2.x 通過(guò)@ConfigurationProperties對(duì)bean刷新自定義屬性的實(shí)現(xiàn)方法和用法區(qū)別”的疑惑有所幫助!接下來(lái),請(qǐng)跟著小編一起來(lái)學(xué)習(xí)吧!

一點(diǎn)理解

  對(duì)于springboot 1.x和2.x配置綁定部分源碼原理上的理解,簡(jiǎn)單提一下。springboot自發(fā)布以來(lái)就提供@ConfigurationProperties注解操作配置類進(jìn)行寬松綁定(Relaxed Binding),有趣的是兩個(gè)大版本中Relaxed Binding的具體實(shí)現(xiàn)是不一樣的,看過(guò)部分文檔后覺(jué)得springboot 2.0是想為使用者提供更嚴(yán)謹(jǐn)?shù)腁PI,所以重新設(shè)計(jì)了綁定發(fā)生的方式。2.0為我們添加了幾個(gè)新的抽象,并且開發(fā)了一個(gè)全新的綁定API,而部分舊包舊代碼不再使用。主要以下幾點(diǎn)

1、PropertySources和ConfigurationPropertySources

  對(duì)于PropertySource你一定不陌生,結(jié)合接口Environment,這個(gè)接口是一個(gè)PropertyResolver,它可以讓你從一些底層的PropertySource實(shí)現(xiàn)中解析屬性。Spring框架為常見的配置提供PropertySource實(shí)現(xiàn),例如系統(tǒng)屬性,命令行標(biāo)志和屬性文件。 Spring Boot 會(huì)以對(duì)大多數(shù)應(yīng)用程序有意義的方式自動(dòng)配置這些實(shí)現(xiàn)(例如,加載application.properties)。

  在Spring Boot 2.0不再直接使用現(xiàn)有的PropertySource接口進(jìn)行綁定,而是引入了一個(gè)新的ConfigurationPropertySource接口。同時(shí)提供了一個(gè)合理的方式來(lái)實(shí)施放松綁定規(guī)則,這些規(guī)則以前是活頁(yè)夾的一部分。該接口的主要API非常簡(jiǎn)單: ConfigurationProperty getConfigurationProperty(ConfigurationPropertyName name);還有一個(gè)IterableConfigurationPropertySource變相的實(shí)現(xiàn)了Iterable接口,以便可以發(fā)現(xiàn)源包含的所有名稱的配置。

通過(guò)使用以下代碼 Iterable<ConfigurationPropertySource> sources = ConfigurationPropertySources.get(environment); 可以獲取外部源數(shù)據(jù);或者根據(jù)需要,還提供一個(gè)簡(jiǎn)單的MapConfigurationPropertySource實(shí)現(xiàn),項(xiàng)目?jī)?nèi)重構(gòu)源用到這種方式,很容易上手。

2、Relaxed Binding的具體實(shí)現(xiàn)

   springboot 1.5和2.0中,屬性與配置值的綁定邏輯都始于ConfigurationPropertiesBindingPostProcessor類的postProcessBeforeInitialization函數(shù)。

  其中1.5版本細(xì)看源碼發(fā)現(xiàn),postProcessBeforeInitialization函數(shù)執(zhí)行時(shí),屬性值綁定的工作被委派給了PropertiesConfigurationFactory<T>類(這哥們是我們?cè)?.0壓根找不到的貨,所以其之下細(xì)節(jié)不展開講了);

  而2.0版本postProcessBeforeInitialization函數(shù)調(diào)用時(shí),屬性值綁定的工作則被委派給了ConfigurationPropertiesBinder類,調(diào)用了bind函數(shù),但ConfigurationPropertiesBinder類并不是一個(gè)public類,實(shí)際上它只相當(dāng)于ConfigurationPropertiesBindingPostProcessor的一個(gè)內(nèi)部靜態(tài)類,表面上負(fù)責(zé)處理@ConfigurationProperties注解的綁定任務(wù)。從源碼中可以看出,具體的工作委派給了另一個(gè)Binder類的對(duì)象。Binder類是SpringBoot 2.0版本后加入的類,它是負(fù)責(zé)處理對(duì)象與多個(gè)ConfigurationPropertySource之間的綁定的執(zhí)行者,后面的代碼示例中我們會(huì)見到。

至此基本springboot 1.x和2.x版本在屬性配置綁定上的差異簡(jiǎn)單說(shuō)明了個(gè)七七八八,后面我們開始從使用上開始填坑:

場(chǎng)景:簽名請(qǐng)求,服務(wù)端需要解析header信息中的簽名字段的過(guò)程。此類字段的key一定是服務(wù)端事先定義好的,解析過(guò)程需要反復(fù)使用的。

簽名頭信息類:

@Data
@ToString
@ConfigurationProperties(prefix="openapi.validate")
public class SignatureHeaders {
    private static final String SIGNATURE_HEADERS_PREFIX = "openapi-validate-";
    
    public static final Set<String> SIGNATURE_PARAMETER_SET = new HashSet<String>();
    private static String HEADER_APPID = SIGNATURE_HEADERS_PREFIX + "appid";
    private static String HEADER_TIMESTAMP = SIGNATURE_HEADERS_PREFIX + "timestamp";
    private static String HEADER_NONCE = SIGNATURE_HEADERS_PREFIX + "nonce";
    private static String HEADER_SIGNATURE = SIGNATURE_HEADERS_PREFIX + "signature";
    
    
    static {
        SIGNATURE_PARAMETER_SET.add(HEADER_APPID);
        SIGNATURE_PARAMETER_SET.add(HEADER_TIMESTAMP);
        SIGNATURE_PARAMETER_SET.add(HEADER_NONCE);
        SIGNATURE_PARAMETER_SET.add(HEADER_SIGNATURE);
    }
    
    /** 分配appid */
    private String appid;
    /** 分配appsecret */
    private String appsecret;
    /** 時(shí)間戳:ms */
    private String timestamp;
    /** 流水號(hào)/隨機(jī)串:至少16位,有效期內(nèi)防重復(fù)提交 */
    private String nonce;
    /** 簽名 */
    private String signature;
    
    
}

一、1.x的使用

解析頭信息

// 篩選頭信息
Map<String, Object> headerMap = Collections.list(request.getHeaderNames())
                .stream()
                .filter(headerName -> SignatureHeaders.HEADER_NAME_SET.contains(headerName))
                .collect(Collectors.toMap(headerName -> headerName.replaceAll("-", "."), headerName -> request.getHeader(headerName)));
PropertySource propertySource = new MapPropertySource("signatureHeaders", headerMap);
SignatureHeaders signatureHeaders = RelaxedConfigurationBinder.with(SignatureHeaders.class).setPropertySources(propertySource).doBind();

綁定輔助類

public class RelaxedConfigurationBinder<T> {
    private final PropertiesConfigurationFactory<T> factory;

    public RelaxedConfigurationBinder(T object) {
        this(new PropertiesConfigurationFactory<>(object));
    }

    public RelaxedConfigurationBinder(Class<T> type) {
        this(new PropertiesConfigurationFactory<>(type));
    }

    public static <T> RelaxedConfigurationBinder<T> with(T object) {
        return new RelaxedConfigurationBinder<>(object);
    }

    public static <T> RelaxedConfigurationBinder<T> with(Class<T> type) {
        return new RelaxedConfigurationBinder<>(type);
    }

    public RelaxedConfigurationBinder(PropertiesConfigurationFactory<T> factory) {
        this.factory = factory;
        ConfigurationProperties properties = getMergedAnnotation(factory.getObjectType(), ConfigurationProperties.class);
        javax.validation.Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
        factory.setValidator(new SpringValidatorAdapter(validator));
        factory.setConversionService(new DefaultConversionService());
        if (null != properties) {
            factory.setIgnoreNestedProperties(properties.ignoreNestedProperties());
            factory.setIgnoreInvalidFields(properties.ignoreInvalidFields());
            factory.setIgnoreUnknownFields(properties.ignoreUnknownFields());
            factory.setTargetName(properties.prefix());
            factory.setExceptionIfInvalid(properties.exceptionIfInvalid());
        }
    }

    public RelaxedConfigurationBinder<T> setTargetName(String targetName) {
        factory.setTargetName(targetName);
        return this;
    }

    public RelaxedConfigurationBinder<T> setPropertySources(PropertySource<?>... propertySources) {
        MutablePropertySources sources = new MutablePropertySources();
        for (PropertySource<?> propertySource : propertySources) {
            sources.addLast(propertySource);
        }
        factory.setPropertySources(sources);
        return this;
    }

    public RelaxedConfigurationBinder<T> setPropertySources(Environment environment) {
        factory.setPropertySources(((ConfigurableEnvironment) environment).getPropertySources());
        return this;
    }

    public RelaxedConfigurationBinder<T> setPropertySources(PropertySources propertySources) {
        factory.setPropertySources(propertySources);
        return this;
    }

    public RelaxedConfigurationBinder<T> setConversionService(ConversionService conversionService) {
        factory.setConversionService(conversionService);
        return this;
    }

    public RelaxedConfigurationBinder<T> setValidator(Validator validator) {
        factory.setValidator(validator);
        return this;
    }

    public RelaxedConfigurationBinder<T> setResolvePlaceholders(boolean resolvePlaceholders) {
        factory.setResolvePlaceholders(resolvePlaceholders);
        return this;
    }

    public T doBind() throws GeneralException {
        try {
            return factory.getObject();
        } catch (Exception ex) {
            throw new GeneralException("配置綁定失??!", ex);
        }
    }
}

坑點(diǎn)前面提到了,在輔助類中需要用到PropertiesConfigurationFactory來(lái)指定configurationPropertySource等設(shè)置、完成綁定動(dòng)作等,而PropertiesConfigurationFactory在2.x中是不存在的。

二、2.x的使用

解析頭信息

// 篩選頭信息
  Map<String, Object> headerMap = Collections.list(request.getHeaderNames())
                .stream()
                .filter(headerName -> SignatureHeaders.SIGNATURE_PARAMETER_SET.contains(headerName))
                .collect(Collectors.toMap(headerName -> headerName.replaceAll("-", "."), headerName -> request.getHeader(headerName)));
  // 自定義ConfigurationProperty源信息
  ConfigurationPropertySource sources = new MapConfigurationPropertySource(headerMap);
  // 創(chuàng)建Binder綁定類
  Binder binder = new Binder(sources);
  // 綁定屬性
  SignatureHeaders signatureHeaders = binder.bind("openapi.validate", Bindable.of(SignatureHeaders.class)).get();

2.x的使用拋開了構(gòu)建屬性配置工廠,我們自己通過(guò)MapConfigurationPropertySource實(shí)現(xiàn)了自定義屬性配置源,然后直接通過(guò)新加的綁定類Binder加載源信息,做識(shí)別后直接綁定到bean屬性,從代碼實(shí)現(xiàn)上看省去大量初始化代碼。

2.x加載外部屬性配置實(shí)現(xiàn):

// 讀取自配置文件/配置中心 // environment可自動(dòng)注入或上下文直接獲取
Iterable<ConfigurationPropertySource> sources = ConfigurationPropertySources.get(environment);// 設(shè)置Binder
Binder binder = new Binder(sources);
// 屬性綁定
SignatureHeaders signatureHeaders = binder.bind("openapi.validate", Bindable.of(SignatureHeaders.class)).get();

demo示例:將自定義Map的配置屬性數(shù)據(jù)加載到頭信息類中去

@RunWith(SpringRunner.class)
@SpringBootTest(classes = SignatureApp.class)
@Slf4j
public class ConfigurationPropertyTest {
    
    @Test
    public void testConfigurationPropertySources() {
        Map<String, Object> dataMap = new HashMap<String, Object>();
        dataMap.put("openapi.validate.appid", "123456789");
        dataMap.put("openapi.validate.timestamp", "1565062140111");
        dataMap.put("openapi.validate.nonce", "20190805180100102030");
        dataMap.put("openapi.validate.signature", "vDMbihw6uaxlhoBCBJAY9xnejJXNCAA0QCc+I5X9EYYwAdccjNSB4L4mPZXymbH+fwm3ulkuY7UBNZclV1OBoELCSUMn7VRLAVqBS4bKrTA=");
        
        ConfigurationPropertySource sources = new MapConfigurationPropertySource(dataMap);
        Binder binder = new Binder(sources);
        SignatureHeaders signatureHeaders = binder.bind("openapi.validate", Bindable.of(SignatureHeaders.class)).get(); 
        
        log.info("###Parse Result: {}", signatureHeaders);
    }
}

 

到此,關(guān)于“Springboot1.x和2.x 通過(guò)@ConfigurationProperties對(duì)bean刷新自定義屬性的實(shí)現(xiàn)方法和用法區(qū)別”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注億速云網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)?lái)更多實(shí)用的文章!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI