亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 開發 > Java > 正文

Spring循環依賴正確性及Bean注入的順序關系詳解

2024-07-13 10:16:07
字體:
來源:轉載
供稿:網友

一、前言

我們知道 Spring 可以是懶加載的,就是當真正使用到 Bean 的時候才實例化 Bean。當然也不全是這樣,例如配置 Bean 的 lazy-init 屬性,可以控制 Spring 的加載時機?,F在機器的性能、內存等都比較高,基本上也不使用懶加載,在容器啟動時候來加載bean,啟動時間稍微長一點兒,這樣在實際獲取 bean 供業務使用時,就可以減輕不少負擔,這個后面再做分析。 我們使用到 Bean 的時候,最直接的方式就是從 Factroy 中獲取,這個就是加載 Bean 實例的源頭。

最近在做項目時候遇到一個奇葩問題,就是bean依賴注入的正確性與bean直接注入的順序有關系,但是正常情況下明明是和順序沒關系的啊,究竟啥情況那,不急,讓我一一道來。

二、普通Bean循環依賴-與注入順序無關

2.1 循環依賴例子與原理

public class BeanA {private BeanB beanB;public BeanB getBeanB() { return beanB;}public void setBeanB(BeanB beanB) { this.beanB = beanB;}}
public class BeanB {private BeanA beanA;public BeanA getBeanA() { return beanA;}public void setBeanA(BeanA beanA) { this.beanA = beanA;}}
<bean id="beanA" class="com.alibaba.test.circle.BeanA"><property name="beanB"> <ref bean="beanB" /></property></bean>
<bean id="beanB" class="com.alibaba.test.circle.BeanB"><property name="beanA"> <ref bean="beanA" /></property></bean>

上述循環依賴注入能夠正常工作,這是因為Spring提供了EarlyBeanReference功能,首先Spring里面有個名字為singletonObjects的并發map用來存放所有實例化并且初始化好的bean,singletonFactories則用來存放需要解決循環依賴的bean信息(beanName,和一個回調工廠)。當實例化beanA時候會觸發getBean(“beanA”);首先看singletonObjects中是否有beanA有則返回:

(1)

Object sharedInstance = getSingleton(beanName);//getSingleton(beanName,true);if (sharedInstance != null && args == null) {if (logger.isDebugEnabled()) { 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"); } else { logger.debug("Returning cached instance of singleton bean '" + beanName + "'"); }} // 如果是普通bean直接返回,工廠bean則返回sharedInstance.getObject();bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);}
protected Object getSingleton(String beanName, boolean allowEarlyReference) { Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { synchronized (this.singletonObjects) { singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null && allowEarlyReference) { ObjectFactory singletonFactory = (ObjectFactory) this.singletonFactories.get(beanName); if (singletonFactory != null) {  singletonObject = singletonFactory.getObject();  this.earlySingletonObjects.put(beanName, singletonObject);  this.singletonFactories.remove(beanName); } } } } return (singletonObject != NULL_OBJECT ? singletonObject : null);}

一開始肯定沒有所以會實例化beanA,如果設置了allowCircularReferences=true(默認為true)并且當前bean為單件并且該bean目前在創建中,則初始化屬性前把該bean信息放入singletonFactories單件map里面:

(2)

boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {if (logger.isDebugEnabled()) { logger.debug("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references");}addSingletonFactory(beanName, new ObjectFactory() { public Object getObject() throws BeansException { return getEarlyBeanReference(beanName, mbd, bean); }});}
protected void addSingletonFactory(String beanName, ObjectFactory singletonFactory) {Assert.notNull(singletonFactory, "Singleton factory must not be null");synchronized (this.singletonObjects) { if (!this.singletonObjects.containsKey(beanName)) { this.singletonFactories.put(beanName, singletonFactory); this.earlySingletonObjects.remove(beanName); this.registeredSingletons.add(beanName); }}}

然后對該實例進行屬性注入beanB,屬性注入時候會getBean(“beanB”) ,發現beanB 不在singletonObjects中,就會實例化beanB,然后放入singletonFactories,然后進行屬性注入beanA,然后觸發getBean(“beanA”);這時候會到(1)getSingleton返回實例化的beanA。到此beanB初始化完畢添加beanB 到singletonObjects然后返回,然后beanA 初始化完畢,添加beanA到singletonObjects然后返回

2.2 允許循環依賴的開關

public class TestCircle2 {private final static ClassPathXmlApplicationContext moduleContext;private static Test test;static { moduleContext = new ClassPathXmlApplicationContext(new String[]{"beans-circile.xml"}); moduleContext.setAllowCircularReferences(false); test = (Test) moduleContext.getBean("test");}public static void main(String[] args) { System.out.println(test.name);}}

ClassPathXmlApplicationContext類中有個屬性allowCircularReferences用來控制是否允許循環依賴默認為true,這里設置為false后發現循環依賴還是可以正常運行,翻看源碼:

public ClassPathXmlApplicationContext(String[] configLocations) throws BeansException {this(configLocations, true, null);}
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException {super(parent);setConfigLocations(configLocations);if (refresh) { refresh();}}
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException {super(parent);setConfigLocations(configLocations);if (refresh) { refresh();}}

知道默認構造ClassPathXmlApplicationContext時候會刷新容器。

refresh方法會調用refreshBeanFactory:

protected final void refreshBeanFactory() throws BeansException {if (hasBeanFactory()) { destroyBeans(); closeBeanFactory();}try { // 創建bean工廠 DefaultListableBeanFactory beanFactory = createBeanFactory(); //定制bean工廠屬性 customizeBeanFactory(beanFactory); loadBeanDefinitions(beanFactory); synchronized (this.beanFactoryMonitor) { this.beanFactory = beanFactory; }}catch (IOException ex) { throw new ApplicationContextException( "I/O error parsing XML document for application context [" + getDisplayName() + "]", ex);}}
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {if (this.allowBeanDefinitionOverriding != null) { beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding.booleanValue());}if (this.allowCircularReferences != null) { beanFactory.setAllowCircularReferences(this.allowCircularReferences.booleanValue());}}

到這里就知道了,我們在調用 moduleContext.setAllowCircularReferences(false)前,spring留出的設置bean工廠的回調customizeBeanFactory已經執行過了,最終原因是,調用設置前,bean工廠已經refresh了,所以測試代碼改為:

public class TestCircle {private final static ClassPathXmlApplicationContext moduleContext;private static Test test;static { //初始化容器上下文,但是不刷新容器 moduleContext = new ClassPathXmlApplicationContext(new String[]{"beans-circile.xml"},false); moduleContext.setAllowCircularReferences(false); //刷新容器 moduleContext.refresh(); test = (Test) moduleContext.getBean("test");}public static void main(String[] args) { System.out.println(test.name);}}

現在測試就會拋出異常:

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'beanA' defined in class path resource [beans-circile.xml]: Cannot resolve reference to bean 'beanB' while setting bean property 'beanB'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'beanB' defined in class path resource [beans-circile.xml]: Cannot resolve reference to bean 'beanA' while setting bean property 'beanA'; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'beanA': Requested bean is currently in creation: Is there an unresolvable circular reference?

三、工廠Bean與普通Bean循環依賴-與注入順序有關

3.1 測試代碼

工廠bean

public class MyFactoryBean implements FactoryBean,InitializingBean{private String name;private Test test;public String getName() { return name;}public void setName(String name) { this.name = name;}public DependentBean getDepentBean() { return depentBean;}public void setDepentBean(DependentBean depentBean) { this.depentBean = depentBean;}private DependentBean depentBean;public Object getObject() throws Exception { return test;}public Class getObjectType() { // TODO Auto-generated method stub return Test.class;}public boolean isSingleton() { // TODO Auto-generated method stub return true;}public void afterPropertiesSet() throws Exception {  System.out.println("name:" + this.name);  test = new Test();  test.name = depentBean.doSomething() + this.name;}}

為了簡化,只寫一個public的變量

public class Test {public String name;}
public class DependentBean {public String doSomething(){ return "hello:";}@Autowiredprivate Test test;}

xml配置

<bean id="test" class="com.alibaba.test.circle.MyFactoryBean"><property name="depentBean"> <bean class="com.alibaba.test.circle.DependentBean"></bean></property><property name="name" value="zlx"></property></bean>

其中工廠Bean MyFactoryBean作用是對Test類的包裝,首先對MyFactoryBean設置屬性,然后在MyFactoryBean的afterPropertiesSet方法中創建一個Test實例,并且設置屬性,實例化MyFactoryBean最終會調用getObject方法返回創建的Test對象。這里MyFactoryBean依賴了DepentBean,而depentBean本身有依賴了Test,所以這是個循環依賴

測試:

public class TestCircle2 {private final static ClassPathXmlApplicationContext moduleContext;private static Test test;static { moduleContext = new ClassPathXmlApplicationContext(new String[]{"beans-circile.xml"}); test = (Test) moduleContext.getBean("test");}public static void main(String[] args) { System.out.println(test.name);}}

結果:

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'com.alibaba.test.circle.DependentBean#1c701a27': Autowiring of fields failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.alibaba.test.circle.Test com.alibaba.test.circle.DependentBean.test; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'test': FactoryBean which is currently in creation returned null from getObject

3.2 分析原因

當實例化test時候會觸發getBean(“test”) ,會看當前bean是否存在

不存在則創建Test 的實例,創建完畢后會把當前bean信息放入singletonFactories單件map里面

然后對該實例進行屬性注入depentBean,屬性注入時候會getBean(“depentBean”) ,

發現depentBean 不存在,就會實例化depentBean,然后放入singletonFactories,

然后進行autowired注入test,然后觸發getBean(“test”);這時候會到(1)getSingleton返回實例化的test。由于test是工廠bean所以返回test.getObject();

而MyFactoryBean的afterPropertiesSet還沒被調用,所以test.getObject()返回null.

下面列下Spring bean創建的流程:

getBean()->創建實例->autowired->set屬性->afterPropertiesSet

也就是調用getObject方法早于afterPropertiesSet方法被調用了。

那么我們修改下MyFactoryBean為如下:

public Object getObject() throws Exception {// TODO Auto-generated method stubif(null == test){ afterPropertiesSet();}return test;}
public void afterPropertiesSet() throws Exception {if(null == test){ System.out.println("name:" + this.name); test = new Test(); test.name = depentBean.doSomething() + this.name;}}

也就是getObject內部先判斷不如test==null那調用下afterPropertiesSet,然后afterPropertiesSet內部如果test==null在創建Test實例,看起來貌似不錯,好想可以解決我們的問題。但是實際上還是不行的,因為afterPropertiesSet內部使用了depentBean,而此時depentBean=null。

3.3 思考如何解決

3.2分析原因是先創建了MyFactoryBean,并在在創建MyFactoryBean的過程中有創建了DepentBean,而創建DepentBean時候需要autowired MyFactoryBean的實例,然后要調用afterPropertiesSet前調用getObject方法所以返回null。

那如果先創建DepentBean,然后在創建MyFactoryBean那?下面分析下過程:

首先會實例化DepentBean,并且加入到singletonFactories

DepentBean實例會autowired Test,所以會先創建Test實例

創建Test實例,然后加入singletonFactories

Test實例會屬性注入DepentBean實例,所以會getBean(“depentBean”);

getBean(“depentBean”) 發現singletonFactories中已經有depentBean了,則返回depentBean對象

因為depentBean不是工廠bean所以直接返回depentBean

Test實例會屬性注入DepentBean實例成功,Test實例初始化OK

DepentBean實例會autowired Test實例OK

按照這分析先創建DepentBean,然后在實例化MyFactoryBean是可行的,修改xml為如下:

<bean id="dependentBean" class="com.alibaba.test.circle.DependentBean"></bean><bean id="test" class="com.alibaba.test.circle.MyFactoryBean"><property name="depentBean"> <ref bean="dependentBean" /> </property><property name="name" value="zlx"></property></bean>

測試運行結果:

name:zlx

hello:zlx

果真可以了,那按照這分析,上面XML配置如果調整了聲明順序,肯定也是會出錯的,因為test創建比dependentBean早,測試下果然如此。另外可想而知工廠bean循環依賴工廠bean時候無論聲明順序如何必然也會失敗。

3.3 一個思考

上面先注入了MyFactoryBean中需要使用的dependentBean,然后注入MyFactoryBean,問題就解決了。那么如果需要在另外一個Bean中使用創建的id=”test”的對象時候,這個Bean該如何注入那?
類似下面的方式,會成功?留給大家思考^^

public class UseTest {@Autowiredprivate Test test;}
<bean id="useTest" class="com.alibaba.test.circle.UseTest"></bean><bean id="dependentBean" class="com.alibaba.test.circle.DependentBean"></bean><bean id="test" class="com.alibaba.test.circle.MyFactoryBean"><property name="depentBean"> <ref bean="dependentBean" /> </property><property name="name" value="zlx"></property></bean>

四、 總結

普通Bean之間相互依賴時候Bean注入順序是沒有關系的,但是工廠Bean與普通Bean相互依賴時候則必須先實例化普通bean,這是因為工廠Bean的特殊性,也就是其有個getObject方法的緣故。

好了,以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對VeVb武林網的支持。


注:相關教程知識閱讀請移步到JAVA教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
亚洲乱码国产乱码精品精| 精品亚洲一区二区三区四区五区| 亚洲电影免费在线观看| 国产亚洲免费的视频看| 欧美日本黄视频| 亚洲97在线观看| 国产在线观看不卡| 欧美重口另类videos人妖| 欧美理论片在线观看| 欧美性猛交xxxx乱大交蜜桃| 日韩有码片在线观看| 中文字幕视频在线免费欧美日韩综合在线看| 亚洲图片欧美日产| 亚洲一区二区三区香蕉| 欧美大肥婆大肥bbbbb| 久久精品国产v日韩v亚洲| 精品成人国产在线观看男人呻吟| 国产精品入口尤物| 欧美电影免费观看网站| 91久久久久久久| 色偷偷偷综合中文字幕;dd| 91精品国产自产在线观看永久| 国产丝袜一区二区三区免费视频| 亚洲第一精品电影| 91沈先生作品| 国语自产精品视频在线看| 欧美亚洲国产视频| 久久久久久久久爱| 欧美大片免费观看在线观看网站推荐| 日韩欧美国产激情| 欧美大片第1页| 国内精品在线一区| 国产精品久久久久久久app| 国产精品福利在线| 中文字幕久久亚洲| 日韩一区二区三区xxxx| 在线精品视频视频中文字幕| 日本一欧美一欧美一亚洲视频| 亚洲视频在线免费看| 亚洲影院高清在线| 精品欧美aⅴ在线网站| 国产精品入口福利| 亚洲人成在线电影| 久久激情视频久久| 精品爽片免费看久久| 亚州国产精品久久久| 中文字幕日本欧美| 亚洲精品日韩欧美| 91精品国产91久久久久久久久| 97精品久久久中文字幕免费| 亚洲午夜未满十八勿入免费观看全集| 国产精品免费久久久久影院| 在线看欧美日韩| 日韩一区二区福利| 日本久久久a级免费| 亚洲激情免费观看| 午夜精品福利视频| 国产成人精品免高潮费视频| 亚洲精品一区中文字幕乱码| 国内精品一区二区三区四区| 欧美日韩精品在线播放| 欧美性少妇18aaaa视频| 国产日韩精品一区二区| 91中文在线视频| 91在线精品视频| 一区二区三区美女xx视频| 亚洲性生活视频在线观看| 综合136福利视频在线| 日韩电影免费观看在线观看| 国产成人jvid在线播放| 狠狠做深爱婷婷久久综合一区| 日本精品久久久久久久| 亚洲欧美制服第一页| 国产色综合天天综合网| 国产精品美乳一区二区免费| 亚洲成人av在线| 日韩一级裸体免费视频| 日韩在线中文视频| 伊人激情综合网| 亚洲欧美日韩一区二区在线| 欧美日韩精品二区| 精品高清一区二区三区| 国产精品久久久久久网站| 国产成人涩涩涩视频在线观看| 成人黄色免费网站在线观看| 国产91精品黑色丝袜高跟鞋| 欧美有码在线观看| 久久久久国产精品一区| 日韩中文字幕免费| 日韩中文在线中文网在线观看| 久久韩国免费视频| 美女国内精品自产拍在线播放| 奇米四色中文综合久久| 国产91在线高潮白浆在线观看| 国产精品毛片a∨一区二区三区|国| 成人激情在线观看| 国产精品久久久久久av下载红粉| 欧美日韩国产色视频| 日韩电影免费在线观看中文字幕| 日韩在线www| 欧美亚洲国产日本| 欧美日韩在线看| 2020欧美日韩在线视频| 91深夜福利视频| 日韩高清电影好看的电视剧电影| 在线播放日韩专区| 一本色道久久88综合日韩精品| 91极品视频在线| 91国内在线视频| 尤物九九久久国产精品的分类| 九九视频直播综合网| 久久综合网hezyo| www.99久久热国产日韩欧美.com| 欧美激情视频免费观看| 一本色道久久综合亚洲精品小说| 最新的欧美黄色| 色哟哟网站入口亚洲精品| 国产精品国产亚洲伊人久久| 黄色成人在线免费| 久久中国妇女中文字幕| 国产精品自产拍在线观看中文| 国产精品久久久久久久av电影| 日韩欧美aaa| 奇米一区二区三区四区久久| 久久精品国产电影| 宅男66日本亚洲欧美视频| 日韩视频在线免费观看| 成人av在线亚洲| 国产精品欧美亚洲777777| 久久综合五月天| 国产精品福利小视频| 91精品视频专区| 好吊成人免视频| 日韩免费观看高清| 日韩中文在线中文网在线观看| 91av视频在线观看| 日韩av在线看| 欧美视频在线视频| 亚洲mm色国产网站| 欧美精品福利在线| 精品国产91乱高清在线观看| 中文字幕v亚洲ⅴv天堂| 最近2019年手机中文字幕| 日韩免费不卡av| 性欧美xxxx交| 91中文在线视频| 欧美有码在线观看视频| 97精品视频在线| 午夜剧场成人观在线视频免费观看| 国产精品嫩草影院一区二区| 国产美女扒开尿口久久久| 91国偷自产一区二区三区的观看方式| 欧美影院久久久| 国产在线播放91| 久久亚洲国产精品成人av秋霞| 欧美一级片在线播放| 国产丝袜视频一区| 国产香蕉精品视频一区二区三区| 亚洲欧美日韩国产精品| 日本高清不卡的在线| 国产精品综合网站| 中文字幕亚洲字幕| 91久久久久久久久久| 国产精品小说在线|