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

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

目录

Spring 多种作用域

使用会话和请求作用域

proxyMode = ScopedProxyMode.INTERFACES

在XML中声明作用域代理

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


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

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>

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值