Bean的作用域
默认情况下,Spring应用上下文中所有bean都是作为以单例(singleton)的形式创建的。
- 单例(Singleton): 在整个应用中,只创建bean的一个实例
- 原型(Prototype):每次注入或者通过Spring应用上下文获取的时候,都会创建一个新的bean实例
- 会话(Session):在Web应用中,为每个会话创建一个bean实例
- 请求(Request):在Web应用中,为每个请求创建一个bean实例
推荐使用方式
@Component @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) public class Notepad(.....) 当然使用 @Scope("prototype")也是可以的,但是推荐使用常量 xml配置 <bean id = "notepad" class = "com.lyc.Notepad" scope="prototype"/>
使用会话和请求作用域
在web应用中,single和prototype不太适合使用,相反 session和required的会比较合适。例如,在典型的电子商务应用中,可能会有一个bean代表用户的购物车。如果购物车是单例的话,那么将会导致所有的用户都会向同一个购物车中添加商品。另一方面,如果购物车是原型作用域(prototype),那么在应用中某一个地方往购物车中添加商品,在应用的另外一个地方可能就不可用了,因为在这里注入的是另外一个原型作用域的购物车。
就购物车bean来说,会话作用域是最合适的,因为它与给定的用户关联性最大。要指定会话的作用域,我们可以使用@scope注解,它的使用方式与指定原型作用域是相同的
@Component @Scope{value = WebApplicationContext.SCOPE_SESSION,proxyMode = ScopedProxyMode.INTERFACES} public ShoppingCart Cart(){....}
这里,我们将value设置为WebApplicationContext中的SCOPE_SESSION常量(它的值是session)。这会告诉spring为web应用中的每个会话创建一个ShoppingCart。这会创建多个ShoppingCart bean实例,但是对于给定的会话只会创建一个实例,在当前会话操作中,这个bean实际上相当于单例
注意: @Scope同时还有一个ProxyMode,他被设置成了ScopedProxyMode.INTERFACES.这个属性解决了将会话或请求作用域的bean注入到单例bean中所遇到的问题。
假设我们要将ShoppingCart bean注入到单例StoreService bean 的setter方法中,如下所示:
@Component public class StoreService{ @Autowired public void setShoppingCart(ShoppingCart shoppingCart){ this. shoppingCart = shoppingCart; } ....... }
因为StoreService是一个单例的bean,会在Spring应用上下文加载的时候创建。当它创建的时候,Spring会试图将ShoppingCart bean注入到setShoppingCart()方法中。但是ShoppingCart bean是会话作用域的,此时并不存在。直到某个用户进入系统,创建了会话之后,才会出现ShoppingCart实例。
另外,系统中将会有多个ShoppingCart实例;每隔用户一个。我们并不想让Spring注入某个固定的ShoppingCart实例到StoreService中。我们希望的是当StoreService处理购物车功能时,它所使用的ShoppingCart实例恰好是当前会话所对应的那一个。
Spring并不会将实际的ShoppingCart bean注入到StoreService中,Spring会注入一个到ShoppingCart bean的代理,这个代理会暴露与ShoppingCart相同的方法,所以StoreService会认为它就是一个购物车。但是当StoreService调用ShoppingCart的方法时,代理会对其进行懒解析并将调用委托给会话作用域内真正的ShoppingCart bean。
@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Scope { @AliasFor("scopeName") String value() default ""; @AliasFor("value") String scopeName() default ""; ScopedProxyMode proxyMode() default ScopedProxyMode.DEFAULT; } // ScopedProxyMode 是一个枚举类 public enum ScopedProxyMode { DEFAULT, NO, INTERFACES, TARGET_CLASS; private ScopedProxyMode() { } }
如果ShoppingCart是接口而不是类的话,proxyMode属性被设置成ScopeProxyMode.INTERFACES,表明这个代理要实现ShoppingCart接口,并将调用委托给实现bean
如果ShoppingCart是一个具体的类,那么ProxyMode属性就设置为ScopedProxyMode.TARGET_CLASS,用来表明要以生成目标类扩展的方法创建代理