spring Bean的作用域

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

在spring容器当中,一共提供了5中作用域类型,在配置文件中,通过属性scope来设置bean的作用域返回,针对不同作用域,

1、singleton:<bean id = "userInfo" class="com.zto.xxx" scope="singleton"/>
一个bean的作用域为scope的时候。spring ioc容器中,只会存在一个共享的bean实例,而且所有对bean的请求只要id与该bean定义相匹配,则只会bean的同一实例,那么也就说当吧一个bean定义设置为singleton,spring ioc 容器只会创建一个唯一的实例,这个单一实例会被存出来单例缓存当中,并且所有针对该bean的后续请求和引用,都将返回之前缓存的单例实例,假如单个spring容器当中,定义某个指定class的bean,那么spring容器将会创建一个且仅有一个由该bean定义知道类的实例。

2、prototype:<bean id="userInfo" class="com.zto.**" scope="prototype"/>
该配置将对bean请求的时候,spring ioc 的容器都会创建一个新的bean的实例,根据经验,对于有状态的bean应该使用prototype,而对于无状态的bean则应该使用singleton。


3、request:<bean id="userInfo" class="com.zto.***" scope="request"/>
request的作用域,针对每一次的http的请求,spring容器会根据相关的bean的定义来创建一个全新的bean实例,而且该实例仅在当前HTTP request 是有效的,因此可以更加放心更改所创建实例的内部状态,而其他请求中,根据该bean定义创建的实例,将不会看到这些特定域某个请求的状态变化。

4、seeison <bean id="userInfo" class="com.zto.***" scope="seeison "/>
这个作用域作用某个http seeision 来起作用的,spring 容器根据该bean的定义,创建一个全新的该bean实例,且该bean仅在当前 http seesion 实例是有效的,与request相同,可以根据需要,放心的更改所创建的实例的内部状态,而别的http seesion中根据该bean创建的实例,将不会看到特定域,某个http seesion 的状态,当被废弃的时候,在该htpp seesion 作用域内所创建的bean的实例也会同样被废弃掉。

5、global seesion<bean id="userInfo" class="com.zto.***" scope="globalSeesion"/>
该作用域类似于标准的http seesion作用域,不过仅仅在基于  portlet web应用中才有意义 。Portlet规范定义
了全局Session的概念,它被所有构成某个 portlet web应用的各种不同的portlet所共享。在global session作用域中定义的bean被限定于全局portlet Session的生命周期范围内。如果你在web中使用global session作用域来标识bean,那么web会自动当成session类型来使用。

单实例模式是重要的设计模式之一,在传统的应用开发中,我们需要手工为每一个单实例类编写特定的代码,在这种情况下,它的业务代码和单实例代码紧密的耦合在一起,而spring以容器的方式,天然的实现单实例功能,无需编写特殊的代码,仅通过配置就可以实现单实例模式。在一般情况下,无状态或状态不可变更的类是合适用单实例模式的。不过spring对此实现超越,在传统的开发过程当中,对于doo类只有collercion这个非线程的安全的变量,往往没有采用单实例模式,而在spring环境下,对于doo类都可以采用单实例的模式,因为spring利用aop和 LocalThread,对于非线程的变量,进行了特殊的处理,使用这些非线程安全的类变成了线程安全的类。因为spring的这一个超越,所以在实际的应用的当中,大部分的bean都能以单实例的方式来运行,这也是为什么将bean的默认作用域定于为singleton。singleton作用域在springioc当中只能有一个实例。

<bean id="car" class="com.zto.car" scope="signleton"/>
<bean id="boss1" class="com.zto.boss1" p:car-ref="car"/>
<bean id="boss2 class="com.zto.boss2" p:car-ref="car"/>
<bean id="boss3" class="com.zto.boss3" p:car-ref="car"/>


如图,不但在配置文件中通过对象引入注入的car,引用相同的carbean,然后通过容器的getbean这种方式,返回的实例也指向同一个Bean,在默认的情况下,spring的 applicatio容器在启动的时候,自动实例化signletonbean,并缓存容器当中,虽然启动会花费时间,但是带来了两个好处,第一,对bean提前的实例化操作,为及早发现一些潜在的配置问难题,第二,并以缓存的方式保存,当运行时使用该bean的时候,就没必要在实例化。这样加快了相关的运行效率。如果用户不需要在容器启动时,不实例化singleton这些bean。那么可以通过   lazy-init 属性来进行控制,而   lazy-init =true的bean,在某些情况下,依旧会实例化。如果该bean被其他需要提前实例化的bean使用到的时候,spring也将忽略延迟实例化的设置。


prototype作用域是之每次从spring调用bean的时候,都将返回一个新的实例,即每次调用getbean()时候,相对于执行new bean()的操作,在默认情况下,spring容器在启动时不实例化prototype。
不实例化prototype的bean:
<bean id="car" class="com.zto.car" scope="protetype"/>
<bean id="boss1" class="com.zto.boss1" p:car-ref="car"/>
<bean id="boss2 class="com.zto.boss2" p:car-ref="car"/>
<bean id="boss3" class="com.zto.boss3" p:car-ref="car"/>

boss1、boss2、boss3引用的都是新的实例,每次通过容器,getbean()方法返回的都是一个新的car的实例



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

当用户spring使用webapplicationcontext的时候,还可以使用另外3中作用于,即request、session,globalSeesion。在使用Web应用应用环境相关的bean作用域时候,必须在Web容器中进行一些额外的配置;
低版本web容器配置
<filter>
    <filter-name>requsetContextFilter</filter-name>
    <filter-class>org.springframework.web.filter.RequestContextFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>requestContextFiler</filter-nama>
    <servlet-name>/*</servlet-name>
</filter-mapping>
用户可以使用http请求过滤器来进行相关的配置。同时在url- pattern  中对所有url进行过滤拦截。

高版本
<listener><listener-class>
    org.springframework.web.context.request.RequestContextListener
</listener-class></listener>

我们需要利用http请求监听器来进行相关的配置。其实在整合spring容器时使用context-thread-listener
它实现servlet-context-listener 监听器接口。servlet-listener只负责监听web容器启动和关闭,而request-context-listenner实现了servlet=reqiest-listener监听器接口,该监听器监听http请求事件,web服务接受的每一次请求都同此该监听器,spring容器启动和关闭操作,由web容器启动和关闭事件来出发,但spring容器的bean需要request,session 和globalsession的支持,spring容器本身就必须获得web容器的http请求事件,以http请求事件来驱动的bean的作用域的逻辑,也就是说通过配置 request-context-listener,spring容器和web容器结合就更加紧密了,spring容器能够对web容器中的风吹草动,洞若观火。以便实施
web相应的bean的作用域的控制,当然spring 完全可以提供一个既实现 servlet-context-listener,又实现servlet-context-listener接口的监听器,这样我们仅需要配置一次就可以了。spring将两种分开的原因,可能出于两方面原因,第一,版本兼容问题。第二,这三种新增的bean的作用域,使用场合不多,往往用户不需要使用这些新增的bean的作用域

Web应用环境的作用于
request的作用域,顾名思义,requst的作用域对应的是一个http请求,和生命周期。那么他的配置其实就是利用scope属性=request来进行配置的。那么这样每次http请求调用到比如上面提到的car的这种bean的时候,spring容器会创建一个新的car bean,请求处理完毕之后就及时销毁这个bean。
session作用域,假设将car的作用域调整为seeseion类型。这样的配置之后car bean的作用域将横跨整个httpseesion。session所有http请求都将共享同一个car的实例bean。但是当httpsession结束之后,这个实例也被销毁。
globalsession作用域,类似sesseion作用域,仅在protlet这种web应用中使用,protlet规范定义了全局session的概念,它被组成protlet web应用的所有子protlet 所共享,如果不在protlet web应用环境之下,globalsession同session一样。

示例代码
创建一个boss类和一个car类,其中boss类引用的到car类

package com.zto.test1;

public class Boss {
    private String name;
    private Car car;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Car getCar() {
        return car;
    }

    public void setCar(Car car) {
        this.car = car;
    }

    public Boss() {
        super();
    }

    @Override
    public String toString() {
        return "Boss [name=" + name + ", car=" + car + "]";
    }
}
package com.zto.test1;

public class Car {
    private String color;
    private String brand;
    private double price;

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }


}

创建一个application-scope.xml 的配置文件
car类声明为singleton模式。
在每一个boss类中都引用car类
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:task="http://www.springframework.org/schema/task"
    xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" 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
     http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
        http://www.springframework.org/schema/task  
     http://www.springframework.org/schema/task/spring-task-4.0.xsd
     http://www.springframework.org/schema/context  
     http://www.springframework.org/schema/context/spring-context-4.1.xsd
      http://code.alibabatech.com/schema/dubbo
        http://code.alibabatech.com/schema/dubbo/dubbo.xsd
        ">

    <bean id="car" class="com.zto.test1.Car" scope="singleton" />
    <bean id="boss1" class="com.zto.test1.Boss" p:car-ref="car" />
    <bean id="boss2" class=" com.zto.test1.Boss" p:car-ref="car" />
    <bean id="boss3" class="com.zto.test1.Boss" p:car-ref="car" />
</beans>

编写测试Main:
package com.zto.test1;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.zto.HelloWorld;

public class Main1 {

    public static void main(String[] args) {
        syaHelloWroldById();

    }

    public static void syaHelloWroldById() {
        BeanFactory beanFactory = new ClassPathXmlApplicationContext(
                "classpath*:application-scope.xml");
        Boss boss1 = beanFactory.getBean("boss1",Boss.class);
        Boss boss2 = beanFactory.getBean("boss2",Boss.class);
        Boss boss3 = beanFactory.getBean("boss3",Boss.class);
        System.out.println(boss1.getCar());
        System.out.println(boss2.getCar());
        System.out.println(boss3.getCar());
    }

}

run as:


打印出来都是同一个内存地址,表示所有的创建的boss类都引用的是同一个car实例。

修改为prototype 模式

scope=prototype

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:task="http://www.springframework.org/schema/task"
    xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" 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
     http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
        http://www.springframework.org/schema/task  
     http://www.springframework.org/schema/task/spring-task-4.0.xsd
     http://www.springframework.org/schema/context  
     http://www.springframework.org/schema/context/spring-context-4.1.xsd
      http://code.alibabatech.com/schema/dubbo
        http://code.alibabatech.com/schema/dubbo/dubbo.xsd
        ">

    <!-- <bean id="car" class="com.zto.test1.Car" scope="singleton" />
    <bean id="boss1" class="com.zto.test1.Boss" p:car-ref="car" />
    <bean id="boss2" class=" com.zto.test1.Boss" p:car-ref="car" />
    <bean id="boss3" class="com.zto.test1.Boss" p:car-ref="car" /> -->
    <bean id="car" class="com.zto.test1.Car" scope="prototype" />
    <bean id="boss1" class="com.zto.test1.Boss" p:car-ref="car" />
    <bean id="boss2" class=" com.zto.test1.Boss" p:car-ref="car" />
    <bean id="boss3" class="com.zto.test1.Boss" p:car-ref="car" />
</beans>

再次运行测试代码




打印出来的地址不同,说明每一个boss实例引用的都是不同的car实例

当我们将bean的作用域定于为prototype这种类型的时候,那么他们将会为每一个获取bean的方法来创建一个新的实例,但是当我们把scope定义为signleton,在整个springioc容器当中,有切仅有一个bean的实例


自定义作用域,在spring2.0当中,spring的bean的作用域机制是可以扩展的,这意味着,你不仅可以使用spring提供的预定义的bean的作用域,而且可以定义自己的作用域,甚至重新顶一个现有的作用域(不提倡这么做,而且你不能覆盖内置的singleton和prototype作用域)

实现自定义作用域类:
首先需要实现自定scope这个接口,
    org.springframework.beans.factory.config.Scope.
本身非常简单,只有两个方法。分别用于底层存储机制获取和删除对象。
注册自定义scope类:
    让spring容器识别我们新的作用域
     ConfigurableBeanFactory.registerScope(String scopeName,Scope Scope)  
使用自定义的Scope:
    Scope customScope = new ThreadScope();    
    beanFactory.registerScope("thread",customeScope);
    <bean id ="***" class="**" scope="scopeName"/>






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值