写点什么

Spring 5 中文解析核心篇 -IoC 容器之 Bean 作用域

用户头像
青年IT男
关注
发布于: 2020 年 09 月 04 日
Spring 5 中文解析核心篇-IoC容器之Bean作用域

当你创建一个bean的定义时候,你可以创建一个模版(recipe)通过bean定义的类定义去创建一个真实的实例。bean定义是模版(recipe)的概念很重要,因为这意味着,与使用类一样,你可以从一个模版(recipe)创建多个对象实例。



你不仅可以控制要插入到从特定bean定义创建的对象中的各种依赖项和配置值,还可以控制从特定bean定义创建的对象的作用域。这种方法是非常有用的和灵活的,因为你可以选择通过配置创建的对象的作用域,而不必在Java类级别上考虑对象的作用域。bean能够定义部署到一个或多个作用域。Spring框架支撑6种作用域,4种仅仅使用web环境。你可以创建定制的作用域。



下面的表格描述了支撑的作用域:





Spring3.0后,线程安全作用域是有效的但默认没有注册。更多的信息,查看文档 SimpleThreadScope。更多关于怎样去注册和自定义作用域,查看自定义作用域



1.5.1 单例bean作用域



单例bean仅仅只有一个共享实例被容器管理,并且所有对具有与该bean定义相匹配的IDbean的请求都会导致该特定bean实例被Spring容器返回。换一种方式,当你定义一个bean的定义并且它的作用域是单例的时候,Spring IoC容器创建通过bean定义的对象定义的实例。这个单例存储在缓存中,并且对命名bean的所有请求和引用返回的是缓存对象。下面图片展示了单例bean作用域是怎样工作的:





Spring的单例bean概念与在GoF设计模式书中的单例模式不同。GoF单例硬编码对应的作用域例如:只有一个特定类的对象实例对每一个ClassLoader只创建一个对象实例。最好将Spring单例的范围描述为每个容器和每个bean(备注:GoF设计模式中的单例bean是针对不同ClassLoader来说的,而Spring的单例是针对不同容器级别的)。这意味着,如果在单个Spring容器对指定类定义一个beanSpring容器通过bean定义的类创建一个实例。在Spring中单例作用域是默认的。在XML中去定义一个bean为单例,你可以定义一个bean类似下面例子:



<bean id="accountService" class="com.something.DefaultAccountService"/>
<!-- 通过scope指定bean作用域 单例:singleton ,原型:prototype-->
<bean id="accountService" class="com.something.DefaultAccountService" scope="singleton"/>



1.5.2 原型作用域



非单例原型bean的作用域部署结果是在每一次请求指定bean的时候都会创建一个bean实例。也就是,bean被注入到其他bean或在容器通过getBean()方法调用都会创建一个新bean。通常,为所有的无状态bean使用原型作用域并且有状态bean使用单例bean作用域。



下面的图说明Spring的单例作用域:





数据访问对象(DAO)通常不被配置作为一个原型,因为典型的DAO不会维持任何会话状态。我们可以更容易地重用单例图的核心。



下面例子在XML中定义一个原型bean



<bean id="accountService" class="com.something.DefaultAccountService" scope="prototype"/>



与其他作用域对比,Spring没有管理原型bean的完整生命周期。容器将实例化、配置或以其他方式组装原型对象,然后将其交给客户端,无需对该原型实例的进一步记录。因此,尽管初始化生命周期回调函数在所有对象上被回调而不管作用域如何,在原型情况下,配置销毁生命周期回调是不被回调。客户端代码必须清除原型作用域内的对象并释放原型Bean占用的昂贵资源。为了让Spring容器释放原型作用域bean所拥有的资源,请尝试使用自定义beanpost-processor后置处理器,该后处理器包含对需要清理的bean的引用(可以通过后置处理器释放引用资源)。



在某些方面,Spring容器在原型范围内的bean角色是Java new运算符的替代。所有超过该点的生命周期管理都必须由客户端处理。(更多关于在Spring容器中的bean生命周期,查看生命周期回调)



1.5.3 单例bean与原型bean的依赖



当你使用依赖于原型bean的单例作用域bean时(单例引用原型bean),需要注意的是这些依赖项在初始化时候被解析。因此,如果你依赖注入一个原型bean到一个单例bean中,一个新原型bean被初始化并且依赖注入到一个单例bean。原型实例是唯一一个被提供给单例作用域bean的实例。(备注:单例引用原型bean时原型bean只会有一个)



然而,假设你希望单例作用域bean在运行时重复获取原型作用域bean的一个新实例。你不能依赖注入一个原型bean到一个单例bean,因为注入只发生一次,当Spring容器实例化单例bean、解析和注入它的依赖时。如果在运行时不止一次需要原型bean的新实例,查看方法注入



1.5.4 Request, Session, Application, and WebSocket Scopes



requestsessionapplication、和websocket作用域仅仅在你使用Spring 的ApplicationContext实现(例如:XmlWebApplicationContext)时有效。如果你将这些作用域与常规的Spring IoC容器(例如ClassPathXmlApplicationContext)一起使用,则会抛出一个IllegalStateException异常,该错抛出未知的bean作用域。



  • 初始化Web配置

  • Request作用域

  • Session作用域

  • Application作用域

  • 作用域bean作为依赖项



<!--没有<aop:scoped-proxy/> 元素-->
<bean id="userPreferences" class="com.something.UserPreferences" scope="session"/>
<bean id="userManager" class="com.something.UserManager">
<property name="userPreferences" ref="userPreferences"/>
</bean>



在前面的例子中,单例bean (userManager) 被注入一个引用到HTTP  Session作用域的bean (userPreferences)。这个显著点是userManager bean是一个单例bean:这个实例在每个容器值初始化一次,并且它的依赖(在这个例子仅仅一个,userPreferences bean)仅仅被注入一次。这意味着userManager bean运行仅仅在相同的userPreferences对象上(也就是,最初注入的那个)。



当注入一个短生命周期作用域的bean到一个长生命周期作用域bean的时候这个不是我们期望的方式(例如:注入一个HTTP Session作用域的协同者bean作为一个依赖注入到单例bean)。相反,你只需要一个userManager对象,并且在HTTP会话的生存期内,你需要一个特定于HTTP会话的userPreferences对象。因此,容器创建一个对象,该对象公开与UserPreferences类完全相同的公共接口(理想地,对象是UserPreferences实例),可以从作用域机制(HTTP 请求,Session,以此类推)获取真正的UserPreferences对象。容器注入这个代理对象到userManager bean,这并不知道此UserPreferences引用是代理。在这个例子中,当UserManager实例调用在依赖注入UserPreferences对象上的方法时,它实际上是在代理上调用方法。然后代理从(在本例中)HTTP会话中获取实际的UserPreferences对象,并将方法调用委托给检索到的实际UserPreferences对象。



因此,在将request-scopedsession-scoped的bean注入到协作对象中时,你需要以下(正确和完整)配置,如以下示例所示:



<bean id="userPreferences" class="com.something.UserPreferences" scope="session">
<aop:scoped-proxy/>
</bean>
<bean id="userManager" class="com.something.UserManager">
<property name="userPreferences" ref="userPreferences"/>
</bean>



  • 代理类型选择



1.5.5 自定义作用域



bean作用域机制是可扩展的。你可以定义你自己的作用域或者甚至重定义存在的作用域,尽管后者被认为是不好的做法,你不能覆盖内置的单例和原型范围。



  • 创建一个自定义作用域

  • 使用自定义作用域



作者



个人从事金融行业,就职过易极付、思建科技、某网约车平台等重庆一流技术团队,目前就职于某银行负责统一支付系统建设。自身对金融行业有强烈的爱好。同时也实践大数据、数据存储、自动化集成和部署、分布式微服务、响应式编程、人工智能等领域。同时也热衷于技术分享创立公众号和博客站点对知识体系进行分享。



博客地址: http://youngitman.tech



CSDN: https://blog.csdn.net/liyong1028826685



微信公众号: 



发布于: 2020 年 09 月 04 日阅读数: 65
用户头像

青年IT男

关注

站在巨人肩上看得更远! 2018.04.25 加入

从事金融行业,就职过易极付、思建科技、网约车平台等一流技术团队,目前就职于银行负责支付系统建设。对金融行业有强烈的爱好。实践大数据、数据存储、自动化集成和部署、分布式微服务、响应式编程、人工智能等领域

评论

发布
暂无评论
Spring 5 中文解析核心篇-IoC容器之Bean作用域