文章由Spring官方文档翻译而来,地址:
https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#beans
一 bean的作用域
创建bean definition时,将创建一个配方来创建该bean definition所定义的类的实例。 bean definition是配方的想法很重要,因为它意味着与类一样,您可以从一个配方中创建许多对象实例。
您不仅可以控制从bean definition创建出的对象的各种依赖项和配置,还可以控制从bean definition创建的对象的范围。 这种方法功能强大且灵活,因为您可以通过配置在创建对象时指定作用域,而不必在Java Class定义作用域。
仅当您使用可感知Web的Spring ApplicationContext实现(例如XmlWebApplicationContext)时,request, session, application, websocket才可用。 如果将这些作用域与常规的Spring IoC容器(例如ClassPathXmlApplicationContext)一起使用,则会抛出一个IllegalStateException异常,该错误抱怨未知的bean作用域。
1.singleton——唯一 bean 实例
当一个 bean 的作用域为 singleton,那么Spring IoC容器中只会存在一个共享的 bean 实例,并且所有对 bean 的请求,只要 id 与该 bean 定义相匹配,则只会返回bean的同一实例。换句话说,当您定义一个bean definition并且其作用域为单例时,Spring IoC容器将为该bean definition所定义的对象创建一个实例。 该实例存储在缓存中,并且对该命名bean的所有后续请求和引用都返回缓存中的对象。
下图显示了单例作用域如何工作
<bean id="accountService" class="com.something.DefaultAccountService"/>
<!-- the following is equivalent, though redundant (singleton scope is the default) -->
<bean id="accountService" class="com.something.DefaultAccountService" scope="singleton"/>
2.prototype——每次请求都会创建一个新的 bean 实例
当一个bean的作用域为 prototype,表示一个 bean 定义对应多个对象实例。 prototype 作用域的 bean 会导致在每次对该 bean 请求(将其注入到另一个 bean 中,或者以程序的方式调用容器的 getBean() 方法)时都会创建一个新的 bean 实例。prototype 是原型类型,它在我们创建容器的时候并没有实例化,而是当我们获取bean的时候才会去创建一个对象,而且我们每次获取到的对象都不是同一个对象。根据经验,对有状态的 bean 应该使用 prototype 作用域,而对无状态的 bean 则应该使用 singleton 作用域。
下图显示了prototype作用域如何工作
(A data access object (DAO) is not typically configured as a prototype, because a typical DAO does not hold any conversational state. It was easier for us to reuse the core of the singleton diagram.)
<bean id="accountService" class="com.something.DefaultAccountService" scope="prototype"/>
与其他作用域相反, Spring不能管理Prototype Bean的完整生命周期。容器将实例化,配置或组装原型对象,然后将其交给客户端,而无需对该prototype 实例的进一步记录。 因此,尽管在不考虑作用域的情况下在所有对象上都调用了初始化生命周期回调方法, 对于prototype实例,不调用已配置的销毁生命周期回调。客户端代码必须清除作用域为prototype的对象,并释放prototype Bean拥有的昂贵资源。要使Spring容器释放prototype bean所拥有的资源,请尝试使用自定义bean post-processor,,该处理器包含对待需要清除的bean的引用。
在某种程度,Spring容器在prototype bean方面的角色是Java new运算符的替代。超过Spring容器管理的所有生命周期必须由客户端处理。
3.request——每一次HTTP请求都会产生一个新的bean,该bean仅在当前HTTP request内有效
request只适用于Web程序,每一次 HTTP 请求都会产生一个新的bean,同时该bean仅在当前HTTP request内有效,当请求结束后,该对象的生命周期即告结束。 在 XML 中将 bean 定义成 request ,可以这样配置:
<bean id="loginAction" class=cn.csdn.LoginAction" scope="request"/>
4.session——每一次HTTP请求都会产生一个新的 bean,该bean仅在当前 HTTP session 内有效
使用以下的XML配置:
<bean id="userPreferences" class="com.something.UserPreferences" scope="session"/>
Spring容器通过在单个HTTP会话的生存期内使用userPreferences bean定义来创建UserPreferences bean的新实例。换句话说,userPreferences bean有效地作用在HTTP会话级别。 与使用request作用域bean一样,您可以根据需要更改实例的内部状态,因为其他HTTP会话实例也使用从相同userPreferences bean definition创建的实例,因为它们特定于单个HTTP会话。 当HTTP会话最终被丢弃时,作用于该特定HTTP会话的bean也将被丢弃。
- Application Scope
使用以下的XML配置:
<bean id="appPreferences" class="com.something.AppPreferences" scope="application"/>
Spring容器通过对整个Web应用程序使用一次appPreferences bean definition来创建AppPreferences bean的新实例。也就是说,appPreferences bean的作用域位于ServletContext级别,并存储为常规ServletContext属性。 这有点类似于Spring单例bean,但有两个重要的区别:它是每个ServletContext的单例,而不是Spring’ApplicationContext’的单例(在任何给定的Web应用程序中可能都有多个),并且它实际上是公开的,因此可见为ServletContext属性。
可以使用@ApplicationScope注释将组件分配给应用程序范围。 以下示例显示了如何执行此操作:
@ApplicationScope
@Component
public class AppPreferences {
// ...
}