激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|JAVA教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|JavaScript|易語言|

服務器之家 - 編程語言 - JAVA教程 - Spring循環依賴正確性及Bean注入的順序關系詳解

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

2021-03-16 13:06George JAVA教程

這篇文章主要給大家介紹了關于Spring循環依賴的正確性,以及Bean注入的順序關系的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧。

一、前言

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

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

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

2.1 循環依賴例子與原理

?
1
2
3
4
5
6
7
8
9
public class BeanA {
private BeanB beanB;
public BeanB getBeanB() {
 return beanB;
}
public void setBeanB(BeanB beanB) {
 this.beanB = beanB;
}
}
?
1
2
3
4
5
6
7
8
9
public class BeanB {
private BeanA beanA;
public BeanA getBeanA() {
 return beanA;
}
public void setBeanA(BeanA beanA) {
 this.beanA = beanA;
}
}
?
1
2
3
4
5
<bean id="beanA" class="com.alibaba.test.circle.BeanA">
<property name="beanB">
 <ref bean="beanB" />
</property>
</bean>
?
1
2
3
4
5
<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)

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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);
}
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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)

?
1
2
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
?
1
2
3
4
5
6
7
8
9
10
11
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);
 }
});
}
?
1
2
3
4
5
6
7
8
9
10
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 允許循環依賴的開關

?
1
2
3
4
5
6
7
8
9
10
11
12
13
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后發現循環依賴還是可以正常運行,翻看源碼:

?
1
2
3
public ClassPathXmlApplicationContext(String[] configLocations) throws BeansException {
this(configLocations, true, null);
}
?
1
2
3
4
5
6
7
8
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
 throws BeansException {
super(parent);
setConfigLocations(configLocations);
if (refresh) {
 refresh();
}
}
?
1
2
3
4
5
6
7
8
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
 throws BeansException {
super(parent);
setConfigLocations(configLocations);
if (refresh) {
 refresh();
}
}

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

refresh方法會調用refreshBeanFactory:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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);
}
}
?
1
2
3
4
5
6
7
8
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了,所以測試代碼改為:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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的變量

?
1
2
3
public class Test {
public String name;
}
?
1
2
3
4
5
6
7
public class DependentBean {
public String doSomething(){
 return "hello:";
}
@Autowired
private Test test;
}

xml配置

?
1
2
3
4
5
6
<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,所以這是個循環依賴

測試:

?
1
2
3
4
5
6
7
8
9
10
11
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為如下:

?
1
2
3
4
5
6
7
public Object getObject() throws Exception {
// TODO Auto-generated method stub
if(null == test){
 afterPropertiesSet();
}
return test;
}
?
1
2
3
4
5
6
7
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為如下:

?
1
2
3
4
5
6
7
<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該如何注入那?
類似下面的方式,會成功?留給大家思考^^

?
1
2
3
4
public class UseTest {
@Autowired
private Test test;
}
?
1
2
3
4
5
6
7
8
<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方法的緣故。

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

原文鏈接:https://segmentfault.com/a/1190000012738048

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 国产精品观看在线亚洲人成网 | 国产女同疯狂激烈互摸 | 国产一区二区三区在线免费观看 | 港台三级在线观看 | 日韩免费黄色 | 毛片免| 欧美片a| 99精品热视频 | 最新se94se在线欧美 | 九九热精品免费视频 | 羞羞视频免费网站 | 日韩av电影在线免费观看 | 在线中文日韩 | 国产在线一区二区三区 | 日韩一级毛毛片 | 污黄视频在线观看 | 久久国产精 | 国产亲子伦在线观看 | 国产精品免费一区二区三区在线观看 | 亚洲精品永久视频 | 男人的天堂视频网站 | www.com香蕉 | 成人乱人乱一区二区三区 | 亚洲视频观看 | 亚洲成人在线免费观看 | 嗯~啊~弄嗯~啊h高潮视频 | 4p嗯啊巨肉寝室调教男男视频 | 国产亚洲精品久久久久久久 | 成人国产在线视频 | 欧美激情视频一区二区免费 | 九九热在线视频观看 | 成人黄色在线观看 | 亚洲最新无码中文字幕久久 | 依依成人精品视频 | 欧美日韩大片在线观看 | 宅男噜噜噜66国产免费观看 | 欧美人与牲禽动交精品一区 | 黄色电影免费网址 | 成人一级视频 | 欧美日韩中文字幕在线视频 | 91精品最新国内在线播放 |