欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Bean的作用域详解

程序员文章站 2022-07-12 11:36:44
...

在典型的电子商务应用中,可能会有一个bean 代表用户的购物车。如果购物车是单例的话,那么将会导致所有的用户都会向同一个购物车中添加商品。另一方面,如果购物车是原型作用域的,那么在应用中某一个地方往购物车中添加商品,在应用的另外一个地方可能就不可用了,因为在这里注入的是另外一个原型作用域的购物车。就购物车bean 来说,会话作用域是最为合适的,因为它与给定的用户关联性最大。要指定会话作用域,我们可以使用@Scope注解,它的使用方式与指定原型作用域是相同的:

@Component
@Scope(
   value=WebApplicationContext.SCOPE_SESSION,
   proxyMode=ScopedProxyMode.INTERFACES)
public class ShoppingCart {

}

这里,我们将value设置成了WebApplicationContext中的SCOPE_SESSION常量(它的值是session)。这会告诉Spring为Web应用中的每个会话创建一个ShoppingCart。这会创建多个ShoppingCart bean 的实例,但是对于给定的会话只会创建一个实例,在当前会话相关的操作中,这个bean 实际上相当于单例的。要注意的是,@Scope同时还有一个proxyMode属性,它被设置成了ScopedProxyMode.INTERFACES。这个属性解决了将会话或请求作用域的bean 注入到单例bean 中所遇到的问题。在描述proxyMode属性之前,我们先来看一下proxyMode所解决问题的场景。假设我们要将ShoppingCart bean 注入到单例StoreService bean 的Set t er方法中,如下所示:

@Component
public class StoreService {
    @Autowired
    public void setShoppingCart(ShoppingCart shoppingCart){
        this.shoppingCart = shoppingCart;
    }
}

因为StoreService是一个单例的bean ,会在Spring应用上下文加载的时候创建。当它创建的时候,Spring会试图将ShoppingCart bean注入到setShoppingCart()方法中。但是ShoppingCart bean 是会话作用域的,此时并不存在。直到某个用户进入系统,创建了会话之后,才会出现ShoppingCart实例。

另外,系统中将会有多个ShoppingCart实例:每个用户一个。我们并不想让Spri n g注入某个固定的ShoppingCart实例到StoreService中。我们希望的是当StoreService处理购物车功能时,它所使用的ShoppingCart实例恰好是当前会话所对应的那一个。

Spring并不会将实际的ShoppingCart bean 注入到St oreServ i ce中,Spri n g会注入一个到ShoppingCart bean 的代理,如图3. 1所示。这个代理会暴露与ShoppingCart相同的方法,所以StoreService会认为它就是一个购物车。但是,当StoreService调
用ShoppingCart的方法时,代理会对其进行懒解析并将调用委托给会话作用域内真正的ShoppingCart bean 。

现在,我们带着对这个作用域的理解,讨论一下proxyMode属性。如配置所示,proxyMode属性被设置成了ScopedProxyMode.INTERFACES,这表明这个代理要实现ShoppingCart接口,并将调用委托给实现bean。

如果ShoppingCart是接口而不是类的话,这是可以的(也是最为理想的代理模式)。但如果ShoppingCart是一个具体的类的话,Spring就没有办法创建基于接口的代理了。此时,它必须使用CGLi b来生成基于类的代理。所以,如果bean 类型是具体类的话,我们必须要将proxyMode属性设置为ScopedProxyMode.TARGET_CLASS,以此来表明要以生成目标类扩展的方式创建代理。

尽管我主要关注了会话作用域,但是请求作用域的bean 会面临相同的装配问题。因此,请求作用域的bean 应该也以作用域代理的方式进行注入。

Bean的作用域详解

xml方式

如果你需要使用XML来声明会话或请求作用域的bean ,那么就不能使用@Scope注解及其proxyMode属性了。元素的scope属性能够设置bean 的作用域,但是该怎样指定代理模式呢?

要设置代理模式,我们需要使用Spring aop命名空间的一个新元素:
Bean的作用域详解

aop:scoped-proxy>是与@Scope注解的proxyMode属性功能相同的Spring XML配置元素。它会告诉Spring为bean 创建一个作用域代理。默认情况下,它会使用CGLi b创建目标类的代理。但是我们也可以将proxy-target-class属性设置为false,进而要求它生成基于接口的代理:
Bean的作用域详解

为了使用元素,我们必须在XML配置中声明Spring的aop命名空间:
Bean的作用域详解

相关标签: bean