Spring in Action 4读书笔记之如何定义bean的使用范围

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
2
3
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class Notepad { ... }

使用java显式定义bean的例子:

1
2
3
4
5
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Notepad notepad() {
return new Notepad();
}

使用xml中bean标签的scope属性显示定义bean的例子:

1
2
3
<bean id="notepad"
class="com.myapp.Notepad"
scope="prototype" />

上面的这些例子,都会在每次注入时从Spring应用上下文中创建一个新的实例。

使用request或session范围

在web应用中,在request或session范围内共享一个bean会非常有用。比如在电子商务应用中的购物车,如果定义为Singleton,所有的用户就会使用同一个购物车,显然会造成混乱。如果定义为Prototype,那么每次注入的都是一个新的购物车,那么这个购物车就没法保存东西了。应该定义session范围的bean:

1
2
3
4
5
@Component
@Scope(
value=WebApplicationContext.SCOPE_SESSION,
proxyMode=ScopedProxyMode.INTERFACES)
public ShoppingCart cart() { ... }

value属性定义了这个bean的使用范围是session。另一个proxyMode是为了解决使用session或request范围的bean造成的问题。来看看没有定义这个属性会有什么问题,先看下面注入这个bean的例子:

1
2
3
4
5
6
7
@Component
public class StoreService {
@Autowired
public void setShoppingCart(ShoppingCart shoppingCart) {
this.shoppingCart = shoppingCart;
}
}

StoreService本身是一个单例的bean(没有使用@Scope,使用默认范围),而shoppingCart是一个session范围的bean。这样会带来两个问题。首先,单例的bean在Spring应用上下文加载时就会创建,而这个时候session范围的bean还没有创建,session范围的bean要等到该用户到来,生成了相应session时,才会创建。第二个问题是,单例的bean只有一个实例,而session范围的bean取决于session的个数,一般有多个实例,这样,没有办法知道要为StoreService注入哪个shoppingCart实例。

Spring对scope范围的bean进行代理

所以,需要使用代理模式,如图,为shoppingCart创建一个代理,这个代理定义了ShoppingCart的所有方法,在注入bean时,不是直接注入shoppingCart实例,而是注入这个代理。由于ShoppingCart是一个接口,所以这里使用ScopedProxyMode.INTERFACES。如果是具体实例,则改用ScopedProxyMode.TARGET_CLASS。

request范围的bean的用法和session范围的bean类似,这里不再赘述。

© 2022 谈谈IT All Rights Reserved. 本站访客数人次 本站总访问量
Theme by hiero