Bean的作用域详解

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/NameGGG/article/details/78034157

在典型的电子商务应用中,可能会有一个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 应该也以作用域代理的方式进行注入。

这里写图片描述

xml方式

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

要设置代理模式,我们需要使用Spring aop命名空间的一个新元素:
这里写图片描述

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

为了使用元素,我们必须在XML配置中声明Spring的aop命名空间:
这里写图片描述

猜你喜欢

转载自blog.csdn.net/NameGGG/article/details/78034157