草庐IT

Spring IOC官方文档学习笔记(五)之bean的作用域

shame11 2023-04-16 原文

1.Bean的作用域

(1) Bean的作用域即Bean实例的作用范围,Spring支持6种bean的作用域,其中4种只能在web环境中使用,具体如下

作用域 描述
singleton 默认作用域,采用单例模式,Spring只会创建一个该bean实例,每次请求时Spring返回的都是同一个bean实例
prototype 采用原型模式,Spring会创建多个该bean实例,每次请求时Spring返回的都是一个新的bean实例
request 仅用于web环境,Spring会为每次Http请求创建一个新的bean实例
session 仅用于web环境,Spring会为每个Session创建一个新的bean实例
application 仅用于web环境,Spring会为每个ServletContext创建一个新的bean实例
websocket 仅用于web环境,Spring会为每个websocket创建一个新的bean实例

(2) Singleton作用域:如果一个bean的作用域为singleton,那么Spring只会创建出一个该bean实例存储于IOC容器中,之后每次对这个bean的请求都只会返回容器中的那个特定的bean实例,换句话说,对该bean请求返回的结果都是相同的,如下图

基于xml的配置如下

<beans ...>
    <!-- scope属性用于声明bean的作用域,默认值即为singleton -->
    <bean id="exampleA" class="cn.example.spring.boke.ExampleA" scope="singleton"></bean>
</beans>

(3) Prototype作用域:如果一个bean的作用域为prototype,那么我们每次对这个bean的请求都会导致Spring会为我们创建出一个全新的bean实例并返回,换句话说,对该bean请求返回的结果都是不同(全新)的,如下图

基于xml的配置如下

<beans ...>
    <!-- scope属性用于声明bean的作用域-->
    <bean id="exampleA" class="cn.example.spring.boke.ExampleA" scope="prototype"></bean>
</beans>

注意:最好对无状态的bean采用singleton模式,而对有状态的bean采用prototype模式,此外,与其他作用域的bean相比,Spring不会管理prototype bean的完整生命周期,即Spring只管prototype bean的创建,不管它的销毁,prototype bean的初始化回调会被Spring调用,但它的销毁回调却不会,因此,我们在使用prototype bean时,必须清理其所拥有的资源,防止内存泄漏(清理方式:通过自定义bean后置处理器)

(4) request,session,application与websocket这4种作用域只能在web环境中使用,否则会抛出异常,此外,如果当前的servlet环境是Spring MVC环境且请求均通过DispatcherServlet进行处理,那么无需任何其他配置,直接就可以使用这4种作用域,否则,需要进行一些特殊的配置,具体可参考官方文档,此处略

(5) 当我们想要将一个短作用域(例如:request)的bean注入到一个长作用域(例如:singleton)的bean中时,可选择注入这个短作用域bean的AOP代理对象,这是因为通常容器只初始化一次,因此singleton bean的依赖项也只会被注入一次,从而我们所获得的依赖项至始至终都是相同的,在这种情况下,我们就需要一个代理对象,在每次请求时都让这个代理去获取实际对象并进行方法委托,从而执行正确的方法调用,如下

<beans ...>
    <bean id="exampleA" class="cn.example.spring.boke.ExampleA" scope="request">
        <!--添加如下标签后,Spring会创建出该对象的代理对象-->
        <aop:scoped-proxy />
    </bean>
    
    <bean id="exampleB" class="cn.example.spring.boke.ExampleB" scope="singleton">
        <!-- 此时,这里注入的不再是exampleA,而是exampleA的代理对象,每次调用方法,其实都是调用代理对象上的方法,然后代理对象再去获取真正的bean,执行方法调用,这样就避免了依赖项exampleA至始至终都是同一个bean -->
        <property name="exampleA" ref="exampleA"></property>
    </bean>
</beans>

注意:在默认情况下,Spring会为含有aop:scoped-proxy标签的bean采用CGLIB来创建代理对象,如果不想通过这种方式创建代理对象,可通过指定标签aop:scoped-proxy中的属性proxy-target-class为false,来创建基于JDK的代理对象,如下

<beans ...>
    <bean id="exampleA" class="cn.example.spring.boke.ExampleA" scope="request">
        <!--创建基于JDK的动态代理对象-->
        <aop:scoped-proxy proxy-target-class="false"/>
    </bean>
</beans>

2.自定义bean的作用域

(1) bean的作用域是支持扩展的,我们可以自定义作用域,或覆盖spring内置的作用域(其中singleton和prototype不能覆盖),如果要自定义bean的作用域,需要实现Scope接口并将其注入进容器内,便可直接进行使用了,如下所示

//自定义bean的作用域:线程域,即每个线程所获得bean是不同的对象
//首先需实现Scope接口,它主要提供了5大方法,用于获取或设置bean,如下
public class ThreadScope implements Scope {
    //使用ThreadLocal来保存线程私有变量
    private final ThreadLocal<Map<String, Object>> threadBeanMap = new ThreadLocal() {
        @Override
        protected Object initialValue() {
            return new HashMap<>();
        }
    };
    
    /**
     * 用于从当前作用域(此例中是线程)中检索bean
     * @param s 需检索的bean的名称
     * @param objectFactory 如果检索失败,就用它来创建一个bean
     * @return
     */
    @Override
    public Object get(String s, ObjectFactory<?> objectFactory) {
        Object bean = threadBeanMap.get().get(s);
        if(null == bean) {
            bean = objectFactory.getObject();
            threadBeanMap.get().put(s, bean);
        }
        return bean;
    }
    
    /**
     * 从当前作用域中移除bean
     * @param s 需移除的bean的名称
     * @return
     */
    @Override
    public Object remove(String s) {
        return threadBeanMap.get().remove(s);
    }

    /**
     * 用于注册销毁回调,当bean作用域销毁时的清理工作
     * @param s
     * @param runnable
     */
    @Override
    public void registerDestructionCallback(String s, Runnable runnable) {
        
    }
    
    /**
     * 用于解析该作用域的上下文,返回该作用域的一些属性
     * @param s
     * @return
     */
    @Override
    public Object resolveContextualObject(String s) {
        return null;
    }
    
    /**
     * 用于获取该作用域的标识符
     * @return
     */
    @Override
    public String getConversationId() {
        return null;
    }
}

<!-- xml文件配置 -->
<beans ...>
    <!-- 注意这里的scope属性采用了我们自定义的作用域,其名称来源于我们的注册 -->
    <bean id="exampleA" class="cn.example.spring.boke.ExampleA" scope="thread"></bean>
</beans>

//测试
public static void main(String[] args) {
    ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("boke/from.xml");
    //创建出我们自定义的作用域对象
    Scope threadScope = new ThreadScope();
    //使用registerScope方法将自定义的作用域对象注入进容器中,同时指定作用域的名称,在上面的xml配置文件中,就是使用的该名称
    ctx.getBeanFactory().registerScope("thread", threadScope);

    for (int i = 0; i < 2; i++) {
        new Thread(() -> {
            //开启2个线程,可以发现每个线程内的bean都是不同的对象
            System.out.println(Thread.currentThread().getName() + "   " + ctx.getBean("exampleA"));
        }).start();
    }
}

除了上面编程式的配置之外,我们还可以使用基于xml的配置来向容器中注册我们自定义作用域,如下

<beans ...>
    <!-- 注册我们自定义作用域 -->
    <bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
        <property name="scopes">
            <map>
                <!-- 使用entry标签,其中的key代表自定义作用域的名称,值为自定义作用域对象 -->
                <entry key="thread">
                    <bean class="cn.example.spring.boke.scope.ThreadScope"/>
                </entry>
            </map>
        </property>
    </bean>

    <bean id="exampleA" class="cn.example.spring.boke.ExampleA" scope="thread"></bean>
</beans>

有关Spring IOC官方文档学习笔记(五)之bean的作用域的更多相关文章

  1. ruby-on-rails - 如果 Object::try 被发送到一个 nil 对象,为什么它会起作用? - 2

    如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象

  2. Matlab imread()读到了什么 (浅显 当复习文档了) - 2

    matlab打开matlab,用最简单的imread方法读取一个图像clcclearimg_h=imread('hua.jpg');返回一个数组(矩阵),往往是a*b*cunit8类型解释一下这个三维数组的意思,行数、数和层数,unit8:指数据类型,无符号八位整形,可理解为0~2^8的数三个层数分别代表RGB三个通道图像rgb最常用的是24-位实现方法,即RGB每个通道有256色阶(2^8)。基于这样的24-位RGB模型的色彩空间可以表现256×256×256≈1670万色当imshow传入了一个二维数组,它将以灰度方式绘制;可以把图像拆分为rgb三层,可以以灰度的方式观察它figure(1

  3. LC滤波器设计学习笔记(一)滤波电路入门 - 2

    目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称

  4. CAN协议的学习与理解 - 2

    最近在学习CAN,记录一下,也供大家参考交流。推荐几个我觉得很好的CAN学习,本文也是在看了他们的好文之后做的笔记首先是瑞萨的CAN入门,真的通透;秀!靠这篇我竟然2天理解了CAN协议!实战STM32F4CAN!原文链接:https://blog.csdn.net/XiaoXiaoPengBo/article/details/116206252CAN详解(小白教程)原文链接:https://blog.csdn.net/xwwwj/article/details/105372234一篇易懂的CAN通讯协议指南1一篇易懂的CAN通讯协议指南1-知乎(zhihu.com)视频推荐CAN总线个人知识总

  5. 深度学习部署:Windows安装pycocotools报错解决方法 - 2

    深度学习部署:Windows安装pycocotools报错解决方法1.pycocotools库的简介2.pycocotools安装的坑3.解决办法更多Ai资讯:公主号AiCharm本系列是作者在跑一些深度学习实例时,遇到的各种各样的问题及解决办法,希望能够帮助到大家。ERROR:Commanderroredoutwithexitstatus1:'D:\Anaconda3\python.exe'-u-c'importsys,setuptools,tokenize;sys.argv[0]='"'"'C:\\Users\\46653\\AppData\\Local\\Temp\\pip-instal

  6. ruby - 我正在学习编程并选择了 Ruby。我应该升级到 Ruby 1.9 吗? - 2

    我完全不是程序员,正在学习使用Ruby和Rails框架进行编程。我目前正在使用Ruby1.8.7和Rails3.0.3,但我想知道我是否应该升级到Ruby1.9,因为我真的没有任何升级的“遗留”成本。缺点是什么?我是否会遇到与普通gem的兼容性问题,或者甚至其他我不太了解甚至无法预料的问题? 最佳答案 你应该升级。不要坚持从1.8.7开始。如果您发现不支持1.9.2的gem,请避免使用它们(因为它们很可能不被维护)。如果您对gem是否兼容1.9.2有任何疑问,您可以在以下位置查看:http://www.railsplugins.or

  7. ruby-on-rails - "assigns"在 Ruby on Rails 中有什么作用? - 2

    我目前正在尝试学习RubyonRails和测试框架RSpec。assigns在此RSpec测试中做什么?describe"GETindex"doit"assignsallmymodelas@mymodel"domymodel=Factory(:mymodel)get:indexassigns(:mymodels).shouldeq([mymodel])endend 最佳答案 assigns只是检查您在Controller中设置的实例变量的值。这里检查@mymodels。 关于ruby-o

  8. Ruby 等同于 Sphinx 文档生成器? - 2

    Ruby有一些不错的文档生成器,例如Yard、rDoc,甚至Glyph。问题是Sphinx可以做网站、PDF、epub、LaTex等。它在重组文本中完成所有这些事情。在Ruby世界中有替​​代方案吗?也许是程序的组合?如果我也能使用Markdown就更好了。 最佳答案 自1.0版以来,Sphinx有了“域”的概念,它是从Python和/或C以外的语言标记代码实体(如方法调用、对象、函数等)的方法。有一个rubydomain,所以你可以只使用Sphinx本身。您唯一会缺少的(我认为)是Sphinx使用autodoc从源代码自动创建文档

  9. ruby - 我如何学习 ruby​​ 的正则表达式? - 2

    如何学习ruby​​的正则表达式?(对于假人) 最佳答案 http://www.rubular.com/在Ruby中使用正则表达式时是一个很棒的工具,因为它可以立即将结果可视化。 关于ruby-我如何学习ruby​​的正则表达式?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/1881231/

  10. ruby - 字符串文字前面的 * 在 ruby​​ 中有什么作用? - 2

    这段代码似乎创建了一个范围从a到z的数组,但我不明白*的作用。有人可以解释一下吗?[*"a".."z"] 最佳答案 它叫做splatoperator.SplattinganLvalueAmaximumofonelvaluemaybesplattedinwhichcaseitisassignedanArrayconsistingoftheremainingrvaluesthatlackcorrespondinglvalues.Iftherightmostlvalueissplattedthenitconsumesallrvaluesw

随机推荐