- 導讀
- 概述
- PropertyPlaceholderConfigurer屬性檔案
- 執行個體
- 使用PropertyPlaceholderConfigurer屬性檔案
- PropertyPlacerholderConfigurer的其他屬性
- locations
- fileEncoding
- order
- placeholderPrefix
- placeholderSuffix
- PropertyPlacerholderConfigurer的其他屬性
- 使用contextproperty-placehoder引用屬性檔案
- 基于注解及基于JAVA類的配置中引用屬性
- 執行個體
- 注意事項
導讀
Spring-使用外部屬性檔案01
Spring-使用加密的屬性檔案02
Spring-屬性檔案自身的引用03
概述
在進行資料源或者郵件伺服器等資源配置時,使用者可以直接在Spring配置檔案中配置使用者名、密碼、連接配接資訊等,但是有一種更好的方法是将這些配置資訊獨立到一個外部屬性檔案中,并在Spring配置檔案中通過形如{user}、{password}的占位符引用屬性檔案中的屬性項。
通過這種方式配置擁有兩個明顯的好處
- 減少維護的工作量
- 部署更加簡單
Spring提供了一個PropertyPlaceholderConfigurer,它能夠使Bean在配置時引用外部屬性檔案。
PropertyPlaceholderConfigurer實作了BeanFactoryPostProcessorBean接口,因而也是一個Bean工廠後處理器.
PropertyPlaceholderConfigurer屬性檔案
代碼已托管到Github—> https://github.com/yangshangwei/SpringMaster
執行個體
maven項目
一種不太好的寫法
id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
<property name="url" value="jdbc:oracle:thin:@172.25.246.11:1521:xgj"/>
<property name="username" value="cctb"/>
<property name="password" value="xgj2017"/>
複制
從上面的配置檔案中,我們可以看到驅動器類名、JDBC的URL以及資料庫的使用者名和密碼都寫在了XML中。部署的時候,如果需要改動資料庫的配置資訊,需要先找到這個xml,然後修改,不是特别友善。
根據實際應用的最佳實踐,我們可以将這些資訊抽取到一個配置檔案中,取名為 jdbc.priperties (随意取名)
我們先看下jdbc.priperties的配置檔案
jdbc.properties
jdbc.driverClassName=oracle.jdbc.driver.OracleDriver
jdbc.url=jdbc:oracle:thin:@172.25.246.11:1521:xgj
jdbc.username=cctb
jdbc.password=xgj2017
複制
屬性檔案可以定義多個屬性,每個屬性都有一個屬性名和屬性值組成,二者用“=”隔開。
使用PropertyPlaceholderConfigurer屬性檔案
下面通過PropertyPlaceholderConfigurer引入jdbc.properties屬性檔案,調整資料源Bean的配置,為了測試,我們引入了JdbcTemplate
beans.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base- package="com.xgj.ioc.propertyplacehoder"/>
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
p:location="classpath:spring/jdbc.properties"
p:fileEncoding="utf-8"/>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close"
p:driverClassName="${jdbc.driverClassName}"
p:url="${jdbc.url}"
p:username="${jdbc.username}"
p:password="${jdbc.password}"/>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"
p:dataSource-ref="dataSource" />
beans>
複制
通過PropertyPlaceholderConfigurer的location屬性引入屬性檔案,這樣在Bean定義的時候就可以引用屬性檔案中的屬性了。
然後通過${jdbc.driverClassName}等占位符來引用jdbc.properties中的屬性,這樣部署人員僅需要關注jdbc.properties這個配置檔案即可,無需關心Spring的配置檔案。
測試:
我們資料庫中temp_user表有一條記錄,我們來模拟登入查詢
PropertyPlaceHoderTest.java
package com.xgj.ioc.propertyplacehoder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
@Component
public class PropertyPlaceHoderTest {
private final static String MATCH_COUNT_SQL = " SELECT count(*) FROM temp_user "
+ " WHERE user_name =? and password=? ";
private JdbcTemplate jdbcTemplate;
@Autowired
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
/**
*
*
* @Title: getMatchCount
*
* @Description: 根據使用者名和密碼判斷使用者是否存在
*
* @param username
* @param password
*
* @return: int
*/
public int getMatchCount(String username, String password) {
return jdbcTemplate.queryForObject(MATCH_COUNT_SQL, new Object[] {
username, password }, Integer.class);
}
/**
*
*
* @Title: main
*
* @Description: 測試
*
* @param args
*
* @return: void
*/
public static void main(String[] args) {
// 加載Spring配置檔案
ApplicationContext ctx = new ClassPathXmlApplicationContext(
"classpath:com/xgj/ioc/propertyplacehoder/beans.xml");
// 擷取通過注解标注的Bean
PropertyPlaceHoderTest propertyPlaceHoderTest = ctx.getBean(
"propertyPlaceHoderTest", PropertyPlaceHoderTest.class);
// 調用方法
int count = propertyPlaceHoderTest.getMatchCount("xgj", "123456");
System.out.println("比對的使用者數量:" + count);
}
}
複制
運作結果:
PropertyPlacerholderConfigurer的其他屬性
locations
如果隻有一個屬性檔案,則直接使用location屬性指定即可,如果有多個屬性檔案,則可以通過locations屬性進行設定,可以像配置list一樣配置locations屬性。
list的配置參考 Spring-注入參數詳解-[集合類型屬性]
fileEncoding
屬性檔案的編碼格式,Spring預設使用作業系統預設編碼讀取屬性檔案,如果屬性檔案使用了特殊編碼,則需要通過該屬性顯式指定。
order
如果配置配置檔案中定義了多個PropertyPlacehoderConfigurer,則通過該屬性指定優先順序。
placeholderPrefix
上面的案例,我們使用{jdbc.driverClassName}引用屬性檔案中的屬性項, 其中, { 為預設的占位符字首,可修改
placeholderSuffix
占位符字尾,預設為
}
使用context:property-placehoder引用屬性檔案
可以使用context命名空間定義屬性檔案,相比傳統的PropertyPlaceholderConfigurer配置,這種方式更優雅
<context:property-placeholder location="classpath:spring/jdbc.properties" />
複制
但是有個缺點: 如果想自定義一些額外的進階功能,比如屬性加密、使用資料庫表儲存配置資訊等,則必須擴充PropertyPlaceholderConfigurer的類并使用Bean的配置方式。
基于注解及基于JAVA類的配置中引用屬性
在基于XML的配置檔案中,通過${propName}的形式引用屬性值,類似的,基于注解的Bean可以通過@Value注解為Bean的成員變量或者方法入參自動注入容器已有的屬性。同樣的基于JAVA類注解@Configuration的類的引用屬性的方式和基于注解配置的引用方式是完全一樣的,不再贅述。
執行個體
package com.xgj.ioc.propertyplacehoder.annotation;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class MyDataSource {
private String driveClassName;
private String url;
private String userName;
private String password;
/**
*
*
* @Title: setDriveClassName
*
* @Description: 注入jdbc.driverClassName的值 (也可以直接在屬性上注入)
*
* @param driveClassName
*
* @return: void
*/
@Value("${jdbc.driverClassName}")
public void setDriveClassName(String driveClassName) {
this.driveClassName = driveClassName;
}
@Value("${jdbc.url}")
public void setUrl(String url) {
this.url = url;
}
@Value("${jdbc.username}")
public void setUserName(String userName) {
this.userName = userName;
}
@Value("${jdbc.password}")
public void setPassword(String password) {
this.password = password;
}
/**
*
*
* @Title: getDriveClassName
*
* @Description: 擷取driveClassName
*
* @return
*
* @return: String
*/
public String getDriveClassName() {
System.out.println("getDriveClassName:" + driveClassName);
return driveClassName;
}
public String getUrl() {
System.out.println("getUrl:" + url);
return url;
}
public String getUserName() {
System.out.println("getUserName:" + userName);
return userName;
}
public String getPassword() {
System.out.println("getPassword:" + password);
return password;
}
}
複制
測試類
package com.xgj.ioc.propertyplacehoder.annotation;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AnnotationTest {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext(
"classpath:com/xgj/ioc/propertyplacehoder/beans.xml");
MyDataSource myDataSource = ctx.getBean("myDataSource",
MyDataSource.class);
myDataSource.getDriveClassName();
myDataSource.getUrl();
myDataSource.getUserName();
myDataSource.getPassword();
}
}
複制
運作結果
注意事項
使用的過程中,一定要確定所引用的屬性值在屬性檔案中存在且數值比對,否則會造成Bean建立錯誤。
比如我們修改一下注入的屬性@Value(“${jdbc.driverClassName1}”)
Error creating bean with name ‘myDataSource’: Injection of autowired dependencies failed; …..
2017-08-06 18:23:17,692 INFO [main] (AbstractApplicationContext.java:583) - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@7fae1081: startup date [Sun Aug 06 18:23:17 BOT 2017]; root of context hierarchy
2017-08-06 18:23:17,767 INFO [main] (XmlBeanDefinitionReader.java:317) - Loading XML bean definitions from class path resource [com/xgj/ioc/propertyplacehoder/beans.xml]
2017-08-06 18:23:18,398 WARN [main] (AbstractApplicationContext.java:551) - Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'myDataSource': Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'jdbc.driverClassName1' in value "${jdbc.driverClassName1}"
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'myDataSource': Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'jdbc.driverClassName1' in value "${jdbc.driverClassName1}"
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:372)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1264)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:553)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:761)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:867)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:543)
at org.springframework.context.support.ClassPathXmlApplicationContext.(ClassPathXmlApplicationContext.java:139)
at org.springframework.context.support.ClassPathXmlApplicationContext.(ClassPathXmlApplicationContext.java:83)
at com.xgj.ioc.propertyplacehoder.annotation.AnnotationTest.main(AnnotationTest.java:9)
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'jdbc.driverClassName1' in value "${jdbc.driverClassName1}"
at org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:174)
at org.springframework.util.PropertyPlaceholderHelper.replacePlaceholders(PropertyPlaceholderHelper.java:126)
at org.springframework.core.env.AbstractPropertyResolver.doResolvePlaceholders(AbstractPropertyResolver.java:236)
at org.springframework.core.env.AbstractPropertyResolver.resolveRequiredPlaceholders(AbstractPropertyResolver.java:210)
at org.springframework.context.support.PropertySourcesPlaceholderConfigurer$2.resolveStringValue(PropertySourcesPlaceholderConfigurer.java:172)
at org.springframework.beans.factory.support.AbstractBeanFactory.resolveEmbeddedValue(AbstractBeanFactory.java:831)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1086)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1066)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:659)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:366)
... 13 more
複制