Spring创建单例的多个实例?

时间:2012-07-18 17:42:13

标签: java spring singleton autowired applicationcontext

我有一个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;
}

所有这些bean都没有指定范围,暗示它们是单身人士(让他们明确的单身人士没有改变任何东西,我已经尝试过了。)

问题在于,在实例化单个应用程序上下文之后,BarBaz的实例包含<{>不同的实例{{1 }}。怎么会发生这种情况?

我尝试为Foo创建public no args构造函数,并且已经确认调试已经确认Foo不止一次。所有这些创作的堆栈跟踪都是here

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

Foo

我理解我的bean是相互交叉引用的,但是我希望Spring框架能够尊重单例范围并初始化一个单例bean,然后将它自动装配给任何想要它的人。

有趣的事实是,如果我使用带有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' 访问器的旧学校private构造函数,这很好用 - 在上下文设置期间不会抛出任何异常。

FWIW,我正在使用Spring版本3.0.5(也尝试使用3.1.2,结果相同)和public static Foo getInstance构造函数。

我可以轻松地将我的代码转换为使用静态初始化程序,但我想了解为什么Spring会以这种方式运行。这是一个错误吗?

编辑:一些额外的调查表明

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

对我来说,这表明这是一个与@Autowired实现有关的Spring bug。我将发布到Spring社区论坛,如果我设法获得任何有用的东西,请回到这里。

4 个答案:

答案 0 :(得分:12)

如果您不小心上下文,子上下文可以重新实例化相同的单例bean:组件扫描注释(还有其他Spring上下文扫描注释,例如MVC和其他注释)。在Web应用程序中使用Spring servlet时,这是一个常见问题,请参阅Why DispatcherServlet creates another application context?

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

答案 1 :(得分:1)

出于某种原因,我们也会在集成测试和服务中随机弹出这个(Spring version 4.1.4,java 1.8)。

看起来可能有不止一个罪魁祸首 - 自动装配似乎最初导致这种情况。

但是,我们通过确保为每个受影响的bean提供“id”字段来解决最一致的失败。

答案 2 :(得分:0)

尝试使用setter注入而不是构造方式,看它是否有效。在spring bean中,xml指定Bean A ref为Bean B,反之亦然。

答案 3 :(得分:0)

我的Spring配置如下:

<context:annotation-config/>

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

课程与您的课程相同

测试app如下:

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());

    }

}

测试应用程序的输出如下:

true
true
true
Baz@8aef2b
Baz@8aef2b
Bar@215bf054
Bar@215bf054

使用3.0.6它完全正常(单身豆确实是单身)。可能还有其他一些你没有在这里说明搞乱你的配置。当然,作为旁注,使用默认包可能会导致一些神秘的魔法发生; - )

相关问题