春天创造了一个单例的多个实例?

我有一个弹簧豆的图表,它们相互自动连接。高度简化的插图:

<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;
}

所有这些bean都没有指定范围,这意味着它们是单例(使它们成为显式单例不会改变任何东西,我已经尝试过)。

问题在于,在实例化单个应用程序上下文之后,包含 的不同实例。怎么会这样?BarBazFoo

我尝试为创建公共无参数构造函数,并且调试已确认已多次创建。所有这些创建的堆栈跟踪都在这里FooFoo

我还尝试为Spring启用调试日志记录,并且在所有其他行中,得到了以下内容:

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'

我知道我的bean是相互交叉引用的,但是我希望Spring框架尊重单例范围并初始化一次单例bean,然后将其自动连接到任何想要它的人。

有趣的是,如果我使用带有访问器的旧学校构造函数,这工作得很好 - 在上下文设置过程中不会抛出任何异常。privatepublic static Foo getInstance

FWIW,我正在使用带有构造函数的Spring版本3.0.5(也尝试使用3.1.2,结果相同)。o.s.c.s.ClassPathXmlApplicationContext(String ...configLocations)

我可以很容易地将我的代码转换为使用静态初始值设定项,但我想了解为什么Spring会以这种方式运行。这是一个错误吗?

编辑:一些额外的调查显示,

  • 初始化应用程序上下文后,所有后续请求始终返回相同的 实例。context.getBean(Foo.class)Foo
  • 用 setter 替换(此 bean 的大约 20 个用法)仍然会导致此对象的多个构造,但所有依赖项都注入了相同的引用。@Autowired

对我来说,上面的建议是一个与实现有关的Spring错误。我将发布到春季社区论坛,如果我设法获得任何有用的东西,请在此处发布。@Autowired


答案 1

如果您不小心上下文:组件扫描注释(还有其他Spring上下文扫描注释以及MVC等),子上下文可以重新实例相同的单例bean。这是在 Web 应用程序中使用 Spring servlet 时的常见问题,请参阅为什么 DispatcherServlet 会创建另一个应用程序上下文?

确保不要在子上下文中重新扫描组件,或者仅扫描特定的包/注释,并从根上下文组件扫描中排除所述包/注释。


答案 2

出于某种原因,我们在集成测试和服务中也随机弹出了这个问题(春季版本4.1.4,java 1.8)。

看起来可能有多个罪魁祸首 - 自动布线起初似乎是造成这种情况的原因。

但是,我们通过确保为每个受影响的 Bean 指定一个“id”字段,解决了最一致的故障。


推荐