Spring中Bean的作用域—Spring官方文档系列
一、单例作用域
单例bean只会产生一个实例,并且所有对具有与该bean定义匹配的id或id的bean的请求Spring容器都只会返回一个实例。换句话说,当你定义一个bean定义,并且作为一个singleton,Spring IoC容器创建该bean所定义的对象的一个实例。 这个单个实例存储在这样的单例bean的缓存中,所有后续的请求和引用返回缓存的这个对象。 Spring的单例bean的概念不同于Singleton模式,和在Gang of Four(GoF)模式的书中定义的不同的地方。 GoF Singleton硬编码对象的范围,使得每一个类加载器内会产生单例类的一个实例。 Spring单例的作用域恰如其名:*一个容器对应一个bean *。 这意味着如果你在一个Spring容器中为一个特定的类定义了一个bean,那么Spring容器将创建一个实例,并且只有一个由该bean定义定义的类的实例。 *singleton scope (单例作用域)*是Spring 中的默认配置。 要将bean定义为XML中的单例,您可以编写,例如:
<bean id="accountService" class="com.foo.DefaultAccountService"/> <!-- 和下面的写法等价,因为单例作用域是默认的 --> <bean id="accountService" class="com.foo.DefaultAccountService" scope="singleton"/>
还可以通过注解定义,例如:
- import java.util.HashSet;
- import java.util.Set;
- import org.springframework.context.annotation.Scope;
- import org.springframework.stereotype.Controller;
- import org.springframework.web.bind.annotation.RequestMapping;
- @Controller
- @RequestMapping(value = "/spring")
- @Scope("prototype")
- public class SpringDemo {
- private static Set<Object> set = new HashSet<>();
- @RequestMapping(value="test")
- public void test(){
- set.add(this);
- System.out.println(set.size());
- }
- }
二、prototype 作用域
设置bean作用域为prototype,就是非单例,bean部署的prototype scope导致每次对该特定bean的请求时都会创建新的bean实例。 也就是说,bean被注入到另一个bean中,或者通过容器上的
getBean()方法调用来请求它。 通常,对所有有状态bean使用prototype scope,对无状态bean使用singleton scope。下图说明了Spring prototype scope。 数据访问对象(DAO)通常不被配置为 prototype scope,因为通常DAO不持有任何会话状态; 如何在XML中定义prototype bean:
< 1bb8b bean id="accountService" class="com.foo.DefaultAccountService" scope="prototype"/>
与其他作用域相比,Spring不管理prototype bean的完整生命周期:容器实例化,配置和以其他方式组装原型对象,并将其传递给客户端,没有原型实例的进一步记录。因此,尽管初始化生命周期回调方法在所有对象上被调用,不管范围如何,在prototype的情况下,配置的销毁生命周期回调被不调用。客户端代码必须清理prototype作用域对象,并释放prototype bean持有的昂贵资源。要使Spring容器释放prototype作用域的bean所拥有的资源,请尝试使用自定义bean post-processor,它持有需要被清理bean的引用。在某些方面,Spring容器在prototype作用域bean中的作用是Java“new”运算符的替代。经由该点的所实例化的bean的所有生命周期管理必须由客户端处理。
Singleton beans 与prototype-bean依赖关系:
这里简单说一下 Singleton bean 与 prototype bean 之间的依赖关系 :在大多数应用场景中,容器中的大多数bean都是 singletons。 当单例bean需要与另一个单例bean协作或非单例bean需要与另一个非单例bean协作时,通常通过将一个bean定义为另一个的属性来处理依赖关系。不过对于具有不同生命周期的bean 来说这样做就会出现问题。 假设单例bean A需要使用非单例(原型)bean B,也许在A上的每个方法调用上。容器仅创建单例bean A一次,因此只有一次机会来设置属性。 这样就没办法 在需要的时候每次让容器为bean A提供一个新的bean B实例。
解决方案是放弃一些控制的反转。 您可以通过实现以下操作来 定义一个类实现
ApplicationContextAware接口,并通过对容器调用getBean(“B”)在每次bean A需要它时调用SpringContextHolder的静态方法getBean()来获取实例B, 如下:
@Service() public class SpringContextHolder implements ApplicationContextAware, DisposableBean { private static ApplicationContext applicationContext = null; /** * 实现ApplicationContextAware接口, 注入Context到静态变量中. */ @Override public void setApplicationContext(ApplicationContext applicationContext) { if (SpringContextHolder.applicationContext != null) { System.out.println("SpringContextHolder中的ApplicationContext被覆盖, 原有ApplicationContext为:" + SpringContextHolder.applicationContext); } System.out.println("Spring容器启动,将容器实例注入到SpringContextHolder实例bean中"); SpringContextHolder.applicationContext = applicationContext; } /** * 实现DisposableBean接口,重写destroy方法,相当于destroy-method,bean被销毁的时候被调用, * 实现在Context关闭时清理静态变量的目的 * 令:还有InitializingBean接口,重写afterPropertiesSet方法,相当于init-method,bean被实例化的时候被调用 */ @Override public void destroy() throws Exception { applicationContext = null; } /** * 取得存储在静态变量中的ApplicationContext. */ public static ApplicationContext getApplicationContext() { return applicationContext; } /** * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型. */ @SuppressWarnings("unchecked") public static <T> T getBean(String name) { return (T) applicationContext.getBean(name); } /** * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型. */ public static <T> T getBean(Class<T> requiredType) { return applicationContext.getBean(requiredType); } }
三、Request, session, application, and WebSocket scopes
request,session,global session作用域,只有在spring web ApplicationContext的实现中(比如XmlWebApplicationContext)才会起作用,若在常规Spring IoC容器中使用,比如ClassPathXmlApplicationContext中,就会收到一个异常IllegalStateException来告诉你不能识别的bean作用域Request scope
[p]考虑下面这种bean定义:<bean id="loginAction" class="com.foo.LoginAction" scope="request"/>Spring容器通过对每个HTTP请求使用
loginActionbean定义来创建一个
LoginActionbean的新实例。 也就是说,
loginActionbean的作用域是在HTTP请求级别。 您可以根据需要更改创建的实例的内部状态,因为根据此
loginAcitonbean定义创建的其他bean实例并不会看到这些状态的改变;他们为各自的request拥有。 当reqeust完成处理,request作用的bean就被丢弃了。当使用注解驱动组件或Java Config时,
@RequestScope注解可以用于将一个组件分配给
request范围。[/p]
@RequestScope @Component public class LoginAction { // ... }
Session scope
考虑下面这种bean的xml配置定义:
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>
Spring容器通过对单个HTTP会话的生命周期使用
userPreferencesbean定义来创建
UserPreferencesbean的新实例。 换句话说,
userPreferencesbean在HTTP
Session级别有效地作用域。 和
request-scopedbean相类似,可以改变bean实例的内部状态,不管bean创建了多少实例都可以,要知道,使用相同的
userPreferences定义创建的其他的bean实例看不到这些状态的改变,因为他们都是为各自的HTTP Session服务的。 当HTTP
Session最终被丢弃时,被限定为该特定HTTP
Session的bean也被丢弃。当使用注解驱动组件或Java Config时,
@SessionScope注解可用于将一个组件分配给
session范围。
@SessionScope @Component public class UserPreferences { // ... }
Application scope
考虑下面这种bean定义:
<bean id="appPreferences" class="com.foo.AppPreferences" scope="application"/>
Spring容器通过对整个web应用程序使用
appPreferencesbean定义来创建一个
AppPreferencesbean的新实例。 也就是说,
appPreferencesbean是在
ServletContext级别定义的,存储为一个常规的
ServletContext属性。 这有点类似于Spring单例bean,但在两个重要方面有所不同:1、他是每一个
ServeltContext一个实例,而不是Spring
ApplicationContext范围。2、它是直接暴露的,作为
ServletContext属性,因此可见。当使用注解驱动组件或Java Config时,
@ApplicationScope注解可用于将一个组件分配给
application作用域。
@ApplicationScope @Component public class AppPreferences { // ... }
不同级别作用域bean之间依赖
Spring IoC容器不仅管理对象(bean)的实例化,还管理协作者(或依赖关系)。 如果要将(例如)一个HTTP请求作用域bean注入到较长期作用域的另一个bean中,您可以选择注入一个AOP代理来代替该作用域bean。 也就是说,您需要注入一个代理对象,该对象暴露与作用域对象相同的公共接口,但也可以从相关作用域(例如HTTP请求)查找实际目标对象,并将方法调用委托给真实对象。
你也可以在定义为“singleton”的bean之间使用<aop:scoped-proxy/>,然后通过引用一个可序列化的中间代理,因此能够在反序列化时重新获得目标单例bean。
当对
prototypescope的bean声明
<aop:scoped-proxy/>时,共享代理上的每个方法调用都将导致创建一个新的目标实例,然后将该调用转发给该目标实例。
这里对<aop:scoped-proxy/> 不做详细解释 。
<?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:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- an HTTP Session-scoped bean exposed as a proxy --> <bean id="userPreferences" class="com.foo.UserPreferences" scope="session"> <!-- instructs the container to proxy the surrounding bean --> <aop:scoped-proxy/> </bean> <!-- a singleton-scoped bean injected with a proxy to the above bean --> <bean id="userService" class="com.foo.SimpleUserService"> <!-- a reference to the proxied userPreferences bean --> <property name="userPreferences" ref="userPreferences"/> </bean> </beans>
自定义作用域 认为是不推荐的做法,并且,你不能覆盖内置的
singleton和
prototype作用域,有兴趣的同学可以百度。此文章参考
- Spring容器扩展(BeanPostProcessor,BeanFactoryPostProcessor,FactoryBean)—Spring官方文档系列
- Spring系列:第三篇-Spring容器三种实例化bean的方式以及Bean的作用域
- 【Spring Boot&& Spring Cloud系列】单点登录SSO之OAuth2官方开发文档翻译
- Android官方开发文档Training系列课程中文版:如何避免ANR?
- Android官方开发文档Training系列课程中文版:手势处理之记录手指移动的轨迹
- Solr入门之官方文档6.0阅读笔记系列(五) 第二部分结束
- spring官方文档(中文篇)
- Android官方开发文档Training系列课程中文版:创建自定义View之View的绘制
- [下载]黑莓BlackBerry开发官方文档系列
- Android官方开发文档Training系列课程中文版:调用相机之简单拍照
- Spring 4 官方文档学习(⑤)WebSocket支持
- Android官方开发文档Training系列课程中文版:电池续航时间优化之按需开启广播接收器
- JAVA帮助文档全系列 JDK1.5 JDK1.6 JDK1.7 官方中英完整版下载
- Spring中bean的作用域
- Android官方开发文档Training系列课程中文版:线程执行操作之定义线程执行代码
- Spring 4.x官方参考文档中文版——第21章 Web MVC框架(5)
- Spring 4.x官方参考文档中文版——第21章 Web MVC框架(13)
- Spring核心技术(五)——Spring中Bean的作用域
- Spring JMS 官方文档学习
- 《Spring 5 官方文档》39. 创建可扩展的XML