2012-07-18 197 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'字段来解决最一致的故障。