BeanPostProcessor
public interface BeanPostProcessor {
Object postProcessBeforeInitialization(
Object bean, String beanName) throws BeansException;
Object postProcessAfterInitialization(
Object bean, String beanName) throws BeansException;
BeanPostProcessor 接口定義bean初始化時(shí)的回調(diào)贾费,我們可以實(shí)現(xiàn)自己的邏輯依賴解析等。我們可以自己實(shí)現(xiàn)該接口從而在spring的容器完成初始化配置也可以實(shí)現(xiàn)類似plugin的方式上岗。我們可以定義多個(gè)類實(shí)現(xiàn)該接口江滨,然后定義一下執(zhí)行的優(yōu)先級(jí) spring文檔中叫order,這樣就會(huì)按我們?cè)O(shè)定的order執(zhí)行你定義的多個(gè)類。
ApplicationContext 可以自動(dòng)檢測(cè)到任何實(shí)現(xiàn)了BeanPostProcessor接口的bean和注冊(cè)這些bean作為post-processors梅肤。在bean創(chuàng)建的過程中調(diào)用。
在<em>spring-beans module</em>中有<em>FactoryBeanTests</em>這個(gè)類邑茄,找到第七個(gè)測(cè)試方法<b>testFactoryBeansWithIntermediateFactoryBeanAutowiringFailure</b>
@Test
public void testCircularReferenceWithPostProcessor() {
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
new XmlBeanDefinitionReader(factory).loadBeanDefinitions(CIRCULAR_CONTEXT);
CountingPostProcessor counter = new CountingPostProcessor();
factory.addBeanPostProcessor(counter);
BeanImpl1 impl1 = factory.getBean(BeanImpl1.class);
assertNotNull(impl1);
assertNotNull(impl1.getImpl2());
assertNotNull(impl1.getImpl2());
assertSame(impl1, impl1.getImpl2().getImpl1());
assertEquals(1, counter.getCount("bean1"));
assertEquals(1, counter.getCount("bean2"));
}
該方法讀取的是
<i>CountingPostProcessor</i>實(shí)現(xiàn)了<i>BeanPostProcessor</i>接口姨蝴,我們?cè)谄渲械膬蓚€(gè)方法中加上日志
public static class CountingPostProcessor implements BeanPostProcessor {
private final Map<String, AtomicInteger> count = new HashMap<String, AtomicInteger>();
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
System.out.println("源碼閱讀debug postProcessBeforeInitialization:"+bean);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
System.out.println("源碼閱讀debug postProcessAfterInitialization:"+bean);
if (bean instanceof FactoryBean) {
return bean;
}
AtomicInteger c = count.get(beanName);
if (c == null) {
c = new AtomicInteger(0);
count.put(beanName, c);
}
c.incrementAndGet();
return bean;
}
public int getCount(String beanName) {
AtomicInteger c = count.get(beanName);
if (c != null) {
return c.intValue();
}
else {
return 0;
}
}
}
我們找到<em>FactoryBeanTests-circular.xml</em>查看其內(nèi)容
<?xml version="1.0" encoding="ISO-8859-1"?>
<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-3.0.xsd">
<bean id="bean1" class="org.springframework.beans.factory.FactoryBeanTests$PassThroughFactoryBean" primary="true">
<constructor-arg value="org.springframework.beans.factory.FactoryBeanTests$BeanImpl1"/>
<property name="instanceName" value="beanImpl1"/>
</bean>
<bean id="beanImpl1" class="org.springframework.beans.factory.FactoryBeanTests$BeanImpl1">
<property name="impl2" ref="bean2"/>
</bean>
<bean id="bean2" class="org.springframework.beans.factory.FactoryBeanTests$PassThroughFactoryBean" primary="true">
<constructor-arg value="org.springframework.beans.factory.FactoryBeanTests$BeanImpl2"/>
<property name="instanceName" value="beanImpl2"/>
</bean>
<bean id="beanImpl2" class="org.springframework.beans.factory.FactoryBeanTests$BeanImpl2">
<property name="impl1" ref="bean1"/>
</bean>
</beans>
其中的兩個(gè)bean比較簡單
public static class BeanImpl1 {
private BeanImpl2 impl2;
public BeanImpl2 getImpl2() {
return impl2;
}
public void setImpl2(BeanImpl2 impl2) {
this.impl2 = impl2;
}
}
public static class BeanImpl2 {
private BeanImpl1 impl1;
public BeanImpl1 getImpl1() {
return impl1;
}
public void setImpl1(BeanImpl1 impl1) {
this.impl1 = impl1;
}
}
<b>但是注意</b>這兩個(gè)bean存在循環(huán)依賴關(guān)系 這個(gè)待會(huì)再說,先看CountingPostProcessor implements BeanPostProcessor
的兩個(gè)方法的執(zhí)行情況肺缕,跑一下junit測(cè)試 debug
在load的時(shí)候會(huì)解析xml并實(shí)例化bean 實(shí)例化得過程會(huì)調(diào)用到這兩個(gè)方法
跑完看一下consle打印的日志
對(duì)了先說一下怎么開啟spring源碼的日志
其實(shí)很簡單
# Set root logger level to Debug and its only appender to A1
log4j.rootLogger=ALL,A1
log4j.category.org.springframework=DEBUG
# A1 is set to be ConsoleAppender
log4j.appender.A1=org.apache.log4j.ConsoleAppender
# A1 uses PatternLayout
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %p [%t] - %m%n
其中l(wèi)og4j配置下一篇再補(bǔ)充一下
ALL是最小的 就是所有有關(guān)的日志都會(huì)打印左医,設(shè)置成<b>ALL</b>即可
還有一步要做就是加入編譯環(huán)境
項(xiàng)目右鍵<b>Build path</b>
這樣就可以打日志了
看一下剛剛debug輸出的日志
2016-08-30 15:16:16 DEBUG [main] - Adding [systemProperties] PropertySource with lowest search precedence
2016-08-30 15:16:16 DEBUG [main] - Adding [systemEnvironment] PropertySource with lowest search precedence
2016-08-30 15:16:16 DEBUG [main] - Initialized StandardEnvironment with PropertySources [systemProperties,systemEnvironment]
2016-08-30 15:16:16 INFO [main] - Loading XML bean definitions from class path resource [org/springframework/beans/factory/FactoryBeanTests-circular.xml]
2016-08-30 15:16:16 DEBUG [main] - Using JAXP provider [com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl]
2016-08-30 15:16:16 DEBUG [main] - Loading schema mappings from [META-INF/spring.schemas]
2016-08-30 15:16:16 DEBUG [main] - Loaded schema mappings: {http://www.springframework.org/schema/beans/spring-beans-4.2.xsd=org/springframework/beans/factory/xml/spring-beans-4.2.xsd, http://www.springframework.org/schema/beans/spring-beans-3.2.xsd=org/springframework/beans/factory/xml/spring-beans-3.2.xsd, http://www.springframework.org/schema/util/spring-util-3.0.xsd=org/springframework/beans/factory/xml/spring-util-3.0.xsd, http://www.springframework.org/schema/util/spring-util-4.1.xsd=org/springframework/beans/factory/xml/spring-util-4.1.xsd, http://www.springframework.org/schema/util/spring-util-2.0.xsd=org/springframework/beans/factory/xml/spring-util-2.0.xsd, http://www.springframework.org/schema/util/spring-util-2.5.xsd=org/springframework/beans/factory/xml/spring-util-2.5.xsd, http://www.springframework.org/schema/tool/spring-tool-3.0.xsd=org/springframework/beans/factory/xml/spring-tool-3.0.xsd, http://www.springframework.org/schema/tool/spring-tool-4.1.xsd=org/springframework/beans/factory/xml/spring-tool-4.1.xsd, http://www.springframework.org/schema/tool/spring-tool-2.0.xsd=org/springframework/beans/factory/xml/spring-tool-2.0.xsd, http://www.springframework.org/schema/util/spring-util-3.2.xsd=org/springframework/beans/factory/xml/spring-util-3.2.xsd, http://www.springframework.org/schema/beans/spring-beans-4.1.xsd=org/springframework/beans/factory/xml/spring-beans-4.1.xsd, http://www.springframework.org/schema/beans/spring-beans-2.0.xsd=org/springframework/beans/factory/xml/spring-beans-2.0.xsd, http://www.springframework.org/schema/tool/spring-tool-2.5.xsd=org/springframework/beans/factory/xml/spring-tool-2.5.xsd, http://www.springframework.org/schema/beans/spring-beans-3.1.xsd=org/springframework/beans/factory/xml/spring-beans-3.1.xsd, http://www.springframework.org/schema/beans/spring-beans-2.5.xsd=org/springframework/beans/factory/xml/spring-beans-2.5.xsd, http://www.springframework.org/schema/beans/spring-beans.xsd=org/springframework/beans/factory/xml/spring-beans-4.2.xsd, http://www.springframework.org/schema/tool/spring-tool.xsd=org/springframework/beans/factory/xml/spring-tool-4.2.xsd, http://www.springframework.org/schema/tool/spring-tool-3.2.xsd=org/springframework/beans/factory/xml/spring-tool-3.2.xsd, http://www.springframework.org/schema/util/spring-util-4.0.xsd=org/springframework/beans/factory/xml/spring-util-4.0.xsd, http://www.springframework.org/schema/tool/spring-tool-4.0.xsd=org/springframework/beans/factory/xml/spring-tool-4.0.xsd, http://www.springframework.org/schema/util/spring-util-3.1.xsd=org/springframework/beans/factory/xml/spring-util-3.1.xsd, http://www.springframework.org/schema/beans/spring-beans-4.0.xsd=org/springframework/beans/factory/xml/spring-beans-4.0.xsd, http://www.springframework.org/schema/util/spring-util-4.2.xsd=org/springframework/beans/factory/xml/spring-util-4.2.xsd, http://www.springframework.org/schema/beans/spring-beans-3.0.xsd=org/springframework/beans/factory/xml/spring-beans-3.0.xsd, http://www.springframework.org/schema/tool/spring-tool-3.1.xsd=org/springframework/beans/factory/xml/spring-tool-3.1.xsd, http://www.springframework.org/schema/util/spring-util.xsd=org/springframework/beans/factory/xml/spring-util-4.2.xsd, http://www.springframework.org/schema/tool/spring-tool-4.2.xsd=org/springframework/beans/factory/xml/spring-tool-4.2.xsd}
2016-08-30 15:16:16 DEBUG [main] - Found XML schema [http://www.springframework.org/schema/beans/spring-beans-3.0.xsd] in classpath: org/springframework/beans/factory/xml/spring-beans-3.0.xsd
2016-08-30 15:16:16 DEBUG [main] - Loading bean definitions
2016-08-30 15:16:27 DEBUG [main] - Creating shared instance of singleton bean 'bean1'
2016-08-30 15:16:27 DEBUG [main] - Creating instance of bean 'bean1'
2016-08-30 15:16:27 DEBUG [main] - Eagerly caching bean 'bean1' to allow for resolving potential circular references
源碼閱讀debug postProcessBeforeInitialization:org.springframework.beans.factory.FactoryBeanTests$PassThroughFactoryBean@659a969b
源碼閱讀debug postProcessAfterInitialization:org.springframework.beans.factory.FactoryBeanTests$PassThroughFactoryBean@659a969b
2016-08-30 15:18:13 DEBUG [main] - Finished creating instance of bean 'bean1'
2016-08-30 15:18:13 DEBUG [main] - Creating shared instance of singleton bean 'beanImpl1'
2016-08-30 15:18:13 DEBUG [main] - Creating instance of bean 'beanImpl1'
2016-08-30 15:18:16 DEBUG [main] - Eagerly caching bean 'beanImpl1' to allow for resolving potential circular references
2016-08-30 15:18:16 DEBUG [main] - Creating shared instance of singleton bean 'bean2'
2016-08-30 15:18:16 DEBUG [main] - Creating instance of bean 'bean2'
2016-08-30 15:18:16 DEBUG [main] - Eagerly caching bean 'bean2' to allow for resolving potential circular references
源碼閱讀debug postProcessBeforeInitialization:org.springframework.beans.factory.FactoryBeanTests$PassThroughFactoryBean@49b0b76
源碼閱讀debug postProcessAfterInitialization:org.springframework.beans.factory.FactoryBeanTests$PassThroughFactoryBean@49b0b76
2016-08-30 15:18:19 DEBUG [main] - Finished creating instance of bean 'bean2'
2016-08-30 15:18:19 DEBUG [main] - Creating shared instance of singleton bean 'beanImpl2'
2016-08-30 15:18:19 DEBUG [main] - Creating instance of bean 'beanImpl2'
2016-08-30 15:18:21 DEBUG [main] - Eagerly caching bean 'beanImpl2' to allow for resolving potential circular references
2016-08-30 15:18:21 DEBUG [main] - Returning cached instance of singleton bean 'bean1'
2016-08-30 15:18:21 DEBUG [main] - Returning eagerly cached instance of singleton bean 'beanImpl1' that is not fully initialized yet - a consequence of a circular reference
源碼閱讀debug postProcessAfterInitialization:org.springframework.beans.factory.FactoryBeanTests$BeanImpl1@769f71a9
源碼閱讀debug postProcessBeforeInitialization:org.springframework.beans.factory.FactoryBeanTests$BeanImpl2@4c9f8c13
源碼閱讀debug postProcessAfterInitialization:org.springframework.beans.factory.FactoryBeanTests$BeanImpl2@4c9f8c13
2016-08-30 15:18:23 DEBUG [main] - Finished creating instance of bean 'beanImpl2'
源碼閱讀debug postProcessAfterInitialization:org.springframework.beans.factory.FactoryBeanTests$BeanImpl2@4c9f8c13
源碼閱讀debug postProcessBeforeInitialization:org.springframework.beans.factory.FactoryBeanTests$BeanImpl1@769f71a9
源碼閱讀debug postProcessAfterInitialization:org.springframework.beans.factory.FactoryBeanTests$BeanImpl1@769f71a9
2016-08-30 15:18:25 DEBUG [main] - Finished creating instance of bean 'beanImpl1'
2016-08-30 15:18:25 DEBUG [main] - Returning cached instance of singleton bean 'beanImpl1'
2016-08-30 15:18:25 DEBUG [main] - Returning cached instance of singleton bean 'bean1'
可以看到日志已經(jīng)輸出授帕,其實(shí)仔細(xì)讀日志基本的初始化過程也可以了解,
接下來看下spring怎么處理兩個(gè)bean之間的循環(huán)依賴的炒辉。
其中日志中有幾條這樣的輸出
Eagerly caching bean 'bean1' to allow for resolving potential circular references
2016-08-30 15:18:16 DEBUG [main] - Eagerly caching bean 'beanImpl1' to allow for resolving potential circular references
2016-08-30 15:18:21 DEBUG [main] - Eagerly caching bean 'beanImpl2' to allow for resolving potential circular references
2016-08-30 15:18:21 DEBUG [main] - Returning eagerly cached instance of singleton bean 'beanImpl1' that is not fully initialized yet
最后出現(xiàn)的這條日志其實(shí)就是 返回了之前的引用
找到輸出這條日志的類<em>AbstractBeanFactory</em>的<em>doGetBean方法</em> 有isSingletonCurrentlyInCreation的判斷
if (isSingletonCurrentlyInCreation(beanName)) {
logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
}
點(diǎn)進(jìn)去找到源頭是<b>DefaultSingletonBeanRegistry</b>
該類實(shí)現(xiàn)了spring-core中的接口 這個(gè)后續(xù)再分析豪墅,先看本類聲明了需要存儲(chǔ)的屬性
/** Cache of singleton objects: bean name --> bean instance */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(64);
/** Cache of singleton factories: bean name --> ObjectFactory */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);
/** Cache of early singleton objects: bean name --> bean instance */
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);
/** Set of registered singletons, containing the bean names in registration order */
private final Set<String> registeredSingletons = new LinkedHashSet<String>(64);
/** Names of beans that are currently in creation */
private final Set<String> singletonsCurrentlyInCreation =
Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>(16));
/** Names of beans currently excluded from in creation checks */
private final Set<String> inCreationCheckExclusions =
Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>(16));
/** List of suppressed Exceptions, available for associating related causes */
private Set<Exception> suppressedExceptions;
/** Flag that indicates whether we're currently within destroySingletons */
private boolean singletonsCurrentlyInDestruction = false;
/** Disposable bean instances: bean name --> disposable instance */
private final Map<String, Object> disposableBeans = new LinkedHashMap<String, Object>();
/** Map between containing bean names: bean name --> Set of bean names that the bean contains */
private final Map<String, Set<String>> containedBeanMap = new ConcurrentHashMap<String, Set<String>>(16);
/** Map between dependent bean names: bean name --> Set of dependent bean names */
private final Map<String, Set<String>> dependentBeanMap = new ConcurrentHashMap<String, Set<String>>(64);
/** Map between depending bean names: bean name --> Set of bean names for the bean's dependencies */
private final Map<String, Set<String>> dependenciesForBeanMap = new ConcurrentHashMap<String, Set<String>>(64);
大概瀏覽下里面的方法
看到有對(duì)屬性對(duì)象的remove和add等操作
其實(shí)
singletonFactories,用于存儲(chǔ)在spring內(nèi)部所使用的beanName->對(duì)象工廠的引用黔寇,一旦最終對(duì)象被創(chuàng)建(通過objectFactory.getObject())偶器,此引用信息將刪除
earlySingletonObjects,用于存儲(chǔ)在創(chuàng)建Bean早期對(duì)創(chuàng)建的原始bean的一個(gè)引用缝裤,注意這里是原始bean屏轰,即使用工廠方法或構(gòu)造方法創(chuàng)建出來的對(duì)象,一旦對(duì)象最終創(chuàng)建好憋飞,此引用信息將刪除
最終創(chuàng)建完成bean后這兩個(gè)對(duì)象中都不會(huì)存在bean 即size為0
debug發(fā)現(xiàn)為了完成最原始得bean注入意思是
bean1 注入的是最原始得bean2 (此時(shí)的bean2不包括屬性bean1)
bean2注入的也是最原始得bean1(此時(shí)的bean1不包括屬性bean2)
會(huì)放到cached
<em>FactoryBeanRegistrySupport</em>中
該類擁有屬性
/** Cache of singleton objects created by FactoryBeans: FactoryBean name --> object */
private final Map<String, Object> factoryBeanObjectCache = new ConcurrentHashMap<String, Object>(16);
兩者的循環(huán)依賴是基于Setter的依賴注入
基于Setter的依賴注入霎苗,是在Bean完成實(shí)例化之后,容器調(diào)用Bean的setter方法來完成的榛做。
這樣的話就解決了循環(huán)依賴問題唁盏。