zoukankan      html  css  js  c++  java
  • 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。

  • 相关阅读:
    Linux 下的类似Windows下Everything的搜索工具
    windows和linux环境下制作U盘启动盘
    程序调试手段之gdb, vxworks shell
    LeetCode 1021. Remove Outermost Parentheses (删除最外层的括号)
    LeetCode 1047. Remove All Adjacent Duplicates In String (删除字符串中的所有相邻重复项)
    LeetCode 844. Backspace String Compare (比较含退格的字符串)
    LeetCode 860. Lemonade Change (柠檬水找零)
    LeetCode 1221. Split a String in Balanced Strings (分割平衡字符串)
    LeetCode 1046. Last Stone Weight (最后一块石头的重量 )
    LeetCode 746. Min Cost Climbing Stairs (使用最小花费爬楼梯)
  • 原文地址:https://www.cnblogs.com/lc0605/p/13305638.html
Copyright © 2011-2022 走看看