Spring-Bean的作用域

  • 概述
  • singleton作用域
    • lazy-init
  • prototype作用域
  • 与Web应用环境相关的Bean的作用域
    • Web容器中配置Http请求监听器
    • request
    • session
    • globalSession
  • 作用域依赖问题

概述

在配置文件中定义Bean时,用户不但可以配置Bean的属性值以及相互之间的依赖关系,还可以定义Bean的作用域。

作用域对Bean的生命周期和创建方式产生影响。

我们来看下Spring4.0支持的所有作用域类型

类型 说明
singleton 在Spring容器中仅存在一个Bean实例,Bean以单例的方式存在
prototype 每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时,相当于执行new XxxBean()的操作
request 每次Http请求都会创建一个新的Bean, 仅适用于WebApplication环境
session 同一个Http Session共享一个Bean,不同的HttpSession使用不同的Bean。仅适用于WebApplication环境
globalSession 同一个全局Session共享一个bean,一般用于Porlet应用环境,仅适用于WebApplication环境

singleton作用域

一般情况下,无状态或者状态不可变的类适合使用单例模式, 不过Spring对此实现了超越,Spring利用AOP和LocalThread功能,对非线程安全的变量或者状态进行了特殊处理,使这些非线程安全的类变成了线程安全的类。

基于这一点,所以在实际应用中,大部分Bean都能以单例形式运行。 这也是Spring将bean的默认作用域指定为singleton的原因。

singleton的Bean在同一SpringIoC容器中只有一个实例。


<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
            http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="plane" class="com.xgj.ioc.scope.Plane" scope="singleton" />

    <bean id="pilot1" class="com.xgj.ioc.scope.Pilot" p:plane-ref="plane" />

    <bean id="pilot2" class="com.xgj.ioc.scope.Pilot" p:plane-ref="plane" />

    <bean id="pilot3" class="com.xgj.ioc.scope.Pilot" p:plane-ref="plane" />

beans>

plane bean的scope声明为singleton (默认为singleton,无需指定,这里仅仅是演示而已), 在容器中有3个其他的Bean(pilot1/2/3)引用这个plane Bean.

在容器内部pilot1/2/3的plane属性都指向同一个Bean,

Spring-Bean的作用域_第1张图片

默认情况下,Spring的ApplicationContext容器在启动时,自动实例化所有singleton的Bean并缓存在容器中。

虽然启动时会花费一些时间,但是有两个好处

  • 首先,对Bean提前进行实例化操作会及早发现一些配置问题
  • 其次,Bean以缓存的方式保存,当运行到该Bean时,无需实例化,提高运行效率。

lazy-init

如果用户不希望在容器启动时提前实例化singleton的Bean,可以通过lazy-init属性进行控制。

"plane" class="com.xgj.ioc.scope.Plane" lazy-init="true" />

lazy-init="true" 的Bean在某些情况下依然会提前实例化,如果该Bean被其他需要提前实例化的Bean所引用,那么Spring会忽略延迟实例化的设置


prototype作用域

采用scope=”prototype”指定非单例作用域的bean .

请看配置

    id="plane" class="com.xgj.ioc.scope.Plane" scope="prototype" />

    id="pilot1" class="com.xgj.ioc.scope.Pilot" p:plane-ref="plane" />

    id="pilot2" class="com.xgj.ioc.scope.Pilot" p:plane-ref="plane" />

    id="pilot3" class="com.xgj.ioc.scope.Pilot" p:plane-ref="plane" />

Spring-Bean的作用域_第2张图片

在默认情况下,Spring容器在启动时不实例化prototype的bean,此外,Spring容器将prototype的bean 交给调用者后,就不再管理它的生命周期。


与Web应用环境相关的Bean的作用域

Web容器中配置Http请求监听器

如果用户使用Spring的WebApplicationContext,则可以使用另外3中Bean的作用域

  1. request
  2. session
  3. gloableSession

不过使用之前,首先必须在Web容器中进行一些额外的配置

高版本的Web容器(Servlet2.3之后)中,可以利用Http请求监听器进行配置

.....

    
        org.springframework.web.context.request.RequestContextListener
    

......

我们知道在WebApplicationContext初始化时已经通过

<listener>
        <listener-class>
            org.springframework.web.context.ContextLoaderListener
        listener-class>
    listener>

ContextLoaderListener将Web容器与Spring容器进行了整合,这里为什么要引入额外的RequestContextListener用来支持Bean的另外3个作用域呢?

事实上ContextLoaderListener 实现了ServletContextListener监听器接口,ServletContextListener只负责监听web容器的启动和关闭时间。

而RequestContextListener实现了ServletRequestListener监听器接口,该监听器监听HTTP请求事件,Web服务器接口的每次请求都会通知该监听器。


request

request作用域的Bean对应一个HTTP请求和生命周期。

如下

    "plane" class="com.xgj.ioc.scope.Plane" scope="request" />

每次HTTP请求调用plane Bean时,Spring容器就会创建一个新的Bean,请求处理完毕,销毁该Bean。


session

"plane" class="com.xgj.ioc.scope.Plane" scope="session" />

这样配置后,plane Bean的组用于横跨整个Http Session . Session中的所有HTTP请求会共享同一个plane Bean. 当HTTP Session结束后,实例才被销毁。


globalSession

"plane" class="com.xgj.ioc.scope.Plane" scope="globalSession" />

globalSession的作用域类似于session作用域, 不过仅在Portlet的Web应用中使用。


作用域依赖问题

如果将Web相关作用域的Bean注入singleton或者prototype的Bean中,我们当然希望引用者从指定的域中取的它的引用,怎么办呢?—Spring AOP

代码清单如下:


<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:aop="http://www.springframework.org/schema/aop"

    xsi:schemaLocation="http://www.springframework.org/schema/beans 
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/aop
            http://www.springframework.org/schema/aop/spring-aop.xsd">
      


    <bean id="plane" class="com.xgj.ioc.scope.Plane" scope="request" >
        <aop:scoped-proxy/> 
    bean>

    <bean id="pilot" class="com.xgj.ioc.scope.Pilot" p:plane-ref="plane" />
    


beans>

上面的配置文件中,我们可以看到 plane Bean的作用域是request, 它被singleton作用域的Pilot Bean引用。为了使Pilot能从适当的作用域中获取plane的引用, 需要使用Spring的AOP为plane Bean的配置声明一个代理类,如(2)所示。

当pilot Bean 在web环境中调用plane Bean 时,Spring AOP 将启动动态代理机制智能的判断 Pilot Bean位于哪个HTTP请求线程中,并从对应的HTTP请求线程中回去对应的 plane Bean.

pilot Bean的作用域为singleton, 也就是说容器中始终只有一个实例, 而plane Bean的作用域为request, 所有每个调用PlaneBean的http请求都会创建一个新的 plane Bean.

Spring 通过动态代理技术,能够让 Pilot bean引用到对应HTTP请求的Plane Bean。

你可能感兴趣的:(【Spring-IOC】,Spring-IOC手札)