您好,登錄后才能下訂單哦!
這篇文章主要介紹“Springboot1.x和2.x 通過@ConfigurationProperties對bean刷新自定義屬性的實現方法和用法區別”,在日常操作中,相信很多人在Springboot1.x和2.x 通過@ConfigurationProperties對bean刷新自定義屬性的實現方法和用法區別問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”Springboot1.x和2.x 通過@ConfigurationProperties對bean刷新自定義屬性的實現方法和用法區別”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
一點理解
對于springboot 1.x和2.x配置綁定部分源碼原理上的理解,簡單提一下。springboot自發布以來就提供@ConfigurationProperties注解操作配置類進行寬松綁定(Relaxed Binding),有趣的是兩個大版本中Relaxed Binding的具體實現是不一樣的,看過部分文檔后覺得springboot 2.0是想為使用者提供更嚴謹的API,所以重新設計了綁定發生的方式。2.0為我們添加了幾個新的抽象,并且開發了一個全新的綁定API,而部分舊包舊代碼不再使用。主要以下幾點
對于PropertySource你一定不陌生,結合接口Environment
,這個接口是一個PropertyResolver
,它可以讓你從一些底層的PropertySource
實現中解析屬性。Spring框架為常見的配置提供PropertySource
實現,例如系統屬性,命令行標志和屬性文件。 Spring Boot 會以對大多數應用程序有意義的方式自動配置這些實現(例如,加載application.properties)。
在Spring Boot 2.0不再直接使用現有的PropertySource
接口進行綁定,而是引入了一個新的ConfigurationPropertySource
接口。同時提供了一個合理的方式來實施放松綁定規則,這些規則以前是活頁夾的一部分。該接口的主要API非常簡單: ConfigurationProperty getConfigurationProperty(ConfigurationPropertyName name);還有一個IterableConfigurationPropertySource
變相的實現了Iterable
接口,以便可以發現源包含的所有名稱的配置。
通過使用以下代碼 Iterable<ConfigurationPropertySource> sources = ConfigurationPropertySources.get(environment); 可以獲取外部源數據;或者根據需要,還提供一個簡單的MapConfigurationPropertySource
實現,項目內重構源用到這種方式,很容易上手。
2、Relaxed Binding的具體實現
springboot 1.5和2.0中,屬性與配置值的綁定邏輯都始于ConfigurationPropertiesBindingPostProcessor類的postProcessBeforeInitialization函數。
其中1.5版本細看源碼發現,postProcessBeforeInitialization函數執行時,屬性值綁定的工作被委派給了PropertiesConfigurationFactory<T>類(這哥們是我們在2.0壓根找不到的貨,所以其之下細節不展開講了);
而2.0版本postProcessBeforeInitialization函數調用時,屬性值綁定的工作則被委派給了ConfigurationPropertiesBinder類,調用了bind函數,但ConfigurationPropertiesBinder類并不是一個public類,實際上它只相當于ConfigurationPropertiesBindingPostProcessor的一個內部靜態類,表面上負責處理@ConfigurationProperties注解的綁定任務。從源碼中可以看出,具體的工作委派給了另一個Binder類的對象。Binder類是SpringBoot 2.0版本后加入的類,它是負責處理對象與多個ConfigurationPropertySource之間的綁定的執行者,后面的代碼示例中我們會見到。
至此基本springboot 1.x和2.x版本在屬性配置綁定上的差異簡單說明了個七七八八,后面我們開始從使用上開始填坑:
場景:簽名請求,服務端需要解析header信息中的簽名字段的過程。此類字段的key一定是服務端事先定義好的,解析過程需要反復使用的。
簽名頭信息類:
@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; /** 時間戳:ms */ private String timestamp; /** 流水號/隨機串:至少16位,有效期內防重復提交 */ 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); } } }
坑點前面提到了,在輔助類中需要用到PropertiesConfigurationFactory來指定configurationPropertySource等設置、完成綁定動作等,而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); // 創建Binder綁定類 Binder binder = new Binder(sources); // 綁定屬性 SignatureHeaders signatureHeaders = binder.bind("openapi.validate", Bindable.of(SignatureHeaders.class)).get();
2.x的使用拋開了構建屬性配置工廠,我們自己通過MapConfigurationPropertySource實現了自定義屬性配置源,然后直接通過新加的綁定類Binder加載源信息,做識別后直接綁定到bean屬性,從代碼實現上看省去大量初始化代碼。
2.x加載外部屬性配置實現:
// 讀取自配置文件/配置中心 // environment可自動注入或上下文直接獲取 Iterable<ConfigurationPropertySource> sources = ConfigurationPropertySources.get(environment);// 設置Binder Binder binder = new Binder(sources); // 屬性綁定 SignatureHeaders signatureHeaders = binder.bind("openapi.validate", Bindable.of(SignatureHeaders.class)).get();
demo示例:將自定義Map的配置屬性數據加載到頭信息類中去
@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); } }
到此,關于“Springboot1.x和2.x 通過@ConfigurationProperties對bean刷新自定義屬性的實現方法和用法區別”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。