spring的bean的scope主要是用来指定如何创建bean对象的,系统已经实现的主要有五中类型,分别是:singleton、prototype、request、session和globalSession,其中request、session和globalSession是只能在web环境中使用的 ,当你在非web环境中使用它们时,系统会抛出IllegalStateException异常,
当然这个也是可以自己进行定义的。
注意:在使用request、session和globalSession的时候,如果你用的不是springMVC的话是需要做一点配置的,具体做法是:
a、如果你用的是servlet2.4+的话,那么你需要在web.xml中加入一个RequestContextListener监听器
<web-app> ... <listener> <listener-class> org.springframework.web.context.request.RequestContextListener </listener-class> </listener> ... </web-app>
b、当然,如果你用的是再老一点的版本的话,你需要在web.xml中加入一个RequestContextFilter
<filter> <filter-name>requestContextFilter</filter-name> <filter-class>org.springframework.web.filter.RequestContextFilter</filter-class> </filter> <filter-mapping> <filter-name>requestContextFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
基于xml方式的scope指定:
<bean id="test" class="com.xxx.spring.cope.Test" scope="singleton"/>
1.singleton,系统默认的scope就是singleton,它表示在整个请求过程中都只会创建一个bean对象,该对象创建以后是保存在singleton beans的缓存中的,以后每次需要的时候都会去取得同一个bean对象。
值得注意的是spring里面的singleton(单例)跟GOF设计模式里面的单例是有区别的,GOF里面的单例是每个类加载器下面只会有一个对象,而spring里面的singleton是每个spring容器会拥有一个对象。
2.prototype:
prototype不是单例形式的,它会在每次有一个新的请求来请求当前对象的时候都会生成一个新的对象。
值得注意的是当一个singleton的bean A 依赖于一个prototype的bean B 的时候,由于singleton的bean A 只会初始化一次,那么如果在其初始化的时候就给其注入一个prototype的bean B 的时候,A拥有的B就只会在A初始化的时候初始化一次,而且每次在A使用B的时候都是用的同一个对象B,这与我们原始想的B为prototype有点违背,不是我们想要的结果,其解决办法是,使bean A 实现一个ApplicationContextAware接口,在每次A需要使用B的时候都从ApplicationContext里面取一个B对象,这个时候取的B对象每次都会是不一样的。
import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; @Component public class A implements ApplicationContextAware { private ApplicationContext applicationContext; private B b; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { // TODO Auto-generated method stub this.applicationContext = applicationContext; } public B getB() { return applicationContext.getBean(B.class); } public void setB(B b) { this.b = b; } }
import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; @Component @Scope("prototype") public class B { //类里面的其他内容 }
3、request:
先看下面这样一个bean定义:
<bean id="loginAction" class="com.xxx.LoginAction" scope="request"/>
上面的情况,在每次有一个http Request请求LoginAction的时候,spring容器都会新建一个全新的LoginAction对象。这就是Request级别的scope。由于每个请求都对应一个全新的LoginAction对象,所以在请求过程中,你可以随意改变其中包含的实例属性。当一次Request请求完毕之后,其对应的LoginAction对象也就被销毁了。
4、session:
先看下面这样一个bean定义:
<bean id="UserService" class="com.xxx.UserService" scope="session"/>
在上面这种情况,spring容器会为每个处于活跃状态的http Session创建一个UserService对象,这就是session级别的scope。其是和httpSession一起销毁的。
5.globalSession:
先看下面这样一个bean定义:
<bean id="UserService" class="com.xxx.UserService" scope="globalSession"/>
globalSession表示在一个全局的HttpSession下会拥有一个单独的实例,通常用于Portlet环境下。
当需要把一个http级别的scope的对象注入到其他bean中的时候,需要在声明的http级别的scope的对象中加入<aop:scoped-proxy/>,如下面的userPreferences对象
<!-- an HTTP Session-scoped bean exposed as a proxy --> <bean id="userPreferences" class="com.foo.UserPreferences" scope="session"> <!-- this next element effects the proxying of the surrounding bean --> <aop:scoped-proxy/> </bean> <!-- a singleton-scoped bean injected with a proxy to the above bean --> <bean id="userService" class="com.foo.SimpleUserService"> <!-- a reference to the proxied userPreferences bean --> <property name="userPreferences" ref="userPreferences"/> </bean>
这样做的原因 是正常情况下singleton的userService中有一个session级别的对象,这样singleton的userService只初始化一次,而其所依赖的session级别的userPreferences也只初始化一次。这就与我们所定义的每个session对应一个对象的初衷相违背了,而使用<aop:scoped-proxy/>的时候,就会在实际调用的时候每次使用代理去代理userPreferences调用其对应的方法,代理访问的是对应的session中的对象,这样就可以实现每个session对应一个对象。而在代理的时候有两种方式,一种是基于JDK的interface的,一种是CGLIB形式的,如果要代理的类是面向对象的,就可以直接使用JDK的代理,否则就需要开启对CGLIB代理的支持,同时要引入CGLIB的jar包。
<bean id="userPreferences" class="com.foo.DefaultUserPreferences" scope="session"> <aop:scoped-proxy proxy-target-class="false"/><!-- 为true则为开启对CGLIB的支持 --> </bean>