Spring Bean的作用域
Spring 容器在初始化一个 Bean 的实例时,同时会指定该实例的作用域。
作用域 | 描述 |
---|---|
singleton | 将每个Spring IoC容器的单个bean定义范围为单个对象实例。 |
prototype | 将单个bean定义作用域到任意数量的对象实例。 |
request | 将单个bean定义范围到单个HTTP请求的生命周期。也就是说,每个HTTP请求都有它自己的bean实例,它是在单个bean定义的后面创建的。仅在支持web的Spring ApplicationContext中有效。 |
session | 将单个bean定义范围到HTTP会话的生命周期。仅在支持web的Spring ApplicationContext中有效。 |
application | 将单个bean定义作用于ServletContext的生命周期。仅在支持web的Spring ApplicationContext中有效。 |
websocket | 将单个bean定义作用于WebSocket的生命周期。仅在支持web的Spring ApplicationContext中有效 |
1、singleton
单例模式,使用 singleton 定义的 Bean 在 Spring 容器中只有一个实例,这也是 Bean 默认的作用域。
一般情况下,无状态或者状态不可变的类适合使用单例模式来实现, 不过 Spring 利用 AOP 和 LocalThread 的能力,对非线程安全的变量(状态)进行了特殊处理,使的一些非线程安全的类(持有 Connection 的 DAO 类)变成了线程安全的类 。
<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"/>
Spring 的 ApplicationContext 容器在启动时,会自动实例化所有 singleton 的 Bean 并缓存在容器中 。优点:
- 对 Bean 提前进行实例化操作会及早发现一些潜在的配置问题
- Bean 以缓存的方式保存,当运行时调用该 Bean 时就无须再次实例化咯,因此提高运行效率 。
那么可以通过 lazy-init 属性进行控制是否提前实例化:
<bean id="accountService" class="com.something.DefaultAccountService" lazy-init="true"/>
2、prototype
原型模式,每次通过 Spring 容器获取 prototype 定义的 Bean 时,容器都将创建一个新的 Bean 实例。
配置了 scope=“prototype” 的 bean 为非单例作用域。Spring 容器在启动时不实例化 prototype 的 bean ,此外, Spring 容器将 prototype 的 bean 交给调用者后,就不再负责管理它的生命周期。
<bean id="accountService" class="com.something.DefaultAccountService" scope="prototype"/>
注意,singleton和prototype相互依赖的场景,下文翻译原文。
当您将单例范围的bean与原型bean上的依赖项一起使用时,请注意依赖项是在实例化时解析的。因此,如果你把一个原型作用域的bean注入到一个单例作用域的bean中,那么一个新的原型bean就会被实例化,然后依赖性注入到单例bean中。原型实例是提供给单例作用域bean的唯一实例。
但是,假设您希望单例作用域bean在运行时反复获得原型作用域bean的新实例。你不能依赖地将原型作用域的bean注入到你的单例bean中,因为这种注入只发生一次,当Spring容器实例化单例bean并解析和注入它的依赖时。如果您在运行时不止一次地需要原型bean的新实例,请参见方法注入
3、request 、session 、application
- request :在一次 HTTP 请求中,容器会返回该 Bean 的同一个实例。而对不同的 HTTP 请求,会返回不同的实例,该作用域仅在当前 HTTP Request 内有效。
- session :在一次 HTTP Session 中,容器会返回该 Bean 的同一个实例。而对不同的 HTTP 请求,会返回不同的实例,该作用域仅在当前 HTTP Session 内有效。
- application :在一个全局的 HTTP Session 中,容器会返回该 Bean 的同一个实例。该作用域仅在使用 portlet context 时有效。
web.xml 增加request 监听器、过滤器
<web-app>
...
<listener>
<listener-class>
org.springframework.web.context.request.RequestContextListener
</listener-class>
</listener>
...
</web-app>
<!--或者-->
<web-app>
...
<filter>
<filter-name>requestContextFilter</filter-name>
<filter-class>org.springframework.web.filter.RequestContextFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>requestContextFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
...
</web-app>
RequestContextListener与 ContextLoaderListener 的区别是:
- ContextLoaderListener 实现了 ServletContextListener 监听器接口, 它只负责监听 web 容器的启动和关闭事件 。
- RequestContextListener 实现了 ServletRequestListener 监听器接口,它监听 HTTP 请求事件, Web 服务器的每一次请求都会通知它 。
<bean id="loginAction" class="com.something.LoginAction" scope="request"/>
<bean id="userPreferences" class="com.something.UserPreferences" scope="session"/>
<bean id="appPreferences" class="com.something.AppPreferences" scope="application"/>
@RequestScope
@Component
public class LoginAction {
// ...
}
@SessionScope
@Component
public class UserPreferences {
// ...
}
@ApplicationScope
@Component
public class AppPreferences {
// ...
}