我是如何理解Spring Bean的作用域

在默认的情况下,Spring应用上下文所有的Bean都是单例形式创建的,也就是说,不管给哪一个bin被注入到其他bean多少次,每次注入的都是同一个实例。

但有些时候,单例的并不能解决所有问题,

Spring定义了多种作用域,可以基于这些作用域创建bean 包括:

单例 Singleton :在整个应用中,只创建bean的一个实例

原型 Prototype: 每次注入或者通过Spring上下文获取的时候,都是一个新的bean实例

会话 Session :在Web应用中,为每个会话创建一个bean实例

请求 Request: 在web应用中,为每个请求创建一个bean实例

就像上面说的那样,Spring默认的是使用单例作用域, 如果要选择其他的作用域,要使用@Scope注解,,他可以和@Component注解或者@Bean一起使用

例如你使用组件扫描来发现和声明Bean 那么你可以在你bean的类上使用@Scope注解,将其声明为原型bean:

@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class Noteped{
...
}

当然你也可以使用@Scope("prototype"), 但是使用SCOPE_PROTOTYPE常量会更加安全并且不容易出错。

如果你想在Java配置中将Notepad声明为原型bean那么可以使用@Scope和@Bean来指定所需的作用域:

@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Notepad noteped (){
    return new Notepad()
}

如果你使用XML来配置的话,也可以在<bean>元祖的scope属性来设置作用域

不管你使用哪种方式来声明原型作用域,每次注入或从Spring应用上下文中检索该bean的时候,都会创建新的实例,这样所导致的结果就是每次操作都能得到自己的Notepad实例

下面说一下使用会话和请求作用域!!

我们首先举个例子,比如在电商的场景下,可能有一个bean代表的是购物车ShoppingCart,如果这个购物车是单例的,会怎么样的?那就是所有人都向这一个购物车添加商品,而如果购物车是个原型的呢?也不行,因为在某个地方往购物车中添加商品,在应用的另外一个地方就不能用了,就只能一个一个买了。

对于购物车来说,我们最好用的就是一个会话作用域。

@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中所遇见的问题,在描述proxyMode属性之前,我们先看一下proxyMode所解决问题的场景

假设我们要将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实例:每个用户一个,我们并不想让Spring注入某个固定的ShoppingCar实例到StoreServie中,我们希望的是当StoreService处理购物车功能时,他使用的是ShoopingCart实例恰好是当前会话所对应的那一个。

Spring并不会将实际的ShoppingCart bean注入到StoreService中,Spring会注入一个到ShoppingCart bean的代理,这个代理会暴露和ShoppingCart相同的方法,所以StoreSercie会认为它就是一个购物车,但是,当StoreService调用ShoppingCart的方法时,代理会对其进行懒解析并将调用委托给会话作用域内真正的ShippingCart bean

如果你想在XML中声明作用域代理

那么需要使用Spring aop命名空间的一个新元素

<bean id="cart"
    class = "com.myapp.ShoppingCart"
    scope="session"
   <aop:scoped-prexy>
</bean>

默认的会使用CGlIB创建目标类的代理。但是我们也可以将proxy-target-class 属性设置为false,进而要求他生成基于接口的代理。

猜你喜欢

转载自blog.csdn.net/Chen_leilei/article/details/107763371
今日推荐