实战Spring高级装配中bean的作用域

2021-04-25 14:39发布

Spring应用上下文中所有bean都是作为以单例(singleton)的形式创建的。也就是说,不管给定的一个bean被注入到其他bean 多少次,每次所注入的都是同一个实例。


因为类很容易改变属性, 重用是不安全的,在这种情况下,单例就不能用了。


Spring 多种作用域

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

原型:每次注入或者通过 Spring 上下面获取的时候,都会创建一个新的 bean 实例;

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

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

单例是默认实例,如果用于宜变的类的话,就不合适了。如果选择其他的作用域,需要使用@Scope 注解,可以与@Component 和@Bean 一起使用。


比如:


@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)  // 可以用来组件扫描来 发现和声明 bean 等同于:@Scope("prototype")

使用会话和请求作用域

实例化在会话和请求范围内共享的bean。


例如,在典型的电子商务应用中,可能会有一个 bean代表用户的购物车。如果购物车是单例的话,那么将会导致所有的用户都会向同一个购物车中添加商品。另一方面,如果购物车是原型作用域的,那么在应用中某一个地方往购物车中添加商品,在应用的另外一个地方可能就不可用了,因为在这里注入的是另外一个原型作用域的购物车。


 @Component

 @Scope(

          value = WebApplicationContext.SCOPE_SESSION,

          proxyMode = ScopedProxyMode.INTERFACES

    )

 public ShoppingCart cart() {

       return null;

 }

这样会告诉 Spring 为 Web 应用中的每个会话创建了一个 ShoppingCart。这样虽然会创建多个实例,但是对于一个会话只有一个实例,也就意味在当前会话相关的操作中,这个 bean 相当于单例的。


proxyMode = ScopedProxyMode.INTERFACES

proxyMode所解决问题的场景:


/**

 * 假设我们要将ShoppingCart bean注入到单例StoreService bean的Setter方法中

 *

 * @author imenger

 * @date 2021/4/16 5:27 下午

 */

@Component

public class StoreService {

 

    @Autowired

    public void setShoppingCart(ShoppingCart shoppingCart){

        this.shoppingCart = shoppingCart;

    }

}

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


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


/**

* Create a JDK dynamic proxy implementing <i>all</i> interfaces exposed by

* the class of the target object.

*/

INTERFACES,

proxyMode属性被设置成 了ScopedProxyMode.INTERFACES,这表明这个代理要实现ShoppingCart接口,并将调用委托给实现bean。


上文单单叙述了 会话作用域, 与其类似的 请求作用域 最好 以作用域代理的方式来进行注入。


如果,ShoppingCart 不是专门的作用域类,是 一个 具体的类的话。Spring 就没有办法创建基于接口的代理了,此时,必须使用 CGLib 来生成基于类的代理。将proxyMode属性设置为ScopedProxyMode.TARGET_CLASS,以此来表明 要以生成目标类扩展的方式 创建代理。


在XML中声明作用域代理

同样除了 javabean 来声明作用域之外的话,xml 也是另外的一种方式,用 xml 来声明。就不需要@Scope 注解以及 proxyMode 属性了。


<bean>元素的scope属性能够设置bean的作用域:


如何指定作用域的代理模式

    <bean id="cart" class="com.myapp.ShoppingCart" scope="session" >

        <aop:scoped-proxy/>

    </bean>

<aop:scoped-proxy>是与@Scope注解的proxyMode属性功能相同的Spring XML配置元素。


它会告诉Spring为bean创建一个作用域代理。默认情况下,它会使用CGLib创建目标类的代理。但是我们也可以将proxy-target-class属性设置为false,进而要求它生成基于接口的代理。


<bean id="cart" class="com.myapp.ShoppingCart" scope="session" >

        <aop:scoped-proxy proxy-target-class="false"/>

    </bean>

 

————————————————

版权声明:本文为CSDN博主「Soinice」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/Soinice/article/details/115765022