Spring Bean 的5种作用域

Spring Bean 中所说的作用域,在配置文件中即是“scope
在面向对象程序设计中作用域一般指对象或变量之间的可见范围。
而在Spring容器中是指其创建的Bean对象相对于其他Bean对象的请求可见范围

在Spring 容器当中,一共提供了5种作用域类型,在配置文件中,通过属性scope来设置bean的作用域范围。
1.    singleton:
[html]  view plain  copy
  1. <bean id="userInfo" class="cn.lovepi.UserInfo" scope="singleton">bean>  
当Bean的作用域为singleton的时候,Spring容器中只会存在一个共享的Bean实例,所有对Bean的请求只要id与bean的定义相匹配,则只会返回bean的同一实例。单一实例会被存储在单例缓存中,为Spring的缺省作用域。
2.    prototype:
[html]  view plain  copy
  1. <bean id="userInfo" class="cn.lovepi.UserInfo" scope=" prototype ">bean>  
每次对该Bean请求的时候,Spring IoC都会创建一个新的作用域。
对于有状态的Bean应该使用prototype,对于无状态的Bean则使用singleton
3.    request:
[html]  view plain  copy
  1. <bean id="userInfo" class="cn.lovepi.UserInfo" scope=" request ">bean>  
Request作用域针对的是每次的Http请求,Spring容器会根据相关的Bean的
定义来创建一个全新的Bean实例。而且该Bean只在当前request内是有效的。
4.    session:
[html]  view plain  copy
  1. <bean id="userInfo" class="cn.lovepi.UserInfo" scope=" session ">bean>  
针对http session起作用,Spring容器会根据该Bean的定义来创建一个全新的Bean的实例。而且该Bean只在当前http session内是有效的。
5.    global session:
[html]  view plain  copy
  1. <bean id="userInfo" class="cn.lovepi.UserInfo"scope=“globalSession">bean>  
类似标准的http session作用域,不过仅仅在基于portlet的web应用当中才有意义。Portlet规范定义了全局的Session的概念。他被所有构成某个portlet外部应用中的各种不同的portlet所共享。在global session作用域中所定义的bean被限定于全局的portlet session的生命周期范围之内。

解析来我们详细的介绍每种作用域的功能:
1)singleton作用域
是指在Spring IoC容器中仅存在一个Bean的示例,Bean以单实例的方式存在,单实例模式是重要的设计模式之一,在Spring中对此实现了超越,可以对那些非线程安全的对象采用单实例模式。
接下来看一个示例:
[html]  view plain  copy
  1. 1. <bean id="car" class="cn.lovepi.Car" scope="singleton">bean>  
  2. 2. <bean id="boss1" class="cn.lovepi .Boss” p:car-ref=“car">bean>  
  3. 3. <bean id="boss2" class="cn.lovepi .Boss” p:car-ref=“car">bean>  
  4. 4. <bean id="boss3" class="cn.lovepi .Boss” p:car-ref=“car">bean>  
在1中car这个Bean生命周期声明为了singleton模式,其他的bean如2,3,4引用了这个Bean。在容器中boss1、boss2、boss3的属性都指向同一个bean car。如下图所示:

不仅在容器中通过对象引入配置注入引用相同的car bean,通过容器的getBean()方法返回的实例也指向同一个bean。
在默认的情况下Spring的ApplicationContext容器在启动的时候,自动实例化所有的singleton的bean并缓存于容器当中。

虽然启动时会花费一定的时间,但是他却带来了两个好处:
  1. 对bean提前的实例化操作,会及早发现一些潜在的配置的问题。
  2. Bean以缓存的方式运行,当运行到需要使用该bean的时候,就不需要再去实例化了。加快了运行效率。

2)prototype作用域
是指每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时,相当于执行new Bean()的操作。在默认情况下,Spring容器在启动时不实例化prototype的Bean。
接下来看一个示例:
[html]  view plain  copy
  1. 1. <bean id=“car" class="cn.lovepi.Car" scope=“prototype">bean>  
  2. 2. <bean id=“boss1" class="cn.lovepi.Boss” p:car-ref=“car">bean>  
  3. 3. <bean id=“boss2" class="cn.lovepi.Boss” p:car-ref=“car">bean>  
  4. 4. <bean id=“boss3" class="cn.lovepi.Boss” p:car-ref=“car">bean>  
通过以上的配置,Boss1、boss2、boss3所引用的都是一个新的car的实例,每次通过容器getBean()方法返回的也是一个新的car实例。如下图所示:

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

3)当用户使用Spring的WebApplicationContext时,还可以使用另外3种Bean的作用域,即request,sessionglobalSession

在使用Web应用环境相关的Bean作用域时,必须在Web容器中进行一些额外的配置:

对于低版本Web容器配置:
用户可以使用http过滤器来进行配置并在url-pattern中对所有的页面进行过滤。
[html]  view plain  copy
  1. <filter>  
  2.     <filter-name>requestContextFilterfilter-name>  
  3.     <filter-class>org.springframework.web.filter.RequestContextFilterfilterclass>  
  4. filter>  
  5. <filter-mapping>  
  6.     <filter-name>requestContextFilterfilter-name>  
  7.     <url-pattern>/*url-pattern>  
  8. filter-mapping>  
对于高版本Web容器配置:
使用http请求监听器来进行配置。
[html]  view plain  copy
  1. <listener> <listener-class>  
  2.     org.springframework.web.context.request.RequestContextListener  
  3. listener-class>listener>  
ServletContextListener只负责监听web容器启动和关闭的事件,
RequestContextListener实现了ServletRequestListener监听器接口,该监听器监听http请求事件。
Web服务器接收到的每一次请求都会通知该监听器。

Spring容器的启动和关闭事件由web容器的启动和关闭事件来触发。
但如果Spring容器中的bean需要requestsessionglobalSession作用域的支持,Spring容器本身就必须获得web容器的http请求事件。以http请求事件来驱动bean的作用域的控制逻辑,也就是说通过配置RequestContextListener接口,Spring和web容器的结合就更紧密了。

那么接下来简单的介绍下这三种作用域。
1.request作用域
       对应一个http请求和生命周期,当http请求调用作用域为request的bean的时候,Spring便会创建一个新的bean,在请求处理完成之后便及时销毁这个bean。
2.session作用域
       Session中所有http请求共享同一个请求的bean实例。Session结束后就销毁bean。
3.globalSession作用域
       与session大体相同,但仅在portlet应用中使用。
Portlet规范定义了全局session的概念。请求的bean被组成所有portlet的自portlet所共享。
如果不是在portlet这种应用下,globalSession则等价于session作用域

接下来使用一个实例来演示下bean作用域的概念:
首先我们创建scope包,然后在包中创建两个Java Bean:老板(Boss.java)以及轿车(Car.java)
[java]  view plain  copy
  1. public class Boss {  
  2.     private String name;  
  3.     private Car car;  
  4.   
  5.     public Boss() {  
  6.     }  
  7.   
  8.     public Car getCar() {  
  9.         return car;  
  10.     }  
  11.   
  12.     public void setCar(Car car) {  
  13.         this.car = car;  
  14.     }  
  15.   
  16.     @Override  
  17.     public String toString() {  
  18.         return "Boss{" +  
  19.                 "name='" + name + '\'' +  
  20.                 ", car=" + car +  
  21.                 '}';  
  22.     }  
  23. }  
  24.   
  25. public class Car {  
  26.     private String color;  
  27.     private String brand;  
  28.     private double price;  
  29.   
  30.     public String getColor() {  
  31.         return color;  
  32.     }  
  33.   
  34.     public void setColor(String color) {  
  35.         this.color = color;  
  36.     }  
  37.   
  38.     public String getBrand() {  
  39.         return brand;  
  40.     }  
  41.   
  42.     public void setBrand(String brand) {  
  43.         this.brand = brand;  
  44.     }  
  45.   
  46.     public double getPrice() {  
  47.         return price;  
  48.     }  
  49.   
  50.     public void setPrice(double price) {  
  51.         this.price = price;  
  52.     }  
  53.   
  54. }  
接着我们创建对应的创建配置文件,在conf包中创建一个conf-scope.xml文件:
[html]  view plain  copy
  1. xml version="1.0" encoding="UTF-8" ?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans"  
  3.   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  4.   xmlns:p="http://www.springframework.org/schema/p"  
  5.   xsi:schemaLocation="http://www.springframework.org/schema/beans  
  6.         http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">  
  7.   
  8.   <bean id="car" class="cn.lovepi.chapter03.scope.Car" scope="singleton"/>  
  9.   <bean id="boss1" class="cn.lovepi.chapter03.scope.Boss" p:car-ref="car" />  
  10.   <bean id="boss2" class="cn.lovepi.chapter03.scope.Boss" p:car-ref="car" />  
  11.   <bean id="boss3" class="cn.lovepi.chapter03.scope.Boss" p:car-ref="car" />  
  12. beans>  
最后我们创建测试类进行测试
[java]  view plain  copy
  1. public class Main {  
  2.     public static void main(String[] args){  
  3.         //加载配置文件,启动IoC容器  
  4.         BeanFactory factory=  
  5.                 new FileSystemXmlApplicationContext("src/conf/conf-scope.xml");  
  6.         //获得bean实例  
  7.         Boss boss1=factory.getBean("boss1",Boss.class);  
  8.         Boss boss2=factory.getBean("boss2",Boss.class);  
  9.         Boss boss3=factory.getBean("boss3",Boss.class);  
  10.         System.out.println(boss1.getCar());  
  11.         System.out.println(boss2.getCar());  
  12.         System.out.println(boss3.getCar());  
  13.     }  
  14. }  
然后将作用域设置成singleton和 prototype分别执行程序观察结果。

接下来再来简单的学习下在Spring当中如何自定义作用域
在Spring 2.0中,Spring的Bean作用域机制是可以扩展的,这意味着,你不仅可以使用Spring提供的预定义Bean作用域,还可以定义自己的作用域,甚至重新定义现有的作用域(不提倡这么做,而且你不能覆盖内置的singleton和prototype作用域)

1.首先需要实现自定义Scope类:
      org.springframework.beans.factory.config.Scope
      首先我们得实现config.scope这个接口,要将自定义scope集成到Spring容器当中就必须要实现这个接口。接口中只有两个方法,分别用于底层存储机制获取和删除这个对象。

2.在实现一个或多个自定义Scope并测试通过之后,接下来便是如何让Spring容器来识别新的作用域。我们需要注册自定义Scope类,使用registerScope方法
     ConfigurableBeanFactory.registerScope(String scopeName, Scope scope)
其中:第一个参数是与作用域相关的全局唯一的名称,第二个参数是准备实现的作用域的实例。

3.在实现和注册自定义的scope类之后我们便来使用自定义的Scope:
[html]  view plain  copy
  1. Scope customScope = new ThreadScope();  
  2.      beanFactory.registerScope(“thread”, customeScope);  
  3.      <bean id=“***” class=“***”  scope=“scopeName” />  

你可能感兴趣的:(spring)