Spring中Bean的作用域—Spring官方文档系列

原创 2018年04月15日 18:17:42
spring bean的作用域分为以下五种:
1、singleton(默认模式):单例,指一个bean容器中只存在一份
2、prototype:每次请求(每次使用)创建新的实例,destroy方式不生效
3、request:每次http请求创建一个实例且仅在当前request内有效
4、session:同上,每次http请求创建,当前session中有效
5、global session:基于portlet的web中有效(portlet定义了global sessio),如果在web中,同session

一、单例作用域

            

单例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"/>

    还可以通过注解定义,例如:

  1. import java.util.HashSet;  
  2. import java.util.Set;  
  3.   
  4. import org.springframework.context.annotation.Scope;  
  5. import org.springframework.stereotype.Controller;  
  6. import org.springframework.web.bind.annotation.RequestMapping;  
  7.   
  8. @Controller  
  9. @RequestMapping(value = "/spring")  
  10. @Scope("prototype")  
  11. public class SpringDemo {  
  12.   
  13.     private static Set<Object> set = new HashSet<>();  
  14.   
  15.     @RequestMapping(value="test")  
  16.     public void test(){  
  17.         set.add(this);  
  18.         System.out.println(set.size());  
  19.     }  

二、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:

<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

考虑下面这种bean定义:

<bean id="loginAction" class="com.foo.LoginAction" scope="request"/>

Spring容器通过对每个HTTP请求使用loginAction bean定义来创建一个LoginAction bean的新实例。 也就是说,loginAction bean的作用域是在HTTP请求级别。 您可以根据需要更改创建的实例的内部状态,因为根据此loginAcitonbean定义创建的其他bean实例并不会看到这些状态的改变;他们为各自的request拥有。 当reqeust完成处理,request作用的bean就被丢弃了。

当使用注解驱动组件或Java Config时,@RequestScope注解可以用于将一个组件分配给request范围。

@RequestScope
@Component
public class LoginAction {
	// ...
}

Session scope

考虑下面这种bean的xml配置定义:

<bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>

Spring容器通过对单个HTTP会话的生命周期使用userPreferences bean定义来创建UserPreferences bean的新实例。 换句话说,userPreferences bean在HTTPSession级别有效地作用域。 和request-scopedbean相类似,可以改变bean实例的内部状态,不管bean创建了多少实例都可以,要知道,使用相同的userPreferences定义创建的其他的bean实例看不到这些状态的改变,因为他们都是为各自的HTTP Session服务的。 当HTTPSession最终被丢弃时,被限定为该特定HTTPSession的bean也被丢弃。

当使用注解驱动组件或Java Config时,@SessionScope注解可用于将一个组件分配给session范围。

@SessionScope
@Component
public class UserPreferences {
	// ...
}

Application scope

考虑下面这种bean定义:

<bean id="appPreferences" class="com.foo.AppPreferences" scope="application"/>

Spring容器通过对整个web应用程序使用appPreferences bean定义来创建一个AppPreferences bean的新实例。 也就是说,appPreferences bean是在ServletContext级别定义的,存储为一个常规的ServletContext属性。 这有点类似于Spring单例bean,但在两个重要方面有所不同:1、他是每一个ServeltContext一个实例,而不是SpringApplicationContext范围。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。
当对prototype scope的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>


自定义作用域 认为是不推荐的做法,并且,你不能覆盖内置的singletonprototype作用域,有兴趣的同学可以百度。

此文章参考 



Spring核心技术(五)——Spring中Bean的作用域

前文概述了Spring的容器,Bean,以及依赖的一些信息,本文将描述一下Bean的作用域Bean的作用域当开发者定义Bean的时候,同时也会定义了具体如何创建Bean实例的步骤。这些步骤是很重要的,...
  • EthanWhite
  • EthanWhite
  • 2016-05-22 18:23:58
  • 14529

spring 中bean的作用域和生命周期

spring 中bean的作用域和生命周期
  • do_bset_yourself
  • do_bset_yourself
  • 2016-04-17 16:50:18
  • 2938

Spring 配置使用 - Bean 作用域

基本概念Scope,也称作用域,在 Spring IoC 容器是指其创建的 Bean 对象相对于其他 Bean 对象的请求可见范围。在 Spring IoC 容器中具有以下几种作用域:基本作用域(re...
  • u012420654
  • u012420654
  • 2016-10-08 21:59:38
  • 2820

Spring学习(十五)Spring Bean 的5种作用域介绍

Spring Bean 中所说的作用域,在配置文件中即是“scope” 在面向对象程序设计中作用域一般指对象或变量之间的可见范围。 而在Spring容器中是指其创建的Bean对象相对于其他Bean...
  • icarus_wang
  • icarus_wang
  • 2016-06-04 23:32:25
  • 4069

Spring中Bean的作用域有哪些

 在Spring的早期版本中,仅有两个作用域:singleton和prototype 1、singleton表示Bean以单例的方式存在; 2、prototype表示每次从容器中调用Bean...
  • HelloCqk1
  • HelloCqk1
  • 2016-04-13 12:23:13
  • 762

详解Spring中bean的作用域

 如何使用spring的作用域:bean id="role" class="spring.chapter2.maryGame.Role" scope="singleton"/>这里的scope就是用来...
  • ProvidenceZY
  • ProvidenceZY
  • 2007-11-11 10:25:00
  • 9908

Spring Bean的作用域之间有什么区别?

Spring容器中的bean可以分为5个范围。所有范围的名称都是自说明的,但是为了避免混淆,还是让我们来解释一下: 1.       singleton:这种bean范围是默认的,这种范围确保不管接...
  • miachen520
  • miachen520
  • 2016-07-31 10:01:57
  • 1516

Spring中bean的作用域与生命周期

在Spring中,那些组成应用程序的主体及由Spring IoC容器所管理的对象,被称之为bean。简单地讲,bean就是由IoC容器初始化、装配及管理的对象,除此之外,bean就与应用程序中的其他对...
  • fuzhongmin05
  • fuzhongmin05
  • 2017-06-17 22:29:18
  • 1757

Spring中Bean的五个作用域

当通过Spring容器创建一个Bean实例时,不仅可以完成Bean实例的实例化,还可以为Bean指定特定的作用域。Spring支持如下5种作用域: singleton:单例模式,在整个S...
  • u011468990
  • u011468990
  • 2015-11-23 15:35:25
  • 22541

spring之注入不同作用域之间的bean

在配置bean时 默认bean的作用域是单例的 也就是scope = “singleton” 的,也就是在项目中只存在一个的 但是有时候为了配置不同的属性 需要在singleton中加入prototy...
  • qq_31214097
  • qq_31214097
  • 2016-10-02 16:01:06
  • 456
收藏助手
不良信息举报
您举报文章:Spring中Bean的作用域—Spring官方文档系列
举报原因:
原因补充:

(最多只允许输入30个字)