Spring定义了多种作用域,可以基于这些作用域创建bean
Singleton 单例 整个应用中,只创建一个
Prototype 原型 每次注入或者通过Spring上下文获取时,都会创建一个新的bean实例
Session 会话 在web应用中,为每个会话创建一个bean实例
Request 请求 在web应用中,为每个请求创建一个bean实例
单例是默认的作用域。可以通过@Scope注解来改变,它可以和@Bean和@Component一起使用。
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class Notepad { ..... }
同样,可以使用XML来配置bean的话,则:
<bean id = "notepad" class = "com.myapp.Notepad" scope = "prototype" />
这样,每次注入或者从Spring上下文获取bean都会创建新的实例
会话作用域和请求作用域
如电子商务系统中,一个bean代表一个购物车,那么如果是单例的话,所有的用户都向一个购物车添加,如果是原型(多例)作用域的话,在一个地方添加购物车,另一个地方就不能用了。就购物车而言,会话作用域最为合适。因为它与给定用于关联性最大。
@Component
@Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.INTERFACES)
public ShoppingCart cart() { ........... }
这里,将value设置成WebApplicationContext中的SCOPE_SESSION常量,它会告诉Spring为每个会话创建一个实例bean
@Scope还有一个属性 proxyMode,它被设置成ScopedProxyMode.INTERFACES,这个属性解决了将会话或者请求作用域的bean注入到单例bean中所遇到的问题。如:
@Component
public class StoreService{
@Autowired
public void setShoppingCart(ShoppingCart shoppingCart){
this.shoppingCart = shoppingCart;
}
....
}
因为StroeService是一个单例bean,会在Spring上下文加载的时候创建它,当它创建的时候,Spring会试图将ShoppingCart bean注入到setShoppingCart()方法中,但是ShoppingCart bean是会话作用域的,此时并不存在,直到某个用户进入系统,创建会话后才会出现ShoppingCar 实例。Spring并不会将实际的ShoppingCart bean注入到StoreServeice中,Spring会注入一个到Shopping cart Bean的代理,这个代理会暴露与ShoppingCart相同的方法,所以Service会认为他是一个购物车。但是当StoreService调用ShoppingCart的方法时,代理会对其进行懒解析并将调用委托给会话作用域内真正的ShoppingCart bean.
proxyMode属性被设置成ScopedProxyMode.INTERFACES,表明这个代理要实现ShoppingCart接口,并将调用委托给实现bean.如果ShoppingCart是一个接口,那么这么设置是可以的。但是如果ShoppingCart是一个具体的类的话,Spring就没有办法创建基于接口的代理了。此时,它必须使用CGLib来生成基于类的代理,所以,如果bean类型是具体类的话,我们必须将proxyMode属性设置成:ScopedProxyMode.TARGET_CLASS,以此来表明要生成目标类扩展的方式创建代理。请求作用域的bean也会面临同样的装配问题,因此请求作用域的bean应该也以作用域代理的方式进行注入。
在XML中声明作用域代理
<bean id="cart" class = "com.myapp.ShoppingCart" >
<aop:scopde-proxy / >
</bean>
<aop:scopde-proxy / >是与@Scope注解的proxyMode属性功能相同的Spring XML配置元素,它会告诉Spring为bean创建一个作用域代理,默认情况下,它会使用CGLib创建目标类的代理,但是我们可以将proxy-target-class属性设置为false,进而要求它生成基于接口的代理。
<bean id="cart" class = "com.myapp.ShoppingCart" scope="session" >
<aop:scopde-proxy proxy-target-class="false"/ >
</bean>