java Spring创建单例的多个实例?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/11547240/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Spring creating multiple instances of a singleton?
提问by mindas
I have a graph of Spring beans which autowire each other. Heavily simplified illustration:
我有一个相互自动装配的 Spring bean 的图表。高度简化的插图:
<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;
}
All of these beans don't have scope specified which imply they are singletons (making them explicit singletons doesn't change anything, I've tried).
所有这些 bean 都没有指定范围,这意味着它们是单例(使它们显式单例不会改变任何东西,我试过)。
The problem is that after the instantiation of a single application context, instances of Bar
and Baz
contain differentinstances of Foo
. How could this happen?
问题是在实例化单个应用程序上下文之后,Bar
和 的实例Baz
包含不同的Foo
. 这怎么会发生?
I have tried to create public no args constructor for Foo
and debugging has confirmed Foo
is created more than once. The stack trace for all of these creations is here.
我试图创建 public no args 构造函数,Foo
并且调试已经确认Foo
创建了不止一次。所有这些创作的堆栈跟踪在这里。
I have also tried to enable debug logging for Spring, and among all other lines, got the following:
我还尝试为 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'
I understand that my beans are cross-referencing each other, but I would expect Spring framework to respect singleton scope and initialize a singleton bean once, and then autowire it to whoever wants it.
我知道我的 bean 是相互交叉引用的,但我希望 Spring 框架尊重单例范围并初始化一个单例 bean,然后将它自动装配给任何想要它的人。
The interesting fact that if I use old school private
constructor with public static Foo getInstance
accessor, this works just fine - no exceptions are thrown during the context setup.
有趣的事实是,如果我使用private
带public static Foo getInstance
访问器的老式构造函数,这工作得很好 - 在上下文设置期间不会抛出异常。
FWIW, I am using Spring version 3.0.5 (also tried with 3.1.2, same results) with o.s.c.s.ClassPathXmlApplicationContext(String ...configLocations)
constructor.
FWIW,我正在使用 Spring 版本 3.0.5(也尝试使用 3.1.2,结果相同)和o.s.c.s.ClassPathXmlApplicationContext(String ...configLocations)
构造函数。
I can easily convert my code to use static initializer but I want to understand why would Spring behave this way. Is this a bug?
我可以轻松地将我的代码转换为使用静态初始值设定项,但我想了解为什么 Spring 会这样做。这是一个错误吗?
EDIT:Some additional investigation showed that
编辑:一些额外的调查表明
- After the application context is initialized, all subsequent requests to
context.getBean(Foo.class)
alwaysreturn the same instance ofFoo
. - Replacing
@Autowired
with setters (about 20 usages of this bean) still results multiple constructions of this object, but all dependencies are injected with the samereference.
- 应用程序上下文初始化后,所有后续请求
context.getBean(Foo.class)
总是返回相同的Foo
. @Autowired
用 setter替换(这个 bean 的大约 20 次使用)仍然会导致这个对象的多次构造,但所有依赖项都注入了相同的引用。
To me above suggests that this is a Spring bug pertaining to @Autowired
implementation. I am going to post to Spring community forums and post back here if I manage to obtain anything useful.
对我来说,上面表明这是一个与@Autowired
实现有关的 Spring 错误。如果我设法获得任何有用的信息,我将在 Spring 社区论坛上发帖并回帖。
采纳答案by rootkit
Child context(s) can reinstantiate the same singleton beans if you are not careful with context:component-scan annotations (there are other Spring context scan annotations as well such as MVC ones and others). This is a common problem when using Spring servlets in web applications, see Why DispatcherServlet creates another application context?
如果您不小心使用 context:component-scan 注释(还有其他 Spring 上下文扫描注释,例如 MVC 注释和其他注释),子上下文可以重新实例化相同的单例 bean。这是在 Web 应用程序中使用 Spring servlet 时的常见问题,请参阅为什么 DispatcherServlet 创建另一个应用程序上下文?
Make sure you are not re-scanning your components in child contexts, or you are scanning only specific packages/annotations and excluding said packages/annotations from root context component scan.
确保您没有重新扫描子上下文中的组件,或者您只扫描特定的包/注释并从根上下文组件扫描中排除所述包/注释。
回答by Andy
For some reason we are getting this popping up randomly in integration tests and services as well (spring version 4.1.4, java 1.8).
出于某种原因,我们也会在集成测试和服务中随机弹出它(spring 版本 4.1.4,java 1.8)。
Looks like there might be more than one culprit - Autowiring appeared to be causing this at first.
看起来可能不止一个罪魁祸首 - 一开始似乎是自动装配导致了这种情况。
However, we have resolved the most consistent failures by ensuring we give each impacted bean an 'id' field.
但是,通过确保为每个受影响的 bean 提供一个“id”字段,我们已经解决了最一致的故障。
回答by Giri
Try using setter injection instead of constructor way and see if it works.In the spring bean xml specify Bean A ref to Bean B and vice versa.
尝试使用 setter 注入而不是构造函数方式,看看它是否有效。在 spring bean xml 中指定 Bean A 引用到 Bean B,反之亦然。
回答by Michal Pasinski
My Spring configuration was like follows:
我的 Spring 配置如下:
<context:annotation-config/>
<bean class="Bar" />
<bean class="Foo" />
<bean class="Baz" />
Classes are identical to yours
课程与您的相同
Test app like follows:
测试应用程序如下:
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());
}
}
Output from test app like follows:
测试应用程序的输出如下:
true
true
true
Baz@8aef2b
Baz@8aef2b
Bar@215bf054
Bar@215bf054
Using 3.0.6 it works perfectly fine (singleton beans are indeed singletons). There might be something else you did not illustrate here messing up your configuration. Of course, as a side note, using default package may cause some misterious magic to happen ;-)
使用 3.0.6 它工作得很好(单例 bean 确实是单例)。可能还有其他一些你没有在这里说明的东西弄乱了你的配置。当然,作为旁注,使用默认包可能会导致一些神秘的魔法发生;-)