Spring装配Bean(六)Bean的作用域

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

在大多数情况下,单例bean时很理想的方案。有时候存在,所使用的类是易变的,对这些类的bean进行重用可能就不安全,例如购物车bean如果是单例的话,每个用户都向一个购物车中添加商品,这就有问题了

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

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

2> 原型(Prototype):每次注入或者通过Spring应用上下文获取的时候,都会去创建一个新的Bean

3> 会话(Session):在Web应用中,为每个会话创建一个Bean实例。

4> 请求(Request):在Web应用中,为每个请求创建一个Bean实例

* JavaConfig中设置作用域

@Scope注解和@Component或@Bean一起使用

@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public CDPlayer cdplayer(){
    return new CDPlayer(this.cd);
}
@Component
@Qualifier("compactDisc2")
@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
public class SgtPeppers implements CompactDisc {
}

这里,使用ConfigurableBeanFactory类的SCOPE_PROTOTYPE常量设置了原型作用域,当然也可以使用@Scope("prototype"),但是SCOPE_PROTOTYPE常量更加安全并且不易出错。

* XML中配置Bean的作用域

 <bean id="compactDisc1" class="com.erong.service.SgtPeppers" scope="prototype">
   		<constructor-arg name="title" value="遥远的ren"></constructor-arg>
   		<constructor-arg name="artist" value="big ben..."></constructor-arg>
   </bean>

* 使用会话和请求作用域

在Web应用中,如果能够实例化在会话和请求范围内共享的Bean,将是很有价值的事情。

例如,在典型的电子商务应用中,可能会有一个Bean代表用户的购物车。如果购物车是单例的,那么就会导致多个用户向同一个购物车中添加商品,如果购物车是原型的,在一个位置设置了,购物车的属性(商品名称,种类..),在另一个地方通过注入使用的时候,将拿不到刚才那个Bean,而是一个新创建的Bean,这就存在问题了。

* JavaConfig中声明作用域代理

就购物车bean来说,会话作用域最合适

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

}

我们将value设置成WebApplicationContext中的SCOPE_SESSION常量。将告诉Spring为Web应用中的每个会话创建Bean。

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

现在将ShoppingCart注入到单例StoreService bean的setter方法中,如下所示:

@Component
@Qualifier("storeService")
public class StoreService {
	private ShoppingCart sc ;
	@Autowired
	@Qualifier("sc")
	public void setShoppingCart(ShoppingCart sc){
		this.sc = sc;
	}
}

StoreService是单例的,创建这个Bean的时候,Spring上下文回去尝试自动注入ShoppingCart这个Bean,但是只要是用户还没有访问系统,这个bean就不会创建。

另外,对于每个用户,都会去创建ShoppingCart这个Bean,也就是StoreService注入的Bean,不是唯一的,我们希望的是注入的购物车Bean,刚好是当前会话的那个。

Spring实际上不将将真正的ShoppingCart Bean注入到StoreService,而是注入它的代理,到具体使用到ShoppingCart Bean才会去,进行懒解析找到实际的Bean。

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

如果ShoppingCart是接口而不是类,这是可以的(也是最为理想的代理模式),但如果shoppingCart是一个具体的类的话,Spring 无法创建基于接口的代理了,此时,必须使用CGLIB生成基于类的代理。将proxyMode属性设置为ScopedProxyMode.

TARGET_CALSS

* 在XML中声明作用域代理

<bean id="helloWorld" class="com.erong.service.HelloWorld" scope="session">
   	<aop:scoped-proxy proxy-target-class="false"/>
   </bean>

默认是采用cglib生成代理对象,如果将proxy-target-class="false" 将表示基于接口生成代理对象



猜你喜欢

转载自blog.csdn.net/Ditto_zhou/article/details/80446345