相信使用过spring框架的 都接触过了解过spring bean的概念,它与java 的bean有所不同。springBean存在作用域这一概念。
相信大家对于@Scope这个注解一定不会陌生。
在默认情况下,springIOC中所有的bean都是以单例(singleton)的形式创建的,也就是说,不管给定的一个bean被注入到其他bean中多少次,每次所注入的都是全局唯一的一个实例。
这个默认情况在大多数场景中都是很理想的一个方案,这样系统对于bean的管理和对象的垃圾回收成本都会很小。
但是在一些情况下,可能就会出现些许问题。例如,我所注入的这个类是易变的,它的某个成员变量作为状态信息,需要被保持,因此不能再在任意的重用它了(这会导致状态值的改变)。这种情况下,singleton就不是什么好主意了。
对此,spring定义了多种作用域,可以基于业务场景灵活调整:
1.单例(singleton):在整个应用中,只会创建bean的一个实例
2.原型(prototype):每次注入或者通过spring应用上下文获取的时候,都会创建一个新的bean实例
3.会话(Session):在Web应用中,为每个会话创建一个bean实例
4.请求(Request):在Web应用中,为每个请求创建一个bean实例
要更改bean的作用域,可以使用@Scope注解
该注解可以加在类上(基于组件扫描来装配bean的模式)和方法上(基于javaConfig来装配bean的模式)
ConfigurableBeanFactory.SCOPE_PROTOTYPE定义了原型作用域的常量,推荐使用
如果使用xml的方式来配置bean的话,可以使用<bean>元素的scope属性来设置作用域
<bean id="test" class="com.myapp.Test" scope="prototype">
</bean>
需要注意一点的是,在使用会话作用域和请求作用域时,应在scope上加入proxyMode参数,否则会出现如下问题:
当你试图向一个单例模式的bean注入一个会话模式的bean时,会出现异常,因为该会话模式的bean只会在会话发起时创建,而当单例模式的bean初始化时,该会话模式的bean并不存在。
proxyMode参数的解决方法是,为这个待注入的bean添加一个代理,这个代理具备与原bean相同的方法,以“蒙蔽”初始化的检查。当原bean真正被调用时,代理会解析并调用委托给真正的bean。详细解答参考 java动态代理技术
一般 跑proxyMode参数有两个值,ScopedProxyMode.INTERFACES和ScopedProxyMode.TARGET_CLASS,前者为默认值,使用基于接口的代理,代理的bean需要为接口类型。后者使用基于CGLib来实现类的代理,若代理的bean是具体的类的话,需要设置此类型。
当你使用xml配置时,可以在<bean>元素内使用<aop>标识 来启用作用域代理。例如:
<bean id="test" class="com.myapp.Test" scope="session">
<aop:scope-proxy proxy-target-class="false" />
</bean>
当proxy-target-class为true时,将开启CGLib的类代理,否则执行接口代理