Spring in Action(Spring实战)的第三章第四节(3.4 Scoping beans)讲述了bean的使用范围。在不同场景下,可以使用不同的使用范围,比如web程序一般使用和普通应用程序不同的范围。
bean的使用范围
默认情况下,Spring应用上下文中的bean都是单例。这意味着无论一个bean被注入多少次,总是注入同一个实例。
有时候,要处理互斥的任务,要求bean的属性值只被其中一个任务访问和修改,这时候,单例会引起问题。别担心,除了单例外,Spring还定义了bean的其它使用范围。Spring定义的bean使用范围如下:
- Singleton:单例,整个应用程序使用唯一实例。
- Prototype:每次注入时都会从Spring应用上下文中创建一个新的实例。
- Session:在web应用中,为每个session创建一个实例。
- Request:在web应用中,为每个request创建一个实例。
可以使用@Scope标签标注@Component标签(定义准备被自动注入的bean),也可以使用@Scope标签标注@Bean标签(显式定义bean)。
在xml中,也可以使用bean标签的scope属性指定bean的范围。
使用自动注入的例子:
1 |
|
使用java显式定义bean的例子:
1 |
|
使用xml中bean标签的scope属性显示定义bean的例子:
1 | <bean id="notepad" |
上面的这些例子,都会在每次注入时从Spring应用上下文中创建一个新的实例。
使用request或session范围
在web应用中,在request或session范围内共享一个bean会非常有用。比如在电子商务应用中的购物车,如果定义为Singleton,所有的用户就会使用同一个购物车,显然会造成混乱。如果定义为Prototype,那么每次注入的都是一个新的购物车,那么这个购物车就没法保存东西了。应该定义session范围的bean:
1 |
|
value属性定义了这个bean的使用范围是session。另一个proxyMode是为了解决使用session或request范围的bean造成的问题。来看看没有定义这个属性会有什么问题,先看下面注入这个bean的例子:
1 |
|
StoreService本身是一个单例的bean(没有使用@Scope,使用默认范围),而shoppingCart是一个session范围的bean。这样会带来两个问题。首先,单例的bean在Spring应用上下文加载时就会创建,而这个时候session范围的bean还没有创建,session范围的bean要等到该用户到来,生成了相应session时,才会创建。第二个问题是,单例的bean只有一个实例,而session范围的bean取决于session的个数,一般有多个实例,这样,没有办法知道要为StoreService注入哪个shoppingCart实例。
所以,需要使用代理模式,如图,为shoppingCart创建一个代理,这个代理定义了ShoppingCart的所有方法,在注入bean时,不是直接注入shoppingCart实例,而是注入这个代理。由于ShoppingCart是一个接口,所以这里使用ScopedProxyMode.INTERFACES。如果是具体实例,则改用ScopedProxyMode.TARGET_CLASS。
request范围的bean的用法和session范围的bean类似,这里不再赘述。