Spring容器不僅提供了強大的配置方法,還提供了容器功能擴展點,用戶可以基於這些擴展點代碼可以增強容器的功能。實際上,Spring很多模塊,比如AOP,對註解的支持等,都是基於該機制實現的。
通過BeanPostProcessor定製Bean
BeanPostProcessor定義了一個能對Bean的初始化進行定製的接口。如果我們實現一個BeanPostProcessor,並註冊到容器,就能對bean的初始化和配置過程進行干預。
先來看看實現BeanPostProcessor的簡單例子:
public class InstantiationTracingBeanPostProcessor implements BeanPostProcessor {
// simply return the instantiated bean as-is
public Object postProcessBeforeInitialization(Object bean, String beanName) {
return bean; // we could potentially return any object reference here...
}
public Object postProcessAfterInitialization(Object bean, String beanName) {
System.out.println("Bean '" + beanName + "' created : " + bean.toString());
return bean;
}
}
回調方法postProcessBeforeInitialization發生在bean的初始化回調被調用(見上一節)之前,該方法返回bean的實例,在這裏我們可以給bean創建一個proxy返回給容器(這正是Spring AOP的實現方式)。回調方法postProcessAfterInitialization發生在bean的初始化方法被調用之後。
由於我們拿到了bean的引用,理論上,我們可以對bean做任何操作。
註冊BeanPostProcessor
BeanPostProcessor本身就是一個bean,因此它的定義方法與其他的bean沒有任何區別,一行簡單的定義即可:<bean class="scripting.InstantiationTracingBeanPostProcessor"/>
。
容器如果發現某個bean是一個BeanPostProcessor,那麼會提前初始化它。如果不是通過xml,而是通過java配置代碼來定義bean,返回的類型(Type)必須實現了BeanPostProcessor,否則容器無法識別這是一個BeanPostProcessor:
public class ConfigurationClass {
@Bean
public Type postProcessor() {
.....
}
}
我們還可以調用發context.getBeanFactory().addBeanPostProcessor()
動態地註冊BeanPostProcessor。
BeanPostProcessor執行順序
如果有多個BeanPostProcessor,那麼可以通過實現Order接口來定義他們的相對執行順序。
如果是context.getBeanFactory().addBeanPostProcessor()
添加的BeanPostProcessor,那麼那麼即使實現了Order接口也會被忽略,而是按註冊的順序執行;而且他們的順序在所有靜態BeanPostProcessor之前。
由於BeanPostProcessor也是一個bean,它自然可以依賴其他bean,這樣一來被依賴的bean將不會被任何BeanPostProcessor處理。這種情形是否有問題,取決於具體的業務場景,但還是避開微妙。Spring此時可能打印一條日誌:“Bean xxxx is not eligible for getting processed by all BeanPostProcessor interfaces”。
BeanPostProcessor的作用域
BeanPostProcessor只作用於它所在所在的context,對父子Context都不會起作用。
通過BeanFactoryPostProcessor定製配置數據
BeanFactoryPostProcessor的原理和BeanPostProcessor有點類似,不同的是,BeanPostProcessor處理bean實例,而BeanFactoryPostProcessor處理bean的定義。
BeanFactoryPostProcessor自身也是以bean的形式被定義,多個BeanFactoryPostProcessor實例也通過Order接口來排序;BeanFactoryPostProcessor僅對本容器上下文自作用,不會跨越上下文父子層。
BeanFactoryPostProcessor在容器讀取配置後自動執行,從而可以對容器配置做一些修改。Spring內部有很多此類處理器,比如PropertyOverrideConfigurer和PropertyPlaceholderConfigurer,前者執行bean屬性的覆蓋,後者負責將屬性佔位符替換成最終值。
PropertyPlaceholderConfigurer
這是大家用得最多的一個processor,用法如下:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations" value="classpath:com/something/jdbc.properties"/>
</bean>
<bean id="dataSource" destroy-method="close"
class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
//jdbc.properties文件的內容
jdbc.driverClassName=org.hsqldb.jdbcDriver
jdbc.url=jdbc:hsqldb:hsql://production:9002
jdbc.username=sa
jdbc.password=root
PropertyPlaceholderConfigurer檢查所有bean的定義,將佔位符(${jdbc.url})替換成屬性文件裏的值(jdbc.url=jdbc:hsqldb:hsql://production:9002)。如果屬性文件裏面沒有定義該佔位符,那麼還會去系統屬性(system properties)裏面去找(可以通過設置PropertyPlaceholderConfigurer的systemPropertiesMode來改變這種行爲)。
PropertyOverrideConfigurer
定義方式和上面類似,所使用屬性文件的類似以下:
dataSource.driverClassName=com.mysql.jdbc.Driver
dataSource.url=jdbc:mysql:mydb
這個屬性文件的意思是dataSource這個bean的driverClassName屬性重寫爲com.mysql.jdbc.Driver
。
示例代碼
定義一個Bean,有兩個屬性,一個有Setter方法,一個被@Tag(自定義註解)註釋。
public class ExampleBean {
@Tag
private String tagA;
private String tagB;
public void print() {
System.out.println("tagA = " + tagA);
System.out.println("tagB = " + tagB);
}
public void setTagB(String tagB) {
this.tagB = tagB;
}
定義了一個BeanPostProcessor,它遍歷bean的所有方法定義,如果發現某個屬性有@Tag註解,則注入一個值:
public class BeanPostProcessor implements org.springframework.beans.factory.config.BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
Field[] fields = bean.getClass().getDeclaredFields();
for (Field f : fields) {
if (f.getAnnotation(Tag.class) != null) {
f.setAccessible(true);
try {
f.set(bean, "BeanPostProcessor賦值");
} catch (Exception e) {
e.printStackTrace();
}
}
}
return bean;
}
}
定義一個BeanFactoryProcessor,它定位ExampleBean的definition,並注入一個屬性定義:
public class BeanFactoryPostProcessor implements org.springframework.beans.factory.config.BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
BeanDefinition beanDefinition = beanFactory.getBeanDefinition("exampleBean");
beanDefinition.getPropertyValues().addPropertyValue("tagB","BeanFactoryPostProcessor賦值");
}
}
}
完整的示例代碼在這裏,請看子工程postprocessor_xml。
FactoryBean
如果bean的初始化過程比較複雜,很難用xml配置來表達,可以定義一個FactoryBean,顧名思義它自己也是一個bean,但是它的使命是創建另一個bean。
FactoryBean接口包含以下方法:
1、Object getObject()
返回工廠創建的bean實例;
2、boolean isSingleTon()
bean是否單例;
3、Class getObjectType()
返回bean的類型;
如你提供一個名叫myBean的FactoryBean的xml定義,那麼當調用容器的getBean("myBean")
方法,返回的工廠創建的bean;如果調用getBean("&myBean")
,返回的是工廠自身。
總結
BeanPostProcessor和BeanFactoryProcessor是我們擴展Spring容器功能的利器,前者干預bean的創建,後者干預bean的定義。Spring全家桶的各功能模塊,大量使用了這兩個機制,充分體現了Spring的優秀設計。有興趣的人可以在Spring源碼裏找找BeanPostProcessor的實現類,會發現Spring那些給我們帶來便利的功能背後,其實都是一個個BeanPostProcessor實現的。