Spring Ioc 容器擴充套件點2 BeanFactoryPostProcessor自定義元資料配置
這個介面的語義與BeanPostProcessor類似,但有一處不同,BeanFactoryPostProcessor操作bean的元資料配置,也就是說,Spring IOC容器允許BeanFactoryPostProcessor來讀取元資料配置並在容器例項化任何bean(除了BeanFactoryPostProcessor)之前修改他。
開發者可以配置多個BeanFactoryPostProcessor例項,也可以通過設定order屬性來控制執行順序。
注意:如果想要修改實際的bean例項,需要使用BeanPostProcessor,在BeanFactoryPostProcessor中使用這些Bean的例項雖然在技術上是可行的,但是這個會將bean過早的例項化,違反了標準的容器生命週期,同時也會導致一些問題,例如繞過bean的後置處理。
BeanFactoryPostProcessor會在整個容器內起作用,所以他僅僅與正在使用的容器相關。
但在ApplicationContext中宣告時,bean工廠後置處理器會自動被執行,這樣就可以對定義在容器中的元資料進行修改,Spring包含了一些預定義的bean工廠後置處理器,例如PropertyOverrideConfigurer和PropertyPlaceholderConfigurer。自定義的BeanFactoryPostProcessor也可以使用,例如註冊自定義的屬性編輯器。
例子:類名替換PropertyPlaceholderConfigurer
這樣做可以使部署應用程式的人能夠制定特定於環境的屬性,如資料庫URL和密碼,而無需修改容器的主XML定義檔案。
從使用了標準JavaProperties格式的bean定義的分離檔案中,開發者可以使用PropertyPlaceholderConfigurer來具體化屬性值。這樣做可以使部署應用程式的使用者能夠定製特定於環境的屬性,如資料庫URL和密碼,而無需修改容器的主要XML定義檔案,從而降低了檔案的複雜性和風險
考慮一下這個基於XML的元資料配置程式碼片段,這裡的DataSource使用了佔位符來定義,這個示例展示了從properties檔案中配置屬性的方法,在執行時,PropertyPlaceholderConfigurer就會用於元資料為資料來源替換指定屬性,指定屬性的值換做為${property-name}形式中的佔位符,這裡運用了Ant/log4j/JSPEL的風格
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations" value="classpath:com/foo/jdbc.properties"/>
</bean>
<bean id="dataSource" destroy-method="close" class="org.apache.commons.dbcp.BasicDataSource">
<property name="dirverClassName" value="${j}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
而真正的值是來自於標準的Java Properties格式的檔案:
jdbc.driverClassName=org.hsqldb.jdbcDriver
jdbc.url=jdbc:hslqdb:hsql://production:9002
jdbc.username=sa
jdbc.password=root
在上面的例子中,字串${jdbc.username}在執行時會被sa替換,其它佔位符也一樣,所有匹配的屬性檔案都會使用鍵值替換佔位符,PropertyPlaceholderConfigurer在bean定義的屬性中會檢查佔位符,此外,對佔位符還可以自定義字首和字尾。
PropertyPlaceholderConfigurer不僅僅會檢視在Properties檔案中指定的屬性,預設情況下,如果他在指定的屬性檔案中找不到屬性,那麼他會檢查Java System的屬性,開發者可以通過設定systemPropertiesMode屬性,使用下面三個整數的其中一個來自定義這種行為:
never(0)從不檢查系統屬性
fallback(1)如果沒有在指定的屬性檔案中解析到屬性,那麼就檢查系統屬性(預設)
override(2)在檢查指定的屬性檔案之前,首先去檢查系統屬性,允許系統屬性覆蓋其它任意的屬性資源
你可以使用PropertyPlaceholderConfigurer來替換類名,在開發者在執行時需要選擇某個特定的實現類時,這個很有用。
例子:修改屬性的值
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="person" class="com.test.entity.Person">
<property name="name" value="fang"></property>
<property name="age" value="10"></property>
<property name="birthday" value="10"></property>
</bean>
<bean id="beanFactoryPostTest" class="com.test.config.BeanFactoryPostTest"></bean>
</beans>
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Person {
private Integer age;
private String name;
private String birthday;
}
public class BeanFactoryPostTest implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
String[] beanStr = beanFactory
.getBeanDefinitionNames();
for (String beanName : beanStr) {
if ("person".equals(beanName)) {
BeanDefinition beanDefinition = beanFactory
.getBeanDefinition(beanName);
MutablePropertyValues m = beanDefinition.getPropertyValues();
if (m.contains("birthday")) {
try {
m.addPropertyValue("birthday", "世界和平");
} catch (Exception e) {
System.out.println("錯誤");
}
}
}
}
}
}
@GetMapping("/test")
public Object get() {
ApplicationContext factory = new ClassPathXmlApplicationContext("classpath:Test.xml");
return factory.getBean("person", Person.class);
}
可以看到person的birthday的屬性值被修改了,使用BeanPostProcessor裡面也能夠實現此功能。
例子 PropertyOverrideConfigurer
PropertyOverrideConfigurer 是另一種bean工廠後置處理器,類似於PropertyPlaceholderConfigurer但是與其不同的是,對於所有的bean屬性,原始定義可能有預設值也可能沒有值,如果一個properties覆蓋檔案沒有特定的bean屬性,這就會使用預設的上下文定義。
注意bean定義是不知道是否被覆蓋的,所以從XML定義檔案中不能馬上看到那個配置檔案正在被使用,在擁有多個PropertyOverrideConfigurer例項的情況下,為相同的bean的屬性定義不同的值,基於覆蓋機制,只會最後一個生效。