Spring单例被多次创建

7

我在我的Spring Web应用程序中定义了一个bean,我希望只有一个实例,这是我的bean定义:

<bean id="accessControl" class="my.spring.app.AccessControl" />

在AccessControl的构造函数中,我为对象分配了一个标识符,类似于这样:
public class AccessControl {
   private long id = 0;
   public AccessControl() {
        id = System.currentTimeMillis();
   }

   public long getAccessControlId() {
        return id;
   }
}

在另一个类中,我尝试获取AccessControl实例,就像这样:

            ApplicationContext ctx =
                     new ClassPathXmlApplicationContext("acbean.xml");

            AccessControl ac = (AccessControl) ctx.getBean("accessControl");
            LOGGER_.info("AccessControl Identifier : " + ac.getAccessControlId());

我希望"id"的值是相同的,因为"id"的值是在构造函数中设置的,而构造函数不应该一遍又一遍地被调用,但事实却是这样。我甚至在构造函数中添加了一个日志语句,每次都会创建一个新的对象。
我已经阅读了这篇文章:http://www.digizenstudio.com/blog/2006/09/14/a-spring-singleton-is-not-a-singleton/,但我认为我没有处理两个具有不同bean标识符的相同类,并且应用程序上下文是相同的。
有人能分享一下我定义bean的方式有什么问题吗?
我也尝试过将singleton ="true"和scope ="singleton",但它们没有任何区别。
谢谢。

每次调用 getBean() 时,构造函数会被调用一次还是两次? - Tomasz Nurkiewicz
很遗憾,我不知道这个问题的答案。根据文档,它应该再次给我相同的对象,也就是说在第一次之后不应该再调用构造函数。根据我所看到的情况,每次调用"getBean(...)"时构造函数都会被调用,这与文档所述完全相反。 - user305210
这段代码是在一个main方法中,你只是一遍又一遍地运行它吗?如果是这样的话,那么每次你运行时都会得到不同的值,因为每次运行时都会创建一个新的JVM。对象本身不能在JVM实例之外自行持久化,这是你需要自己完成的(可能需要使用数据库)。 - nicholas.hauschild
这是一个网络应用程序,只要应用程序没有重新启动,新的JVM实例就不会发挥作用。 - user305210
3个回答

20

在Spring框架中,单例模式是基于应用程序上下文实现的。每次创建应用程序上下文的新实例(例如第二个代码示例中的第一行),所有单例都会被实例化。

您需要拥有一个单一的应用程序上下文,并在应用程序中重复使用它。


1
是的,那就是错误所在。我还没有尝试过你的解决方案,但我百分之百确定这就是问题所在。谢谢。 - user305210

7
您每次调用以下方法时都会创建一个新的应用程序上下文:
ApplicationContext ctx = new ClassPathXmlApplicationContext("acbean.xml");

因此,您最终获得了一个新的Spring容器,这意味着您的bean都将由新容器重新创建。

另外,您提到这是一个Web应用程序。如果是这样,您需要允许Web应用程序加载Spring上下文,并在必要时获取和使用该上下文。

请在web.xml中添加:

<context-param>
    <description>Core Spring context.</description>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/classes/applicationContext.xml</param-value>
</context-param>
<listener>
    <description>Spring loader.</description>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

或类似的内容。根据需要通过servletcontext获取web上下文。

另外需要注意的一点是,Spring 的一个重要目标是提供控制反转(Inversion of Control, IoC),通常与依赖注入(Dependency Injection, DI)一起使用。您应该考虑允许 Spring 为您注入任何依赖项,而不是自己获取上下文并拉取 Bean。


谢谢您。我之前不知道这种方法,但我自己也在寻找类似的东西。 - user305210

3
在Spring应用中,您不应该显式地创建自己的应用程序上下文。
理想情况下,单例应该被注入到您的类中,或者您应该实现ApplicationContextAware(文档一些笔记)。我更喜欢注入; 更容易。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接