在 Spring 中,那些组成应用程序的主体,以及由 Spring IOC 容器所管理的对象,被称之为 bean。简单地讲,bean 就是由 IOC 容器初始化、装配及管理的对象,除此之外,bean 就与应用程序中的其他对象没有什么区别了,而 bean 的定义以及 bean 相互间的依赖关系,将通过配置元数据来描述。
Spring 中的 bean 默认都是单例的,在多线程程序下,这些单例 Bean 是如何保证线程安全的呢?
例如对于 Web 应用来说,Web 容器对于每个用户的请求,都会创建一个单独的 Sevlet 线程来处理请求,引入 Spring 框架后,每个 Action 都是单例的,那么对于 Spring 托管的单例 Service Bean,如何保证其安全呢?
答案是:Spring 的单例是基于 BeanFactory,也就是 Spring 容器的,单例 Bean 在此容器内只有一个,Java 的单例是基于 JVM,每个 JVM 内只有一个实例。
在大多数情况下。单例 bean 是很理想的方案。不过,有时候系统所使用的类是易变的,它们会保持一些状态,因此重用是不安全的。在这种情况下,将 class 声明为单例就显得不是那么明智了。因为对象会被污染,重用的时候可能会出现意想不到的问题,所以 Spring 定义了支持多种作用域的 bean。
bean 的作用域
创建一个 bean 定义,其实质是用该 bean 定义对应的类来创建真正实例的 “配方”。把 bean 定义看成是配方很有意义,它与 class 很类似,只根据一张 “处方” 就可以创建多个实例,不仅可以控制注入到对象中的各种依赖和配置值,还可以控制该对象的作用域。这样可以灵活选择所建对象的作用域,而不必在 Java Class 级定义作用域。如下表所示,Spring Framework 支持五种作用域。
以上五种作用域中,request、session 和 global session 三种作用域,仅能用在基于 web 的 Spring ApplicationContext 环境。
singleton:唯一 bean 实例
当一个 bean 的作用域为 singleton 时,那 Spring IoC 容器中只会存在一个共享的 bean 实例,并且所有对 bean 的请求,只要 id 与该 bean 定义相匹配,则只会返回 bean 的同一实例。 singleton 是单例类型 (对应于单例模式),就是在创建容器时就同时创建一个该 bean 的对象,不管是否使用,不过可以指定 Bean 节点的 lazy-init="true"
来延迟初始化 bean,这时候,只有在第一次获取 bean 时才会初始化 bean,以后每次获取到的对象都是同一个对象。注意,singleton 作用域是 Spring 中的缺省作用域。如果要显示在 XML 中将 bean 定义成 singleton ,可以这样配置:
<bean id="ServiceImpl" class="cn.mariojd.service.ServiceImpl" scope="singleton">
也可以通过 @Scope
注解(还可以显示指定 bean 的作用范围)的方式:
@Service
@Scope("singleton")
public class ServiceImpl {
}
prototype:每次请求都会创建一个新的 bean 实例
当一个 bean 的作用域为 prototype,表示一个 bean 的定义对应多个对象实例。prototype 作用域的 bean 会导致每次在对该 bean 请求(将其注入到另一个 bean 中,或者以程序的方式调用容器的 getBean() 方法)时,都会创建一个新的 bean 实例。prototype 是原型类型,它在我们创建容器的时候并没有实例化,而是当我们获取 bean 的时候才去创建一个对象,而且每次获取到的对象都不是同一个对象。
总之,对有状态的 bean 应该使用 prototype 作用域,而对无状态的 bean 则应该使用 singleton 作用域。在 XML 中将 bean 定义成 prototype ,可以这样配置:
<bean id="account" class="cn.mariojd.DefaultAccount" scope="prototype"/>
# 或者
<bean id="account" class="cn.mariojd.DefaultAccount" singleton="false"/>
request:每次 HTTP 请求都会产生新的 bean,该 bean 仅在当前 HTTP request 内有效
request 作用域只适用于 Web 程序,每一次 HTTP 请求都会产生一个新的 bean,同时该 bean 仅在当前 HTTP request 内有效,当请求结束后,该对象的生命周期即结束。在 XML 中将 bean 定义成 request ,可以这样配置:
<bean id="loginAction" class=cn.mariojd.LoginAction" scope="request"/>
session:每次 HTTP 请求都会产生新的 bean,该 bean 仅在当前 HTTP session 内有效
session 只适用于 Web 程序,session 作用域表示该针对每一次 HTTP 请求都会产生一个新的 bean,同时该 bean 仅在当前 HTTP session 内有效。当HTTP session 最终被废弃的时候,在该 HTTP session 作用域内的 bean 也会被销毁掉。
<bean id="userPreferences" class="cn.mariojd.UserPreferences" scope="session"/>
globalSession:伴随应用本身的全局作用域
global session 作用域类似于标准的 HTTP session 作用域,不过仅仅在基于 portlet 的 web 应用中才有意义。Portlet 规范定义了全局 Session 的概念,它被所有构成某个 portlet web 应用的各种不同的 portlet 所共享。在 global session 作用域中定义的 bean ,将被限定于全局 portlet Session 的生命周期范围内。
<bean id="user" class="cn.mariojd.Preferences "scope="globalSession"/>