单例Bean引用会话Bean/原型Bean创建了多少个实例?

33

当使用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作用域。因此,存在一个单例bean,其引用了一个具有session作用域或prototype作用域的bean。

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

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

谢谢, Divya


1
阅读文档 http://docs.spring.io/spring/docs/3.0.0.M3/reference/html/ch04s04.html#beans-factory-scopes-global-session - Bart
回答你的问题。这完全取决于会话的数量。 - Bart
3个回答

48

单例模式

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

换句话说,当您定义一个bean定义并将其作用域设置为singleton时,Spring IoC容器将创建该bean定义定义的对象的唯一实例。这个单一实例将存储在这样的单例bean缓存中,并且所有后续请求和引用该命名bean都将导致返回缓存对象。

会话模式

如上所述的bean定义放置在适当的位置后,Spring容器将创建一个全新的bean实例,仅在单个HTTP会话的生命周期中

根据Spring框架参考文档,在类的“寿命更长”(在本例中为singleton bean)需要注入具有相对较短寿命(session-scoped bean)的另一个类的情况下,需要采用不同的方法。对于原型和单例范围,方法是不同的。

在XML中,我们希望singletonBean实例仅被实例化一次,并使用sessionBean进行注入。但是,由于sessionBean是会话范围的(这意味着它应该在每个会话中重新实例化),因此配置是有歧义的(因为依赖项在实例化时设置,而会话作用域的值可以稍后更改)。

因此,不是使用该类进行注入,而是使用公开与sessionBean完全相同的公共接口的代理进行注入。容器将此代理对象注入singletonBean bean中,该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>
当一个单例bean实例调用依赖注入的sessionBean对象上的方法时,它实际上是在代理上调用方法。然后代理从(在本例中)HTTP会话中获取真实的sessionBean对象,并将方法调用委派到检索到的真实sessionBean对象。
此外,请参阅此处以获取更多信息。
具有原型bean依赖项的单例bean
查找方法注入
当您使用依赖于原型bean的单例作用域bean时,请注意依赖关系在实例化时间解析。因此,如果您将一个prototype bean作为依赖注入到singleton作用域bean中,则会实例化一个新的prototype bean,然后将其依赖注入到singleton bean中。该原型实例是唯一提供给singleton作用域bean的实例。
但是,假设您希望singleton作用域bean在运行时重复获取原型作用域bean的新实例。您不能将原型作用域bean注入到singleton bean中,因为这个注入只发生一次,当Spring容器实例化singleton 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注入是容器覆盖容器管理的Bean上的方法,以返回容器中另一个命名Bean的查找结果的能力。在前面的部分所述的场景中,lookup通常涉及原型Bean。Spring框架通过使用来自CGLIB库的字节码生成动态生成子类来实现此方法注入,从而重写方法。

请参阅查找方法注入

单击此处获取更详细的示例和信息。


1
谢谢。现在清楚了。如果假设类“A”的范围为“Prototype”,那么与“Singleton”的情况相比,情况会如何?因为原型模式也会为每个请求创建一个新实例。它与会话范围有什么不同? - user1477232
@user1477232 答案已修改以解决原型依赖问题。Spring提供了“查找方法”来解决此问题。 - Ankur Singhal
@ankur-singhal:首先感谢您的详细解释。我的问题是,如果我们尝试使用“user1477232”提供的bean配置加载xml,会发生什么情况?我们在运行时会得到任何异常吗? - Lathy

3
如果我们按照问题中提到的方式使用spring IOC,它将始终创建相同的对象作为单例。为了在单例中注入原型bean,我们有两种方法:
1)查找方法注入
2)范围代理
更多详细信息请参见此处

0
首先,我认为同时使用相同的bean id定义具有会话和原型范围的bean是无效的。
单例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实例。


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