Bean的作用域


默认情况下,Spring应用上下文中所有bean都是作为以单例(singleton)的形式创建的。

  • 单例(Singleton): 在整个应用中,只创建bean的一个实例
  • 原型(Prototype):每次注入或者通过Spring应用上下文获取的时候,都会创建一个新的bean实例
  • 会话(Session):在Web应用中,为每个会话创建一个bean实例
  • 请求(Request):在Web应用中,为每个请求创建一个bean实例

推荐使用方式

@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class Notepad(.....)

当然使用
@Scope("prototype")也是可以的,但是推荐使用常量

xml配置
<bean id = "notepad" class = "com.lyc.Notepad" scope="prototype"/>

使用会话和请求作用域

   在web应用中,single和prototype不太适合使用,相反 session和required的会比较合适。例如,在典型的电子商务应用中,可能会有一个bean代表用户的购物车。如果购物车是单例的话,那么将会导致所有的用户都会向同一个购物车中添加商品。另一方面,如果购物车是原型作用域(prototype),那么在应用中某一个地方往购物车中添加商品,在应用的另外一个地方可能就不可用了,因为在这里注入的是另外一个原型作用域的购物车。

      就购物车bean来说,会话作用域是最合适的,因为它与给定的用户关联性最大。要指定会话的作用域,我们可以使用@scope注解,它的使用方式与指定原型作用域是相同的

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

  这里,我们将value设置为WebApplicationContext中的SCOPE_SESSION常量(它的值是session)。这会告诉spring为web应用中的每个会话创建一个ShoppingCart。这会创建多个ShoppingCart bean实例,但是对于给定的会话只会创建一个实例,在当前会话操作中,这个bean实际上相当于单例

  注意: @Scope同时还有一个ProxyMode,他被设置成了ScopedProxyMode.INTERFACES.这个属性解决了将会话或请求作用域的bean注入到单例bean中所遇到的问题。

  假设我们要将ShoppingCart bean注入到单例StoreService bean 的setter方法中,如下所示:

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

  因为StoreService是一个单例的bean,会在Spring应用上下文加载的时候创建。当它创建的时候,Spring会试图将ShoppingCart bean注入到setShoppingCart()方法中。但是ShoppingCart bean是会话作用域的,此时并不存在。直到某个用户进入系统,创建了会话之后,才会出现ShoppingCart实例。
  另外,系统中将会有多个ShoppingCart实例;每隔用户一个。我们并不想让Spring注入某个固定的ShoppingCart实例到StoreService中。我们希望的是当StoreService处理购物车功能时,它所使用的ShoppingCart实例恰好是当前会话所对应的那一个。
  Spring并不会将实际的ShoppingCart bean注入到StoreService中,Spring会注入一个到ShoppingCart bean的代理,这个代理会暴露与ShoppingCart相同的方法,所以StoreService会认为它就是一个购物车。但是当StoreService调用ShoppingCart的方法时,代理会对其进行懒解析并将调用委托给会话作用域内真正的ShoppingCart bean。

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Scope {
    @AliasFor("scopeName")
    String value() default "";

    @AliasFor("value")
    String scopeName() default "";

    ScopedProxyMode proxyMode() default ScopedProxyMode.DEFAULT;
}
//  ScopedProxyMode 是一个枚举类
 public enum ScopedProxyMode {
    DEFAULT,
    NO,
    INTERFACES,
    TARGET_CLASS;

    private ScopedProxyMode() {
    }
}

  如果ShoppingCart是接口而不是类的话,proxyMode属性被设置成ScopeProxyMode.INTERFACES,表明这个代理要实现ShoppingCart接口,并将调用委托给实现bean
  如果ShoppingCart是一个具体的类,那么ProxyMode属性就设置为ScopedProxyMode.TARGET_CLASS,用来表明要以生成目标类扩展的方法创建代理

 

posted @ 2020-04-10 15:33  烟雨蒙尘  阅读(230)  评论(0编辑  收藏  举报