• Spring bean的作用域


    一、spring bean的作用域之间有什么区别

    类别 说明
    singleton 在Sring IOC容器中仅存在一个Bean实例,Bean以单例方式存在,默认值
    prototype 每次从容器中调用Bean时,都会返回一个新的实例,即每次调用getBean()时,相当于执行new XxxBean()
    request 每次HTTP请求都会创建一个新的Bean,该作用域仅适用于WebApplicationContext环境
    session 同一个HTTP Session共享一个Bean,不同Session使用不同Bean,仅适用于WebApplicationContext环境
    globalSession 一般用于Portlet应用环境,该作用域仅适用于WebApplicationContext环境

     

     

     

     

     

     

     

    五种作用域中,request、session和global session三种作用域仅在基于web的应用中使用(不必关心你所采用的是什么web应用框架),只能用在基于web的Spring ApplicationContext环境。

      (1)当一个bean的作用域为Singleton,那么Spring IoC容器中只会存在一个共享的bean实例,并且所有对bean的请求,只要id与该bean定义相匹配,则只会返回bean的同一实例。Singleton是单例类型,就是在创建起容器时就同时自动创建了一个bean的对象,不管你是否使用,他都存在了,每次获取到的对象都是同一个对象。注意,Singleton作用域是Spring中的缺省作用域。要在XML中将bean定义成singleton,可以这样配置:

    <bean id="book" class="cn.lc.beans.Book" scope="singleton">
            <property name="id" value="1"></property>
            <property name="title" value="西游记"></property>
            <property name="price" value="38.78"></property>
    </bean>

      (2)当一个bean的作用域为Prototype,表示一个bean定义对应多个对象实例。Prototype作用域的bean会导致在每次对该bean请求(将其注入到另一个bean中,或者以程序的方式调用容器的getBean()方法)时都会创建一个新的bean实例。Prototype是原型类型,它在我们创建容器的时候并没有实例化,而是当我们获取bean的时候才会去创建一个对象,而且我们每次获取到的对象都不是同一个对象。根据经验,对有状态的bean应该使用prototype作用域,而对无状态的bean则应该使用singleton作用域。在XML中将bean定义成prototype,可以这样配置:

    <bean id="book" class="cn.lc.beans.Book" scope="prototype">
            <property name="id" value="1"></property>
            <property name="title" value="西游记"></property>
            <property name="price" value="38.78"></property>
    </bean>

      (3)当一个bean的作用域为Request,表示在一次HTTP请求中,一个bean定义对应一个实例;即每个HTTP请求都会有各自的bean实例,它们依据某个bean定义创建而成。该作用域仅在基于web的Spring ApplicationContext情形下有效。针对每次HTTP请求,Spring容器会根据loginAction bean的定义创建一个全新的LoginAction bean实例,且该loginAction bean实例仅在当前HTTP request内有效,因此可以根据需要放心的更改所建实例的内部状态,而其他请求中根据loginAction bean定义创建的实例,将不会看到这些特定于某个请求的状态变化。当处理请求结束,request作用域的bean实例将被销毁。考虑下面bean定义:

    <bean id="book" class="cn.lc.beans.Book" scope="request">
            <property name="id" value="1"></property>
            <property name="title" value="西游记"></property>
            <property name="price" value="38.78"></property>
    </bean>

      (4)当一个bean的作用域为Session,表示在一个HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。针对某个HTTP Session,Spring容器会根据userPreferences bean定义创建一个全新的userPreferences bean实例,且该userPreferences bean仅在当前HTTP Session内有效。与request作用域一样,可以根据需要放心的更改所创建实例的内部状态,而别的HTTP Session中根据userPreferences创建的实例,将不会看到这些特定于某个HTTP Session的状态变化。当HTTP Session最终被废弃的时候,在该HTTP Session作用域内的bean也会被废弃掉。考虑下面bean定义:

    <bean id="book" class="cn.lc.beans.Book" scope="session">
            <property name="id" value="1"></property>
            <property name="title" value="西游记"></property>
            <property name="price" value="38.78"></property>
    </bean>

      (5)当一个bean的作用域为Global Session,表示在一个全局的HTTP Session中,一个bean定义对应一个实例。典型情况下,仅在使用portlet context的时候有效。该作用域仅在基于web的Spring ApplicationContext情形下有效。考虑下面bean定义:

    <bean id="book" class="cn.lc.beans.Book" scope="globalSession">
            <property name="id" value="1"></property>
            <property name="title" value="西游记"></property>
            <property name="price" value="38.78"></property>
    </bean>

       SpringBoot项目中  @Scope注解

        @Scope默认是单例模式,即scope="singleton"。

        另外scope还有prototype、request、session、global session作用域。scope="prototype"多例

        例:@scope的原型模式,即:@scope("prototype")

    二、面试题:Spring的Controller是单例还是多例,怎么保证并发的安全

    答案:

    controller默认是单例的,不要使用非静态的成员变量,否则会发生数据逻辑混乱。正因为单例所以不是线程安全的。

    我们下面来简单的验证下:

    import org.springframework.context.annotation.Scope;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    
    @Controller
    public class ScopeTestController {
    
        private int num = 0;
    
        @RequestMapping("/testScope")
        public void testScope() {
            System.out.println(++num);
        }
    
        @RequestMapping("/testScope2")
        public void testScope2() {
            System.out.println(++num);
        }
    
    }

    我们首先访问 http://localhost:8080/testScope,得到的答案是1;

    然后我们再访问 http://localhost:8080/testScope2,得到的答案是 2。

    得到的不同的值,这是线程不安全的。

    接下来我们再来给controller增加作用多例 @Scope("prototype")

    import org.springframework.context.annotation.Scope;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    
    @Controller
    @Scope("prototype")
    public class ScopeTestController {
    
        private int num = 0;
    
        @RequestMapping("/testScope")
        public void testScope() {
            System.out.println(++num);
        }
    
        @RequestMapping("/testScope2")
        public void testScope2() {
            System.out.println(++num);
        }
    
    }

    我们依旧首先访问 http://localhost:8080/testScope,得到的答案是1;

    然后我们再访问 http://localhost:8080/testScope2,得到的答案还是 1。

    相信大家不难发现 :

    单例是不安全的,会导致属性重复使用。

    解决方案

    • 不要在controller中定义成员变量。
    • 万一必须要定义一个非静态成员变量时候,则通过注解@Scope(“prototype”),将其设置为多例模式。
    • 在Controller中使用ThreadLocal变量

    补充说明

    spring bean作用域有以下5个:

    singleton: 单例模式,当spring创建applicationContext容器的时候,spring会欲初始化所有的该作用域实例,加上lazy-init就可以避免预处理;

    prototype: 原型模式,每次通过getBean获取该bean就会新产生一个实例,创建后spring将不再对其管理;

    (下面是在web项目下才用到的)

    request: 搞web的大家都应该明白request的域了吧,就是每次请求都新产生一个实例,和prototype不同就是创建后,接下来的管理,spring依然在监听;

    session: 每次会话,同上;

    global session: 全局的web域,类似于servlet中的application。

  • 相关阅读:
    curd_4
    curd_2
    Python Regex库的使用
    Python Assert 确认条件为真的工具
    Python Regex库的使用(2)
    Python lambda的用法
    Python 列表综合
    with ss(date,date2) (select * from sysdummy1) select * from ss
    延迟执行函数
    ObjectiveC 的基本数据类型、数字、字符串和集合等介绍
  • 原文地址:https://www.cnblogs.com/lc0605/p/13305638.html
Copyright © 2020-2023  润新知