文章目錄
-
- BeanPostProcessor自定義bean
- BeanFactoryPostProcessor自定義配置中繼資料
- 使用FactoryBean自定義執行個體化邏輯
Spring提供了一系列的接口來提供對Spring容器的擴充功能。下面我們一一介紹。
前面一篇文章我們在自定義bean中提到,可以實作Spring的InitializingBean和DisposableBean接口來實作自定義bean的生命周期。如果是容器級别的,Spring提供了更加強大的BeanPostProcessor,來實作在容器級對Bean的擴充。
BeanPostProcessor接口定義了兩個方法:
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
該方法在調用容器初始化方法(如InitializingBean.afterPropertiesSet()或任何聲明的init方法)之前,以及在任何bean初始化之後,被調用。
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
該方法在容器初始化方法之後被調用。
BeanPostProcessor可以配置多個,如果想控制多個BeanPostProcessor的順序,可以實作Ordered接口,來定義他們的順序。
雖然BeanPostProcessor是通過ApplicationContext自動檢測的,你也可通過ConfigurableBeanFactory的addBeanPostProcessor來手動注冊。手動注冊則其Ordered失效,以手動注冊的先後為準。
還要注意,以程式設計方式注冊的BeanPostProcessor執行個體總是在注冊為自動檢測的執行個體之前進行處理,而不接收任何顯式排序。
所有BeanPostProcessor執行個體和這些執行個體直接引用的bean都在啟動時執行個體化,因為AOP自動代理是作為BeanPostProcessor本身實作的,是以BeanPostProcessor執行個體和它們直接引用的bean都不符合自動代理的條件。
下面是一個調用的例子:
<bean id="beanA" class="com.flydean.beans.BeanA"/>
<bean id="beanB" class="com.flydean.beans.BeanB"/>
<bean class="com.flydean.beans.InstantiationTracingBeanPostProcessor"/>
調用實作:
public static void main(final String[] args) throws Exception {
ApplicationContext ctx = new ClassPathXmlApplicationContext("bean-post-processor.xml");
BeanA beanA = (BeanA) ctx.getBean("beanA");
System.out.println(beanA);
}
BeanFactoryPostProcessor接口的語義與BeanPostProcessor的語義相似,但有一個主要差別:BeanFactoryPostProcessor對Bean配置中繼資料進行操作。也就是說,Spring IOC容器允許BeanFactoryPostProcessor讀取配置中繼資料,并可能在容器執行個體化BeanFactoryPostProcessor執行個體以外的任何bean之前對其進行更改。
BeanFactoryPostProcessor也可以配置多個,并通過實作Ordered接口來确定執行順序。BeanFactoryPostProcessor定義了一個方法:
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
通過該方法可以擷取到可配置的beanFactory進而對bean定義進行修改。
Spring提供了很多預定義的bean工廠後處理器,例如PropertyOverrideConfigurer和PropertyPlaceholderConfigurer。下面我們通過例子來說明怎麼使用。
PropertyOverrideConfigurer類名替換
PropertyPlaceholderConfigurer主要用于從外部的Property檔案讀取屬性,用來替換定義好的配置,這樣做可以使部署應用程式的人員自定義特定于環境的屬性,如資料庫URL和密碼,而不必為容器修改一個或多個XML定義主檔案進而增加複雜性或風險。
下面是配置的XML檔案:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations" value="classpath:jdbc.properties"/>
</bean>
<bean id="dataSource" destroy-method="close"
class="com.flydean.beans.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>
這個例子展示了屬性被配置在外部的Properties檔案中。在運作時,使用PropertyPlaceholderConfigurer将中繼資料替換成DataSource中的某些屬性。要替換的值被指定為${property-name}格式的占位符,該格式遵循ant和log4j以及JSP EL樣式。
真實的值取自外部的Java Properties格式的檔案:
jdbc.driverClassName=org.hsqldb.jdbcDriver
jdbc.url=jdbc:hsqldb:hsql://production:9002
jdbc.username=sa
jdbc.password=root
PropertyOverrideConfigurer屬性覆寫
PropertyOverrideConfigurer可以用來覆寫Bean屬性的預設值,或者設定新的值。我們看一個例子:
<bean class="org.springframework.beans.factory.config.PropertyOverrideConfigurer">
<property name="locations" value="classpath:override.properties"/>
<property name="properties">
<value>beanOverride.url=com.something.DefaultStrategy</value>
</property>
</bean>
<bean name="beanOverride" class="com.flydean.beans.BeanOverride"/>
對應的類是:
@Data
public class BeanOverride {
private String name="beanA";
private String url="http://www.163.com";
}
它的預設屬性會被覆寫。
FactoryBean接口提供3個方法:
- Object getObject(): 傳回工廠建立的執行個體,該執行個體可能是被共享的, 取決于該執行個體是單例還是多例模式。
- boolean isSingleton():判斷FactoryBean傳回的是單例還是多例。
- Class getObjectType():傳回getObject() 方法傳回的類型,如果提前不知道類型,那麼傳回null。
我們可以實作FactoryBean接口來自定義Bean的實作邏輯。
public class BeanFactoryBean implements FactoryBean {
@Resource
private BeanA beanA;
@Override
public Object getObject() throws Exception {
return beanA;
}
@Override
public Class<?> getObjectType() {
return BeanA.class;
}
}
下面是其配置:
<context:annotation-config/>
<bean id="beanA" class="com.flydean.beans.BeanA"/>
<bean id="beanFactoryBean" class="com.flydean.beans.BeanFactoryBean"/>
如何使用?
public static void main(final String[] args) throws Exception {
ApplicationContext ctx = new ClassPathXmlApplicationContext("bean-factory.xml");
BeanFactoryBean beanFactoryBean = (BeanFactoryBean) ctx.getBean("&beanFactoryBean");
System.out.println(beanFactoryBean.getObject());
System.out.println(beanFactoryBean.getObjectType());
BeanA beanA=(BeanA)ctx.getBean("beanFactoryBean");
System.out.println(beanA);
}
當需要向容器請求實際的FactoryBean執行個體本身而不是它生成的bean時,在調用ApplicationContext的getbean()方法時,在bean的ID前面加上符号(&)。是以,對于ID為beanFactoryBean的給定FactoryBean,在容器上調用getBean(“beanFactoryBean”)傳回FactoryBean生成的bean,而調用getBean(“&beanFactoryBean”)則傳回FactoryBean執行個體本身。
- 區塊鍊從入門到放棄系列教程-涵蓋密碼學,超級賬本,以太坊,Libra,比特币等持續更新
- Spring Boot 2.X系列教程:七天從無到有掌握Spring Boot-持續更新
- Spring 5.X系列教程:滿足你對Spring5的一切想象-持續更新
- java程式員從小工到專家成神之路(2020版)-持續更新中,附詳細文章教程