1. 程式人生 > 其它 >注入屬性檔案的值

注入屬性檔案的值

按照以往的方式,我們總是直接把具體的字面量值填入程式碼進行字面量值的注入。如下所示:

 1 @Component
 2 public class Music {
 3     private String musicName = null;
 4     private Date publishTime = null;
 5 
 6     @Value("Dream")
 7     public void setMusicName(String musicName) {
 8         this.musicName = musicName;
 9     }
10 
11     @Value("2022/12/22")
12 public void setPublishTime(Date publishTime) { 13 this.publishTime = publishTime; 14 } 15 16 // 省略getter方法 17 }

可以看到Music定義了三個屬性。其值直接填在程式碼裡,由@Value註解注入。直覺告訴我們,這種硬編碼的注入方式並不友好。假如Bean的屬性值由於某種原因需做變動,我們還得修改程式碼。非常麻煩,非常不好維護!要是能從某個屬性檔案裡讀取屬性值之後作為字面量值進行注入,該有多好!這樣,我們無需修改程式碼,只需修改屬性檔案就能達到修改Bean的屬性值的目的。非常靈活,非常簡單,非常方便,非常易於修改,非常易於維護!幸運的是,Spring確實提供了這樣的支援。而這支援,得從Environment介面談起。

Environment是Spring定義的一個介面,代表著當前正在執行的應用程式的環境,主要提供兩種功能:一種是設定Profile;一種是能讓開發人員比較方便地訪問屬性檔案。至於何為Profile,我們將在其它章節另行介紹。現在重點關注的是與屬性檔案訪問相關的內容。

具體而言,Spring實現了兩種Environment介面:一種是StandardEnvironment,用於與Web無關的應用程式;一種是StandardServletEnvironment,用於與Web相關的應用程式。建立Spring容器時,Spring容器將會建立Environment,而Environment則會載入屬性檔案。因此,我們需要提供配置資訊告訴Environment屬性檔案在哪。而這,可以通過Spring提供的@PropertySource註解指定。如下所示:

1 @Configuration
2 @ComponentScan("com.dream.controller")
3 @PropertySource("classpath:com/dream/app.properties")
4 public class ServletConfig {
5 }

我們指定的屬性檔案是classpath:com/dream/app.properties,位於com.dream包裡。屬性檔案的內容如下:

music.name=Dream
music.publishtime=2022/12/22

於是,Environment能從@PropertySource註解指定的位置載入屬性檔案。之後,我們需把Environment注入我們的Bean裡,以使我們的Bean能用Environment獲取屬性檔案的屬性值,按照屬性名的匹配情況把相應的屬性值注入我們的Bean裡,如下所示:

 1 @Component
 2 public class Music {
 3     private Environment environment = null;
 4     private String musicName = null;
 5     private Date publishTime = null;
 6 
 7     @Autowired
 8     public Music(Environment environment) {
 9         this.environment = environment;
10         this.musicName = environment.getProperty("music.name");
11         this.publishTime = environment.getProperty("music.publishtime", Date.class);
12     }
13 
14     // 省略getter方法
15 }

通過@Autowired註解,我們告訴Spring容器把Environment注入Music的建構函式裡。建構函式拿到Environment之後,以屬性檔案的屬性名作為引數呼叫getProperty()方法獲取屬性值,把屬性值賦給Bean的屬性,完成字面量值的注入。getProperty()方法具有四種過載,簽名如下:
1.String getProperty(String key)
2.String getProperty(String key, String defaultValue)
3.T getProperty(String key, Class<T> type)
4.T getProperty(String key, Class<T> type, T defaultValue)

大家一看便知怎麼呼叫,無需詳敘。重點在於,我們還可通過往@Value註解裡填入屬性佔位符的方式注入屬性檔案的值。而這,需要我們建立PropertySourcesPlaceholderConfigurer型別的Bean,讓它幫助我們針對這種方式進行處理。如下所示:

 1 @Configuration
 2 @ComponentScan("com.dream.controller")
 3 public class ServletConfig {
 4     @Bean
 5     public PropertySourcesPlaceholderConfigurer placeholderConfigurer(
 6             ResourceLoader resourceLoader) {
 7         var resourceUrl = "classpath:com/dream/app.properties";
 8         var resource = resourceLoader.getResource(resourceUrl);
 9         var configurer = new PropertySourcesPlaceholderConfigurer();
10         configurer.setLocations(resource);
11         return configurer;
12     }
13 }

可以看到先前新增的@PropertySource註解已被刪除,卻定義了一個具有ResourceLoader型別的引數的方法,用於建立PropertySourcesPlaceholderConfigurer型別的Bean。建立過程如下:
1.呼叫resourceLoader的getResource()方法獲取屬性檔案這種資源。
2.建立PropertySourcesPlaceholderConfigurer型別的例項。
3.呼叫setLocations()方法,把屬性檔案這種資源交給PropertySourcesPlaceholderConfigurer載入。

注意:setLocations()方法的引數是Resource...型別的,可以指定多個代表屬性檔案的資源。

於是,Spring容器載入配置檔案之後,PropertySourcesPlaceholderConfigurer型別的Bean就存在Spring容器裡,幫助我們處理屬性佔位符。因此,我們還得修改Music如下:

 1 @Component
 2 public class Music {
 3     private String musicName = null;
 4     private Date publishTime = null;
 5 
 6     @Value("${music.name}")
 7     public void setMusicName(String musicName) {
 8         this.musicName = musicName;
 9     }
10 
11     @Value("${music.publishtime}")
12     public void setPublishTime(Date publishTime) {
13         this.publishTime = publishTime;
14     }
15 
16     // 省略getter方法
17 }

可以看到@Value註解注入的是屬性佔位符。屬性佔位符是這樣構成的:${屬性檔案裡的屬性名},也就是用${}把屬性檔案裡的屬性名括起來。這樣,實現了BeanFactoryPostProcessor介面的PropertySourcesPlaceholderConfigurer就能在Spring容器建立Bean之前做些事情:
1. 把由setLocations()方法指定的屬性檔案載入到Environment裡。
2. 找出Bean的定義裡的屬性佔位符,與Environment裡的屬性名進行匹配,並把匹配成功的屬性佔位符改成屬性名對應的屬性值。

於是,那些含有屬性佔位符的Bean的定義都被修改了,由屬性佔位符改成屬性檔案的值。之後,Spring容器根據修改之後的Bean的定義建立和裝配Bean,注入的值自然是屬性檔案的值。

另外,如果配置檔案是XML的話,可以這樣配置:

1 <bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
2     <property name="locations" value="classpath:com/dream/app.properties"/>
3 </bean>
4 <bean class="com.dream.controller.Music">
5     <property name="musicName" value="${music.name}" />
6     <property name="publishTime" value="${music.publishtime}" />
7 </bean>

為了方便配置,Spring還專門提供了<context:property-placeholder>元素,用於建立PropertySourcesPlaceholderConfigurer型別的Bean。因此,配置資訊也能改成這樣:

1 <context:property-placeholder locations ="classpath:com/dream/app.properties"/>
2 
3 <bean class="com.dream.controller.Music">
4     <property name="musicName" value="${music.name}" />
5     <property name="publishTime" value="${music.publishtime}" />
6 </bean>

還有,Environment除了能夠載入屬性檔案,也能按照優先順序從上到下載入這些引數:
1.Servlet初始化引數
2.Servlet上下文初始化引數
3.JNDI環境變數
4.JVM系統屬性(也就是JVM的命令列引數)
5.作業系統環境變數

這意味著我們也能通過屬性佔位符注入這些引數的值。大家可以試試,這裡不作過多介紹。

於是,關於怎樣通過屬性佔位符注入屬性檔案的值,我們已經理清楚了。下章該談談Spring表示式語言,看看能用強大的Spring表示式語言注入怎樣的字面量值。歡迎大家繼續閱讀,謝謝大家!

返回目錄下載程式碼