Spring中bean工廠后置處理器也就是BeanFactoryPostProcessor接口主届,是用來(lái)干什么的呢厂抽?我們都知道一個(gè)好的框架必備的特性至少得有開(kāi)閉原則羡蛾,可擴(kuò)展性评架。
在前面的文章里SpringIOC源碼閱讀—BeanDefinitionDocumentReader我們了解到Spring對(duì)bean定義的載入有很多種方式眷茁,讀取的過(guò)程是可插拔的,不論何種形式纵诞,spring的IOC容器只要獲得了bean定義信息上祈,都可以正常工作。而我們熟知的配置讀取方式就是XML文件,如果你希望登刺,可以自己定制配置信息的讀取過(guò)程籽腕,這就是Spring的特性體現(xiàn)之一。
同樣BeanFactoryPostProcessor
也是Spring可擴(kuò)展性的一個(gè)體現(xiàn)纸俭,我們讀一下這個(gè)接口的源碼
public interface BeanFactoryPostProcessor {
/**
* Modify the application context's internal bean factory after its standard
* initialization. All bean definitions will have been loaded, but no beans
* will have been instantiated yet. This allows for overriding or adding
* properties even to eager-initializing beans.
* @param beanFactory the bean factory used by the application context
* @throws org.springframework.beans.BeansException in case of errors
*/
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
注釋的意思是允許我們?cè)诠S里所有的bean被加載進(jìn)來(lái)后但是還沒(méi)初始化前节仿,對(duì)所有bean的屬性進(jìn)行修改也可以add屬性值。
下面通過(guò)實(shí)例看下效果
beans.xml
<?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-2.5.xsd">
<!-- 普通的bean -->
<bean id="user" init-method="init" class="xz.quartz.analysis.User"></bean>
<!-- BeanFactroy后置處理器 -->
<bean id="beanfactorypostpro" class="xz.quartz.analysis.beanfactorypostpro"></bean>
</beans>
我們定義一個(gè)普通bean和一個(gè)BeanFactroy后置處理器
User類(lèi)
package xz.quartz.analysis;
public class User {
String name;
String age;
public User() {
System.out.println("construtor");
System.out.println("name:"+name+",age:"+age);
}
public void go(){
System.out.println("age:"+age);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
}
BeanFactroy后置處理器類(lèi)
package xz.quartz.analysis;
import org.springframework.beans.BeansException;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
public class beanfactorypostpro implements BeanFactoryPostProcessor{
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("******調(diào)用BeanFactoryPostProcessor開(kāi)始");
//獲取到Spring中所有的beanName
String[] beanStr = beanFactory.getBeanDefinitionNames();
//循環(huán)bean做出自定義的操作
for (String beanName : beanStr) {
System.out.println("bean name:"+beanName);
if ("user".equals(beanName)) {
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
System.out.println("修改user的age值");
beanDefinition.getPropertyValues().add("age", "20");
}
}
System.out.println("******調(diào)用BeanFactoryPostProcessor結(jié)束");
}
}
在上面BeanFactoryPostProcessor的實(shí)現(xiàn)類(lèi)里通過(guò)我寫(xiě)的注釋可以看到這個(gè)工廠后置器獲取到所有的beanName掉蔬,并且可以對(duì)特定的bean或者全部bean進(jìn)行操作廊宪,這也就是后置器的核心所在之處。
main方法
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:beans.xml");
System.out.println("獲取到User實(shí)例 ");
((User)applicationContext.getBean("user")).go();
}
主函數(shù)中第一行初始化的時(shí)候女轿,讀取beans.xml時(shí)箭启,會(huì)獲取實(shí)現(xiàn)了BeanFactoryPostProcessor接口的bean,然后把它作為后置處理器類(lèi)來(lái)處理蛉迹。
然后我們通過(guò)調(diào)用user的go方法傅寡,輸出age的值來(lái)看下
******調(diào)用BeanFactoryPostProcessor開(kāi)始
bean name:user
修改user的age值
bean name:beanfactorypostpro
******調(diào)用BeanFactoryPostProcessor結(jié)束
construtor
name:null,age:null
獲取到User實(shí)例
age:20
我們?cè)贐eanFactroy后置處理器類(lèi)里給user的age屬性成功賦值成20,大家通過(guò)程序?qū)嵗梢钥吹胶笾锰幚砥鞯淖饔煤陀梅恕?/p>
在這里提出兩個(gè)疑問(wèn)
1.后置處理器和bean指定的init-method還有構(gòu)造方法的順序是誰(shuí)先誰(shuí)后呢北救?
2.這三個(gè)方法同時(shí)對(duì)屬性賦值荐操,最終會(huì)是哪個(gè)起作用呢?
對(duì)上面的兩個(gè)問(wèn)題珍策,我們改造下代碼托启,先把構(gòu)造方法加上賦值過(guò)程
User類(lèi)
package xz.quartz.analysis;
public class User {
String name;
String age;
void init(){
System.out.println("init");
}
public User() {
System.out.println("construtor");
name = "zx-construtor";
age = "zx-construtor";
System.out.println("name:"+name+",age:"+age);
}
public void go(){
System.out.println("最終age的值:"+age);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
}
輸出結(jié)果:
******調(diào)用BeanFactoryPostProcessor開(kāi)始
bean name:user
修改user的age值
bean name:beanfactorypostpro
******調(diào)用BeanFactoryPostProcessor結(jié)束
construtor
name:zx-construtor,age:zx-construtor
init
name:zx-construtor,age:20
獲取到User實(shí)例
最終age的值:20
可以看到程序先執(zhí)行后置器將值改為20
,然后執(zhí)行的構(gòu)造方法攘宙,改為zx-construtor
屯耸,但是最終age的值為20
,到這里疑問(wèn)就來(lái)了蹭劈,為什么后執(zhí)行的賦值沒(méi)起作用呢疗绣?這個(gè)我后面解讀源碼的時(shí)候再聊。
我們繼續(xù)把init-method方法加上
package xz.quartz.analysis;
public class User {
String name;
String age;
void init(){
System.out.println("init");
name = "zx-init";
age = "zx-init";
System.out.println("name:"+name+",age:"+age);
}
public User() {
System.out.println("construtor");
name = "zx-construtor";
age = "zx-construtor";
System.out.println("name:"+name+",age:"+age);
}
public void go(){
System.out.println("最終age的值:"+age);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
}
看輸出結(jié)果
******調(diào)用BeanFactoryPostProcessor開(kāi)始
bean name:user
修改user的age值
bean name:beanfactorypostpro
******調(diào)用BeanFactoryPostProcessor結(jié)束
construtor
name:zx-construtor,age:zx-construtor
init
name:zx-init,age:zx-init
獲取到User實(shí)例
最終age的值:zx-init
通過(guò)上面兩個(gè)例子我們可以知道這三個(gè)方法的執(zhí)行順序
bean工廠后置處理器—>構(gòu)造方法—>init-method铺韧。
而賦值結(jié)果的疑問(wèn)后續(xù)通過(guò)源碼來(lái)解釋吧多矮!