【问题标题】:How many instances created for singleton bean referring to a session bean/prototype bean为引用会话 bean/原型 bean 的单例 bean 创建了多少实例
【发布时间】:2014-09-19 22:41:18
【问题描述】:

我对下面提到的场景中将创建的实例数量有疑问,当使用 Spring Framework 时:

bean配置是这样的

<bean id="a" class="A">
    <property name="b" ref="b"/>
</bean>

<bean id="b" class="B" scope="session"/> or

<bean id="b" class="B" scope="prototype"/>

默认情况下,bean "a" 有singleton scope。所以有一个单例 bean 引用了具有会话范围或原型范围的 bean。

在这种情况下,如果应用程序同时有 2 个请求,那么将创建多少个 A 实例,以及创建多少个 B 实例?

如果有人能解释这是如何工作的,那将是非常有帮助的。

谢谢, 迪维娅

【问题讨论】:

标签: spring instance


【解决方案1】:

单例范围

当 bean 是 singleton 时,只会管理 bean 的一个共享实例,并且所有对 id 或 id 与该 bean 定义匹配的 bean 的请求都将导致 Spring 返回一个特定的 bean 实例容器。

换句话说,当你定义一个bean定义并且它的作用域是singleton时,那么Spring IoC container将由那个bean定义定义create exactly one instance of the object。该单个实例将存储在此类单例 bean 的缓存中,并且对该命名 bean 的所有后续请求和引用都将导致返回缓存的对象。

会话范围

有了上面的 bean 定义,Spring 容器将为 lifetime of a single HTTP Session 创建一个全新的 bean 实例。

根据 Spring 框架参考,如果需要将“lives longer”(在这种情况下为单例 bean)的类注入另一个生命周期相对较短的类(会话-范围的bean)。不过,原型和单例范围的方法不同。

在您的 XML 中,我们想要的是 singletonBean 实例应该只实例化一次,并且应该使用 sessionBean 注入。但是由于sessionBean 是会话范围的(这意味着它应该为每个会话重新实例化),所以配置是不明确的(因为依赖项是在实例化时设置的,并且会话范围的值也可以在以后更改)。

因此,与其注入该类,不如注入一个代理,该代理公开与 sessionBean 完全相同的公共接口。容器将这个代理对象注入到 singletonBean bean 中,它不知道这个 sessionBean 引用是一个代理。它通过在 sessionBean 中写入此标记来指定:

<aop:scoped-proxy/>

XML 配置:

<bean name="singletonBean" class="somepkg.SingletonBean">
<property name="someProperty" ref="sessionBean"/>
</bean>

<bean name="sessionBean" class="somepkg.SessionBean" scope="session">
<aop:scoped-proxy/>
</bean>

singletonBean 实例调用依赖注入的 sessionBean 对象上的方法时,它实际上是在调用代理上的方法。然后代理从(在这种情况下)HTTP Session 中获取真实的 sessionBean 对象,并将方法调用委托给检索到的真实 sessionBean 对象。

更多信息请参考this

具有原型 bean 依赖关系的单例 bean

查找方法注入

当您使用 singleton-scoped bean 和 prototype beans 上的依赖项时,请注意在实例化时解决依赖项。因此,如果您将 prototype-scoped bean 依赖注入到单例 bean 中,则会实例化一个新的原型 bean,然后将依赖注入到单例 bean 中。原型实例是提供给单例范围 bean 的唯一实例。

但是,假设您希望单例范围的 bean 在运行时重复获取原型范围的 bean 的新实例。您不能将原型范围的 bean 依赖注入到单例 bean 中,因为该注入仅发生一次,此时 Spring 容器正在实例化单例 bean 并解析和注入其依赖项。

<!-- a stateful bean deployed as a prototype (non-singleton) -->
<bean id="command" class="fiona.apple.AsyncCommand" scope="prototype">
  <!-- inject dependencies here as required -->
</bean>

<!-- commandProcessor uses statefulCommandHelper -->
<bean id="commandManager" class="fiona.apple.CommandManager">
  <lookup-method name="createCommand" bean="command"/>
</bean>

Lookup method 注入是容器对override methods on container 托管bean 的能力,返回容器中另一个命名bean 的查找结果。 lookup 通常涉及prototype bean,如上一节所述的场景中。 Spring 框架通过使用来自CGLIB library 的字节码生成来动态生成覆盖该方法的子类来实现此方法注入。

请参考lookup method injection

Follow 获取更详细的示例和信息。

【讨论】:

  • 谢谢。现在很清楚了。如果假设类“A”的范围为“原型”而不是“单例”,那么相同的场景将如何?因为原型也会为每个请求创建一个新实例。它与会话范围有何不同?
  • @user1477232 答案也针对原型依赖进行了修改。 Spring为此提供了“查找方法”。
  • @ankur-singhal:首先感谢您的详细解释。我的问题是如果我们尝试使用“user1477232”给出的 bean 配置加载 xml 会发生什么。我们在运行时是否遇到任何异常?
【解决方案2】:

如果我们使用问题中提到的方式,spring IOC 将创建总是返回与单例相同的对象,为了在单例中注入原型 bean,我们有两种方式

1) 查找方法注入

2) 范围代理

查看更多详情here

【讨论】:

    【解决方案3】:

    首先,我不认为它是有效的定义一个bean,同时具有会话和原型范围,具有相同的bean id。

    为引用原型 bean 的单例 bean 创建了多少个实例?

    在你的情况下:一个

    一般情况下:取决于您访问 bean 的方式:

    一个

    @Component
    class MySingletonBean{
        @Autowired
        MyPrototypeBean b;
    }
    

    两个

    @Component
    class MySingletonBean{
        @Autowired
        MyPrototypeBean b;
    
        @Autowired
        MyPrototypeBean bSecondInstance;
    
    }
    

    或更多

    @Component
    class MySingletonBean{
        @Autowired
        javax.inject.Provider<MyPrototypeBean> providerOfB;
    
        void accessMultipleInstances(){
           MyPrototypeBean bInstance1 = providerOfB.get();
           MyPrototypeBean bInstance2 = providerOfB.get();
           MyPrototypeBean bInstance3 = providerOfB.get();
           //.....
    
        }
    
    }
    

    注意: MyPrototypeBean 被认为已被标记为:@Scope(scopeName = ConfigurableBeanFactory.SCOPE_PROTOTYPE)。如果省略它,那么在上述所有情况下,您将引用相同的单例实例。

    关于会话范围的 bean:

    每个会话一个。

    根据this answer,spring 会根据会话自动创建一个代理,针对不同的实例。

    这意味着在上述所有情况下,您都可以在同一会话中访问同一实例。

    关于提供的xml配置:

    对我来说,这样的事情会更有意义:

    <bean id="a" class="A">
        <property name="b" ref="b"/>
        <property name="b2" ref="b2"/>
    </bean>
    <bean id="b" class="B" scope="session"/> or
    <bean id="b2" class="B" scope="prototype"/>
    

    在这种情况下,您将在每个会话中获得一个 b 实例和一个且唯一的 b2 实例,因为您从单例中使用它并且您不使用提供程序或某些 similar pattern

    【讨论】:

      猜你喜欢
      • 2015-01-14
      • 1970-01-01
      • 2015-07-23
      • 1970-01-01
      • 1970-01-01
      • 2011-01-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多