在Spring中,默认情况下所有的bean都是单例作用域的,也就是说不管给定的一个bean被注入多少次,所注入的都是同一个实例。
但是,有时候,你所使用的类是易变的,他们会保持一些状态,因此重复使用是不安全的,在这种情况下把class声明为单例就会造成对象污染。
Spring定义了多种作用域包括:
单例(Singleton):在整个应用中,只创建bean一个实例
原型(Prototype):每次注入或者通过Spring应用上下文获取的时候,都会创建一个新的bean实例
会话(Session):在Web应用中,会为每个会话创建一个bean实例
请求(Request):在web应用中,为每个请求创建一个bean实例
@Scope注解就是表示当前bean是哪个作用域的
比如你想声明一个原型的Bean
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class Notepad(){
}
你也可以使用@Scope("prototype")
如果你想使用会话作用域的话
@Component
@Scope(value="WebApplication.SCOPE_Session,
proxyMode =ScopedProxyMode.INTERFACES)
public ShoppingCart cart(){
}
proxyMode的作用是为了解决会话注入单例Bean时出现的问题
因为StoreService是一个单例的Bean,会在Spring应用上下文的时候就会被创建,当他被创建的时候,Spring 会试图区找ShoppingCart 的 Bean注入到StoreService,但是这个时候还没有ShoppingCart,只有当有用户进入系统,创建会话后,才会有ShoppingCart
另外,系统中将会有多个SHoppingcart实例,每个用户一个,我们并不想让Spring注入一个固定的Shoppingcart实例,我们希望当StoreService处理购物车的时候,他所使用的Shoppingcart实例恰好是当前会话所对应的那一个
其实Spring 不会将实际的Shoppingcart Bean注入到StoreService,Spring会注入一个Shoppingcart的代理,这个代理会暴露出和Shoppingcart一样的方法,所以对于StoreService而言,这个代理就是一个购物车,但是当StoreService调用Shoppingcart方法时,代理会对其进行懒解析并调用委托给会话作用域的真正的Shoppingcart
对于ScopedProxyMode.INTERFACES 这表明这个代理要实现Shoppingcart接口。并将调用委托给实现bean
如果Shoppingcart是一个接口,这样是可以的,但是如果Shoppingcart是一个类的话,Spring就没有办法创建基于接口的代理了,此时,它必须使用CGLIB来生成基于类的代理,所以如果bean是个类的话,我们就必须使用ScopedMode.TARGET_CLASS,以此来表明要以生成目标类扩展的方式创建代理
请求作用域一样也以相同的方式注入。