日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

摆脱困境:将属性值注入配置Bean

發布時間:2023/12/3 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 摆脱困境:将属性值注入配置Bean 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Spring Framework對將從屬性文件中找到的屬性值注入到bean或@Configuration類中提供了很好的支持。 但是,如果將單個屬性值注入這些類中,則會遇到一些問題。

這篇博客文章指出了這些問題,并描述了我們如何解決它們。

讓我們開始吧。

如果使用Spring Boot,則應使用其Typesafe配置屬性。 您可以從以下網頁中獲取有關此信息的更多信息:

  • Spring Boot參考手冊的23.7節Typesafe配置屬性
  • @EnableConfigurationProperties批注的Javadoc
  • @ConfigurationProperties批注的Javadoc
  • 在Spring Boot中使用@ConfigurationProperties

很簡單,但并非沒有問題

如果將單個屬性值注入到我們的bean類中,我們將面臨以下問題:

1.注入多個屬性值很麻煩

如果我們通過使用@Value批注注入單個屬性值,或者通過使用Environment對象來獲取屬性值,則注入多個屬性值會很麻煩。

假設我們必須向UrlBuilder對象注入一些屬性值。 該對象需要三個屬性值:

  • 服務器的主機( app.server.host )
  • 服務器監聽的端口( app.server.port )
  • 使用的協議( app.server.protocol )

當UrlBuilder對象構建用于訪問Web應用程序的不同功能的URL地址時,將使用這些屬性值。

如果我們通過使用構造函數注入和@Value注釋注入這些屬性值,則UrlBuilder類的源代碼如下所示:

import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component;@Component public class UrlBuilder {private final String host;private final String port;private final String protocol;@Autowiredpublic UrlBuilder(@Value("${app.server.protocol}") String protocol,@Value("${app.server.host}") String serverHost,@Value("${app.server.port}") int serverPort) {this.protocol = protocol.toLowercase();this.serverHost = serverHost;this.serverPort = serverPort;} }

補充閱讀:

  • @Value注釋的Javadoc

如果我們通過使用構造函數注入和Environment類注入這些屬性值,則UrlBuilder類的源代碼如下所示:

import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; import org.springframework.stereotype.Component;@Component public class UrlBuilder {private final String host;private final String port;private final String protocol;@Autowiredpublic UrlBuilder(Environment env) {this.protocol = env.getRequiredProperty("app.server.protocol").toLowercase();this.serverHost = env.getRequiredProperty("app.server.host");this.serverPort = env.getRequiredProperty("app.server.port", Integer.class);} }

補充閱讀:

  • 環境接口的Javadoc

我承認這看起來還不錯。 但是,當所需屬性值的數量增加和/或我們的類也具有其他依賴項時,將所有這些屬性都注入是很麻煩的。

2.我們必須指定多個屬性名稱(或記住使用常量)

如果我們將單個屬性值直接注入需要它們的Bean中,并且有多個Bean(A和B)需要相同的屬性值,那么我們想到的第一件事就是在兩個Bean類中指定屬性名稱:

import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component;@Component public class A {private final String protocol;@Autowiredpublic A(@Value("${app.server.protocol}") String protocol) {this.protocol = protocol.toLowercase();} }import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component;@Component public class B {private final String protocol;@Autowiredpublic B(@Value("${app.server.protocol}") String protocol) {this.protocol = protocol.toLowercase();} }

這是一個問題,因為

  • 因為我們是人類,所以我們會打錯字 。 這不是一個大問題,因為在啟動應用程序時我們會注意到它。 但是,它使我們放慢了速度。
  • 這使維護更加困難 。 如果更改屬性的名稱,則必須對使用該屬性的每個類進行此更改。
  • 我們可以通過將屬性名稱移到常量類來解決此問題。 如果這樣做,我們的源代碼如下所示:

    public final PropertyNames {private PropertyNames() {}public static final String PROTOCOL = "${app.server.protocol}"; }import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component;@Component public class A {private final String protocol;@Autowiredpublic A(@Value(PropertyNames.PROTOCOL) String protocol) {this.protocol = protocol.toLowercase();} }import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component;@Component public class B {private final String protocol;@Autowiredpublic B(@Value(PropertyNames.PROTOCOL) String protocol) {this.protocol = protocol.toLowercase();} }

    這可以解決維護問題,但前提是所有開發人員都記得要使用它。 我們當然可以通過使用代碼審閱來強制執行此操作,但這是審閱者必須記住要檢查的另一件事。

    3.添加驗證邏輯成為問題

    假設我們有兩個類( A和B ),它們需要app.server.protocol屬性的值。 如果我們將此屬性值直接注入到A和B Bean中,并且想要確保該屬性的值為'http'或'https',則必須

  • 將驗證邏輯添加到兩個bean類中。
  • 將驗證邏輯添加到實用程序類中,并在需要驗證是否給出了正確的協議時使用它。
  • 如果我們將驗證邏輯添加到兩個bean類,則這些類的源代碼如下所示:

    import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component;@Component public class A {private final String protocol;@Autowiredpublic A(@Value("${app.server.protocol}") String protocol) {checkThatProtocolIsValid(protocol);this.protocol = protocol.toLowercase();}private void checkThatProtocolIsValid(String protocol) {if (!protocol.equalsIgnoreCase("http") && !protocol.equalsIgnoreCase("https")) {throw new IllegalArgumentException(String.format("Protocol: %s is not allowed. Allowed protocols are: http and https.",protocol));}} }import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component;@Component public class B {private final String protocol;@Autowiredpublic B(@Value("${app.server.protocol}") String protocol) {checkThatProtocolIsValid(protocol);this.protocol = protocol.toLowercase();}private void checkThatProtocolIsValid(String protocol) {if (!protocol.equalsIgnoreCase("http") && !protocol.equalsIgnoreCase("https")) {throw new IllegalArgumentException(String.format("Protocol: %s is not allowed. Allowed protocols are: http and https.",protocol));}} }

    這是一個維護問題,因為A和B類包含復制粘貼代碼。 通過將驗證邏輯移至實用程序類并在創建新的A和B對象時使用它,可以稍微改善這種情況。

    完成此操作后,我們的源代碼如下所示:

    public final class ProtocolValidator {private ProtocolValidator() {}public static void checkThatProtocolIsValid(String protocol) {if (!protocol.equalsIgnoreCase("http") && !protocol.equalsIgnoreCase("https")) {throw new IllegalArgumentException(String.format("Protocol: %s is not allowed. Allowed protocols are: http and https.",protocol));}} }import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component;@Component public class A {private final String protocol;@Autowiredpublic A(@Value("${app.server.protocol}") String protocol) {ProtocolValidator.checkThatProtocolIsValid(protocol);this.protocol = protocol.toLowercase();} }import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component;@Component public class B {private final String protocol;@Autowiredpublic B(@Value("${app.server.protocol}") String protocol) {ProtocolValidator.checkThatProtocolIsValid(protocol);this.protocol = protocol.toLowercase();} }

    問題在于,我們仍然必須記住要調用此實用程序方法。 我們當然可以通過使用代碼審閱來強制執行此操作,但是再次重申,審閱者必須記住要檢查的另一件事。

    4.我們不能編寫好的文檔

    我們不能編寫描述應用程序配置的好的文檔,因為我們必須將此文檔添加到實際的屬性文件中,使用Wiki或編寫* gasp * Word文檔。

    這些選項中的每個選項都會引起問題,因為我們不能在編寫需要從屬性文件中找到屬性值的代碼的同時使用它們。 如果需要閱讀文檔,則必須打開“外部文檔”,這會導致上下文切換非常昂貴 。

    讓我們繼續前進,找出如何解決這些問題。

    將屬性值注入配置Bean

    通過將屬性值注入配置Bean中,我們可以解決前面提到的問題。 首先,為示例應用程序創建一個簡單的屬性文件。

    創建屬性文件

    我們要做的第一件事是創建一個屬性文件。 我們的示例應用程序的屬性文件稱為application.properties ,其外觀如下:

    app.name=Configuration Properties example app.production.mode.enabled=falseapp.server.port=8080 app.server.protocol=http app.server.host=localhost

    讓我們繼續并配置示例應用程序的應用程序上下文。

    配置應用程序上下文

    我們的示例應用程序的應用程序上下文配置類有兩個目標:

  • 啟用S??pring MVC并導入其默認配置。
  • 確保讀取了從application.properties文件中找到的屬性值,并且可以將其注入Spring Bean中。
  • 通過執行以下步驟,我們可以實現其第二個第二個目標:

  • 配置Spring容器以掃描所有包含bean類的軟件包。
  • 確保從application.properties文件中找到的屬性值已讀取并添加到Spring Environment中 。
  • 確保從@Value批注中找到的$ {...}占位符替換為從當前Spring Environment及其PropertySources中找到的屬性值。
  • WebAppContext類的源代碼如下所示:

    import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; import org.springframework.web.servlet.config.annotation.EnableWebMvc;@Configuration @ComponentScan({"net.petrikainulainen.spring.trenches.config","net.petrikainulainen.spring.trenches.web" }) @EnableWebMvc @PropertySource("classpath:application.properties") public class WebAppContext {/*** Ensures that placeholders are replaced with property values*/@BeanPropertySourcesPlaceholderConfigurer propertyPlaceHolderConfigurer() {return new PropertySourcesPlaceholderConfigurer();} }

    補充閱讀:

    • @ComponentScan批注的Javadoc
    • @PropertySource批注的Javadoc
    • Spring框架參考手冊的5.13.4 @PropertySource部分
    • PropertySourcesPlaceholderConfigurer類的Javadoc

    下一步是創建配置Bean類,并將從屬性文件中找到的屬性值注入到它們中。 讓我們找出如何做到這一點。

    創建配置Bean類

    讓我們創建以下描述的兩個配置bean類:

    • WebProperties類包含用于配置使用的協議,服務器的主機以及服務器偵聽的端口的屬性值。
    • ApplicationProperties類包含用于配置應用程序名稱并標識是否啟用生產模式的屬性值。 它還具有對WebProperties對象的引用。

    首先 ,我們必須創建WebProperties類。 我們可以按照以下步驟進行操作:

  • 創建WebProperties類,并使用@Component注釋對其進行注釋。
  • 將最終協議 , serverHost和serverPort字段添加到創建的類中。
  • 通過使用構造函數注入將屬性值注入到這些字段中,并確保協議字段的值必須為'http'或'https'(忽略大小寫)。
  • 添加用于獲取實際屬性值的吸氣劑。
  • WebProperties類的源代碼如下所示:

    import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component;@Component public final class WebProperties {private final String protocol;private final String serverHost;private final int serverPort;@Autowiredpublic WebProperties(@Value("${app.server.protocol}") String protocol,@Value("${app.server.host}") String serverHost,@Value("${app.server.port}") int serverPort) {checkThatProtocolIsValid(protocol);this.protocol = protocol.toLowercase();this.serverHost = serverHost;this.serverPort = serverPort;}private void checkThatProtocolIsValid(String protocol) {if (!protocol.equalsIgnoreCase("http") && !protocol.equalsIgnoreCase("https")) {throw new IllegalArgumentException(String.format("Protocol: %s is not allowed. Allowed protocols are: http and https.",protocol));}}public String getProtocol() {return protocol;}public String getServerHost() {return serverHost;}public int getServerPort() {return serverPort;} }

    其次 ,我們必須實現ApplicationProperties類。 我們可以按照以下步驟進行操作:

  • 創建ApplicationProperties類,并使用@Component注釋對其進行注釋。
  • 將最終名稱 , productionModeEnabled和webProperties字段添加到創建的類。
  • 通過使用構造函數注入,將屬性值和WebProperties bean注入ApplicationProperties bean。
  • 添加用于獲取字段值的吸氣劑。
  • ApplicationProperties類的源代碼如下所示:

    import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component;@Component public final class ApplicationProperties {private final String name;private final boolean productionModeEnabled;private final WebProperties webProperties;@Autowiredpublic ApplicationProperties(@Value("${app.name}") String name,@Value("${app.production.mode.enabled:false}") boolean productionModeEnabled,WebProperties webProperties) {this.name = name;this.productionModeEnabled = productionModeEnabled;this.webProperties = webProperties;}public String getName() {return name;}public boolean isProductionModeEnabled() {return productionModeEnabled;}public WebProperties getWebProperties() {return webProperties;} }

    讓我們繼續研究該解決方案的好處。

    這對我們有何幫助?

    現在,我們創建了包含從application.properties文件中找到的屬性值的Bean類。 該解決方案可能看起來像是過度設計,但與傳統的簡單方法相比,它具有以下優點:

    1.我們只能注入一個Bean而不是多個屬性值

    如果我們將屬性值注入到配置Bean中,然后通過使用構造函數注入將該配置Bean注入UrlBuilder類,則其源代碼如下所示:

    import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component;@Component public class UrlBuilder {private final WebProperties properties;@Autowiredpublic UrlBuilder(WebProperties properties) {this.properties = properties;} }

    如我們所見,這使我們的代碼更整潔( 尤其是在使用構造函數注入的情況下 )。

    2.我們只需指定一次屬性名稱

    如果將屬性值注入到配置Bean中,則只能在一個地方指定屬性名稱。 這意味著

    • 我們的代碼遵循關注點分離原則。 可從配置Bean中找到屬性名稱,而其他需要此信息的Bean不知道其來源。 他們只是使用它。
    • 我們的代碼遵循“ 不要重復自己”的原則。 因為屬性名稱僅在一個位置(在配置bean中)指定,所以我們的代碼更易于維護。

    另外,(IMO)我們的代碼看起來也更加簡潔:

    import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component;@Component public class A {private final String protocol;@Autowiredpublic A(WebProperties properties) {this.protocol = properties.getProtocol();} }import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component;@Component public class B {private final String protocol;@Autowiredpublic B(WebProperties properties) {this.protocol = properties.getProtocol();} }

    3.我們只需要編寫一次驗證邏輯

    如果將屬性值注入到配置Bean中,則可以將驗證邏輯添加到配置Bean中,而其他Bean不必知道它。 這種方法具有三個好處:

    • 我們的代碼遵循關注點分離原則,因為可以從配置bean(它所屬的地方)中找到驗證邏輯。 其他的豆子不必知道。
    • 我們的代碼遵循“不要重復自己”的原則,因為驗證邏輯是從一個地方找到的。
    • 創建新的Bean對象時,我們不必記住調用驗證邏輯,因為在創建配置Bean時我們可以強制執行驗證規則。

    另外,我們的源代碼看起來也更加干凈:

    import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component;@Component public class A {private final String protocol;@Autowiredpublic A(WebProperties properties) {this.protocol = properties.getProtocol();} }import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component;@Component public class B {private final String protocol;@Autowiredpublic B(WebProperties properties) {this.protocol = properties.getProtocol();} }

    4.我們可以從IDE中訪問文檔

    我們可以通過向配置bean中添加Javadoc注釋來記錄應用程序的配置。 完成此操作后,當我們編寫需要這些屬性值的代碼時,可以從IDE中訪問此文檔。 我們不需要打開其他文件或閱讀維基頁面。 我們可以簡單地繼續編寫代碼,避免上下文切換的開銷 。

    讓我們繼續并總結從這篇博客文章中學到的知識。

    摘要

    這篇博客文章告訴我們將屬性值注入配置Bean:

    • 幫助我們遵循關注點分離原則。 有關配置屬性和屬性值驗證的內容封裝在我們的配置bean中。 這意味著使用這些配置bean的bean不知道屬性值來自何處或如何驗證它們。
    • 幫助我們遵循“不要重復自己”的原則,因為1)我們只需指定一次屬性名稱,然后2)可以將驗證邏輯添加到配置bean。
    • 使我們的文檔更易于訪問。
    • 使我們的代碼更易于編寫,閱讀和維護。

    但是,這無助于我們弄清應用程序的運行時配置。 如果我們需要此信息,則必須讀取從服務器中找到的屬性文件。 這很麻煩。

    我們將在我的下一篇博客文章中解決此問題。

    • PS:您可以從Github獲得此博客文章的示例應用程序 。

    翻譯自: https://www.javacodegeeks.com/2015/04/spring-from-the-trenches-injecting-property-values-into-configuration-beans.html

    總結

    以上是生活随笔為你收集整理的摆脱困境:将属性值注入配置Bean的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。