SpringBean作用域——基本作用域与web作用域

Spring的 bean有5种作用域分别是:singleton、prototype、request、session和globalSession(不常用)
其中后三种request、session、global session专用于Web应用程序。

1、singleton 单例

在Spring里,通过容器创建的对象默认是singleton单例(这里要注意的是singleton作用域和GOF设计模式中的单例是不同的)
单例就是在整个容器的生命周期,只会存在一个共享的bean实例。
下面是文档上的图:
在这里插入图片描述

可如下配置实例:

<bean id="order" class="twm.spring.start.Order" scope="singleton" />

或者

<bean id="order" class="twm.spring.start.Order" singleton="true" />

或者什么都不写,默认就是单例

<bean id="order" class="twm.spring.start.Order" />

2、prototype 多例

如果某个bean被标记为多例,则每次请求使用该对象时,都会创建一个新的bean实例。比如将这个bean注入到另一个bean中,或者在程序中调用getBean(“beanid”)方法,都会触发生成一个新的bean实例,相当于new的操作。
下面是文档上的图:
在这里插入图片描述
Spring容器会对bean 的生命周期负责。容器会调用构造函数初始化bean,调用析构函数清理释放bean。
但是对于prototype作用域的bean,Spring容器就不会整个生命周期负责了。在初始化完成后,就交给调用它的程序,析构释放都由调用程序负责了。

配置实例:

<bean id="order" class="twm.spring.start.Order" scope="prototype" />

或者

<bean id="order" class="twm.spring.start.Order" singleton="false" />

要注意singleton 依赖prototype的情况

singleton 类依赖了prototype类,容器会在singleton 类初始化就会根据依赖关系将prototype类注入。以后的每一次调用singleton bean都是同一个对象,里面的prototype bean也是最初注入的那个,容器再也不会为singleton bean产生新的prototype bean。
要想在singleton bean中每次调用时都产生新的prototype bean,可使用代理。

如果想把一个web作用域的Bean注入到另一个周期长的作用域的Bean中(比如单例的bean),就需要选择注入一个AOP代理来替换这个web作用域Bean。在定义时,web作用域的Bean都需要声明使用代理模式:配置中加上<aop:scoped-proxy/>元素

官方文档的例子:

<!-- 一个HTTP session作用域的Bean 作为代理暴露出去 -->
    <bean id="userPreferences" class="com.foo.UserPreferences" scope="session">
        <!--指示容器代理这个Bean -->
        <aop:scoped-proxy/>
    </bean>
    <!--一个单例Bean注入一个代理Bean -->
    <bean id="userManager" class="com.foo.UserManager">
      <!-- 实际使用的是userPreferences的代理对象 -->
        <property name="userPreferences" ref="userPreferences"/>
</bean>

为什么要这样做?
上例中,HTTP Session作用域的userPreferences bean注入给单例userManger bean。因为userManager bean是单例的,即每个容器只会实例化一个,所以它的依赖对象userPreferences bean也仅会注入一次。这样下来,userManagerbean只能操作相同的userPreferences对象,就是初始化时注入的那一个。

将一个短生命周期作用域bean注入给长生命周期作用域bean都会存在此类问题。
我们期望的是userManager对象中的userPreferences beans与session同生命周期和同作用域。

在userPreferences bean配置中加入<aop:scoped-proxy/>后,容器将创建一个代理对象,该对象拥有和UserPreferences完全相同的public接口并暴露。代理对象每次调用时会从 Session范围内获取真正的UserPreferences对象,而userManager类却不知道。


3、Request

对于每次HTTP请求,使用request定义的Bean都将产生一个新实例,每个HTTP request中的实例都是独立互不影响的的
比如下面bean定义:

<bean id="userrole" class="twm.demo.UserRole" scope="request" />

针对每次HTTP请求,Spring容器会创建一个全新的twm.demo.UserRole bean实例userrole bean, 且userrole bean仅在当前HTTP request内有效。
因此当request A请求创建并更改了userrole bean的内部状态,request B请求中创建的userrole bean,并不会有相应的这些状态变化。
当处理请求结束,request作用域的bean实例将被销毁。

4、Session

和Request很类似,对于每个HTTP Session,使用session定义的Bean都将产生一个新实例。每个HTTP Session中的实例都是独立互不影响的的。当HTTP Session最终被废弃的时候,在该HTTP Session作用域内的bean也会被废弃掉。

这里重点要明白request 作用域 和session作用域的差别:
一个http session中可以有多个request
简单说就是,用户A访问同一网站的PageA和PageB,这就有两个http request,但是只有一个http session
用户A,B,C三人同时访问这个网站的PageA,这就有三个http request,同时也有三个 http session

5、globalSession

global session作用域类似于标准的HTTP Session作用域,不过它仅仅在基于portlet的web应用中才有意义。

以上web作用域只有在spring web ApplicationContext的实现中(比如XmlWebApplicationContext)才会起作用,若在常规Spring IoC容器中使用,比如ClassPathXmlApplicationContext中,就会收到一个异常IllegalStateException来告诉你不能识别的bean作用域

初始化web配置

为了支持request,sesssion,global session这种级别bean的作用域(web作用域bean),在定义bean之前需要一些初始化的小配置。
目的是为了使Spring捕获到相应的事件(如request请求开始,request请求结束,session会话开始,sesssion会话结束等。)

这里分两种情况:
1、 SpringMVC:
若使用 SpringMVC访问这些作用域bean,实际上是使用Srping DispatcherServlet类或者DispatcherPortlet类处理request,则无需特别配置。因为DispatcherServlet 和 DispatcherPortlet已经暴露了所有的相关状态。

2、非Spring的DispacherServlet
若使用了非Spring的DispacherServlet处理请求,比如Struts,则需要注册:

2.1、Servlet 2.4及以上的web容器,需要在web.xml中增加监听器:

<web-app>
    ...
    <listener>
        <listener-class>
            org.springframework.web.context.request.RequestContextListener
        </listener-class>
    </listener>
    ...
</web-app>

2.2、Servlet2.4以前的web容器,在web.xml中增加过滤器:

<web-app>
 ..
 <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>
   ...
</web-app>

DispatcherServlet,RequestContextListener,RequestContextFilter都是做相同的事儿,也就是绑定HTTPrequest对象到服务的Thread线程中,并开启接下来 用到的session-scoped功能。

猜你喜欢

转载自blog.csdn.net/m0_37556444/article/details/83109788