2012-07-18 198 views
15

我有一個相互自動裝配的Spring bean的圖形。重簡化圖解:Spring創建單例的多個實例?

<context:annotation-config/> 
<bean class="Foo"/> 
<bean class="Bar"/> 
<bean class="Baz"/> 

... 

public class Foo { 
    @Autowired Bar bar; 
    @Autowired Baz baz; 
} 

public class Bar { 
    @Autowired Foo foo; 
} 

public class Baz { 
    @Autowired Foo foo; 
} 

所有這些豆子不具備規定範圍這意味着他們是單身(使他們明確單身人士不會改變任何東西,我試過)。

的問題是,一單個應用上下文的實例化後,的BarBaz實例包含的Foo不同實例。這怎麼會發生?

我試圖創建Foo的公共無參數構造函數,調試已確認Foo被多次創建。所有這些作品的堆棧跟蹤爲here

我也曾嘗試啓用調試日誌記錄春天,和所有其他線路中,得到如下:

DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'Foo' 
DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'Foo' 
DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'Foo' 

我明白我的豆交叉引用對方,但我希望Spring框架尊重單例作用域並初始化單例bean一次,然後將其自動裝入任何想要它的人。

一個有趣的事實是,如果我使用舊學校private構造函數與public static Foo getInstance訪問器,這工作得很好 - 在上下文設置過程中不會引發異常。

FWIW,我正在使用Spring 3.0.5版本(也嘗試使用3.1.2,結果相同)和o.s.c.s.ClassPathXmlApplicationContext(String ...configLocations)的構造函數。

我可以很容易地將我的代碼轉換爲使用靜態初始化,但我想明白爲什麼春天會這樣。這是一個錯誤?

編輯:一些額外的調查顯示,應用程序上下文初始化

  • 後,以context.getBean(Foo.class)所有後續請求始終返回Foo相同的實例。
  • 用setter替代@Autowired(約20個這個bean的用法)仍然導致這個對象的多重構造,但是所有的依賴關係都被注入了相同的引用。

對我而言以上表明這是一個與@Autowired實現有關的Spring bug。如果我設法獲取任何有用的信息,我將發佈到Spring社區論壇併發布回來。

+0

這可能很明顯,但只有1個JVM在使用嗎?循環依賴關係? – 2012-07-18 17:49:28

+0

是的,這只是一個JVM。循環依賴 - 是的,但我相信我在我的帖子中解釋了這一點。 – mindas 2012-07-18 17:50:01

+0

我明白了,但如果你有例如構造函數注入會發生什麼? Spring如何解決這個問題? – 2012-07-18 17:51:13

回答

11

如果您不小心上下文,子上下文(s)可以重新實例化相同的單例bean:component-scan annotations(there是其他Spring上下文掃描註釋以及MVC和其他)。當使用Web應用程序春天的servlet,見Why DispatcherServlet creates another application context?

確保您不會重新掃描您的組件在孩子的上下文,或者你只掃描特定的軟件包/註釋,不包括從根說包/註釋這是一個常見的問題上下文組件掃描。

+0

這些單例是否會加載相同的類加載器? – gstackoverflow 2017-01-17 15:21:47

+0

你可以提供一個簡單的例子,其中春天的單身人士會加載兩次? – gstackoverflow 2017-01-18 06:51:11

0

嘗試使用setter注入,而不是構造函數的方式,看看它是否工作。在spring bean xml中指定Bean A ref,反之亦然。

+0

我已更新我的帖子。只是重申一下 - 我知道如何解決這個問題,但更重要的是,我正試圖理解**爲什麼會發生這種情況。 – mindas 2012-07-19 10:26:39

0

我的Spring配置就像如下:

<context:annotation-config/> 

<bean class="Bar" /> 
<bean class="Foo" /> 
<bean class="Baz" /> 

類是相同的你

測試應用程式,例如如下:

import org.springframework.context.ApplicationContext; 
import org.springframework.context.support.ClassPathXmlApplicationContext; 

public class SpringTest { 

    /** 
    * @param args 
    */ 
    public static void main(String[] args) { 
     ApplicationContext ctx = new ClassPathXmlApplicationContext("META-INF/spring/testctx.xml"); 

     Foo foo = ctx.getBean(Foo.class); 
     Baz baz = ctx.getBean(Baz.class); 
     Bar bar = ctx.getBean(Bar.class); 

     System.out.println(foo.equals(baz.foo)); 
     System.out.println(foo.equals(bar.foo)); 
     System.out.println(baz.equals(foo.baz)); 

     System.out.println(foo.baz.toString()); 
     System.out.println(baz.toString()); 
     System.out.println(foo.bar.toString()); 
     System.out.println(bar.toString()); 

    } 

} 

從測試APP輸出一樣如下:

true 
true 
true 
[email protected] 
[email protected] 
[email protected] 
[email protected] 

使用3.0.6它工作得很好(單身豆實際上是單身人士)。這裏可能還有其他的東西沒有說明你的配置。當然,作爲一個方面說明,使用默認包可能會導致一些神奇的魔法發生;-)

+0

感謝您付出努力。在我的情況下,它是更復雜的圖形對象,其中數百個。由於顯而易見的原因,我無法在這裏發佈所有這些內容,只是爲了說明這一點而削減最小的場景。 – mindas 2012-11-29 09:24:38

+0

@mindas您是否嘗試重新排序文件中的bean定義?嘗試將Foo放在第二個或最後一個地方。 – partlov 2012-12-28 08:13:34

1

出於某種原因,我們在集成測試和服務中也隨機彈出這個彈出窗口(spring version 4.1.4,java 1.8)。

看起來可能有多個罪魁禍首 - 自動裝配似乎首先造成這種情況。

但是,我們通過確保給每個受影響的bean一個'id'字段來解決最一致的故障。