Spring 配置使用 - Bean 作用域
来源:互联网 发布:支付宝软件下载 编辑:程序博客网 时间:2024/06/08 09:10
基本概念
Scope,也称作用域,在 Spring IoC 容器是指其创建的 Bean 对象相对于其他 Bean 对象的请求可见范围。
在 Spring IoC 容器中具有以下几种作用域:基本作用域(request、prototype),Web 作用域(reqeust、session、globalsession),自定义作用域。
Spring 的作用域在装配 Bean 时就必须在配置文件中指明,配置方式如下(以 xml 配置文件为例):
<!-- 具体的作用域需要在 scope 属性中定义 --><bean id="animals" class="com.demo.Animals" scope="xxx" />
基本作用域
1.singleton
singleton,也称单例作用域。在每个 Spring IoC 容器中有且只有一个实例,而且其完整生命周期完全由 Spring 容器管理。对于所有获取该 Bean 的操作 Spring 容器将只返回同一个 Bean。
需要注意的是,若一个 Bean 未指定 scope 属性,默认也为 singleton 。
- 在配置文件中定义:
<bean id="animals" class="com.demo.Animals" scope="singleton" />
- 调用验证:
String location = ...ApplicationContext factory = new FileSystemXmlApplicationContext(location);// 获取 BeanAnimals animals = (Animals) factory.getBean("animals");Animals animals2 = (Animals) factory.getBean("animals");System.out.println(animals);System.out.println(animals2);//输出结果://com.demo.Animals@2151b0a5//com.demo.Animals@2151b0a5
观察输出结果,发现多次获取 Bean,返回的都是同一个 Bean,再次验证了其在 Spring IoC 容器中有且只有一个实例。
2.prototype
prototype,也称原型作用域。每次向 Spring IoC 容器请求获取 Bean 都返回一个全新的Bean。相对于 singleton 来说就是不缓存 Bean,每次都是一个根据 Bean 定义创建的全新 Bean。
- 在配置文件中定义:
<bean id="animals" class="com.demo.Animals" scope="prototype" />
- 调用验证,参照 singleton 的例子,观察输出结果,发现每次调用返回不同的实例。
Web 作用域
1.reqeust
request,表示每个请求需要容器创建一个全新Bean。
在 Spring IoC 容器,即XmlWebApplicationContext 会为每个 HTTP 请求创建一个全新的 RequestPrecessor 对象。当请求结束后,该对象的生命周期即告结束。当同时有 10 个 HTTP 请求进来的时候,容器会分别针对这 10 个请求创建 10 个全新的 RequestPrecessor 实例,且他们相互之间互不干扰,从不是很严格的意义上说,request 可以看做 prototype 的一种特例,除了场景更加具体之外,语意上差不多。
- 在配置文件中定义:
<bean id="animals" class="com.demo.Animals" scope="request" />
- 调用验证(这里以 SpringMVC 为例,模拟了不同的两个请求)
@RequestMapping(value = "/hello1")public void hello1(HttpServletRequest request) throws Exception { // 在 Web 程序取得 Ioc 容器 ApplicationContext context = (ApplicationContext) WebApplicationContextUtils.getWebApplicationContext(request.getServletContext()); Animals animals1 = (Animals) context.getBean(Animals.class); animals1.setName("animals"); Animals animals2 = (Animals) context.getBean(Animals.class); System.out.println(animals1.getName()); System.out.println(animals2.getName()); // 输出结果: // animals // animals}@RequestMapping(value = "/hello2")public void hello2(HttpServletRequest request) throws Exception { ApplicationContext context = (ApplicationContext) WebApplicationContextUtils.getWebApplicationContext(request.getServletContext()); Animals animals = (Animals) context.getBean(Animals.class); System.out.println(animals1.getName()); // 输出结果: // null}
观察输出结果,发现在同一个请求中(hello1 )多次获取 Bean 返回的是同一个实例,而在不同请求(hello2)中获取 Bean 返回的是不同的 Bean。
2.session
session,表示每个会话需要容器创建一个全新 Bean。比如对于每个用户一般会有一个会话,该用户的用户信息需要存储到会话中,此时可以将该 Bean 配置为 web 作用域。
- 在配置文件中定义:
<bean id="animals" class="com.demo.Animals" scope="session" />
- 调用验证(使用不同的浏览器发起该请求)
@RequestMapping(value = "/hello1")public void hello1(HttpServletRequest request) throws Exception { // 在 Web 程序取得 Ioc 容器 ApplicationContext context = (ApplicationContext) WebApplicationContextUtils.getWebApplicationContext(request.getServletContext()); Animals animals1 = (Animals) context.getBean(Animals.class); animals1.setName("animals"); Animals animals2 = (Animals) context.getBean(Animals.class); System.out.println(animals1); System.out.println(animals2);}
观察输出结果,会发现不同浏览器(不同会话)返回的 Bean 实例不同,而同一个浏览器(同一会话)多次发起请求返回的是同一个 Bean 实例。
3.globalSession
globalSession,类似于session 作用域,只是其用于 portlet 环境的 web 应用。如果在非portlet 环境将视为 session 作用域。
自定义作用域
1.实例探究
自定义作用域,需要实现 Scope 接口。
首先来看该接口的定义:
public interface Scope { // 从作用域中获取Bean, objectFactory 表示当在当前作用域没找到合适Bean时使用它创建一个新的Bean Object get(String name, ObjectFactory<?> objectFactory); // 从作用域中移除 Bean Object remove(String name); // 用于注册销毁回调,如果想要销毁相应的对象则由Spring容器注册相应的销毁回调,而由自定义作用域选择是不是要销毁相应的对象; void registerDestructionCallback(String name, Runnable callback); // 用于解析相应的上下文数据,比如request作用域将返回request中的属性。 Object resolveContextualObject(String key); // 作用域的会话标识,比如session作用域将是sessionId。 String getConversationId();}
接下来看看如何使用自定义的作用域
- 自定义作用域 ThreadScope,表示 Bean 作用范围为同一个线程:
public class ThreadScope implements Scope { // 用于存放线程中的 Bean private final ThreadLocal<Map<String, Object>> THREAD_SCOPE = new ThreadLocal<Map<String, Object>>() { protected Map<String, Object> initialValue() { return new HashMap<String, Object>(); } }; @Override public Object get(String name, ObjectFactory<?> objectFactory) { Map<String, Object> map = THREAD_SCOPE.get(); if (!map.containsKey(name)) { map.put(name, objectFactory.getObject()); } return map.get(name); } @Override public Object remove(String name) { Map<String, Object> map = THREAD_SCOPE.get(); return map.remove(name); } @Override public void registerDestructionCallback(String name, Runnable callback) { } @Override public Object resolveContextualObject(String key) { return null; } @Override public String getConversationId() { return null; }}
- 在配置文件定义:
<!-- CustomScopeConfigurer 的 scopes 属性注册自定义作用域实现 --><bean class="org.springframework.beans.factory.config.CustomScopeConfigurer"> <property name="scopes"> <map> <entry key="thread" > <bean class="scope.ThreadScope"/> </entry> </map> </property></bean> <bean id="animals" class="com.demo.Animals" scope="thread"/>
- 调用验证:
public static void main(String [ ] args) { String location = "WebRoot/WEB-INF/spring-bean.xml"; ApplicationContext factory = new FileSystemXmlApplicationContext(location); // 在 main 线程获取 Bean Animals animals1 = (Animals) factory.getBean("animals"); Animals animals2 = (Animals) factory.getBean("animals"); System.out.println(animals1); System.out.println(animals2); // 新建线程获取 Bean Thread thread = new Thread() { public void run() { String location = "WebRoot/WEB-INF/spring-bean.xml"; ApplicationContext factory = new FileSystemXmlApplicationContext(location); Animals animals = (Animals) factory.getBean("animals"); System.out.println(animals); } }; thread.start(); // 输出结果: // com.demo.Animals@3ddfd90f // com.demo.Animals@3ddfd90f // com.demo.Animals@153b2cb}
观察输出结果,发现同一线程多次获取 Bean 返回的是同一个实例,而不同线程获取 Bean 返回的是不同实例。
2.原理探究
这里以自定义作用域为例,探究下 scope 的基本原理。在 Spring 中对于 Bean 的 scope(作用域)的检查发生在【获取 Bean】的过程中。获取方法如下:
Animals animals1 = (Animals) factory.getBean("animals");
由此可见,获取 Bean 的入口在 BeanFacotry 中定义。而 BeanFacotry 的基本功能实现都在它的基本实现类 AbstractBeanFactory 中,具体的调用过程这里不再探究,简单的调用流程如下: getBean -> doGetBean。因此这里重点来看下 doGetBean 这个方法:
protected <T> T doGetBean(final String name, final Class<T> requiredType, final Object [ ] args, boolean typeCheckOnly) throws BeansException { final String beanName = transformedBeanName(name); // 省略部分源码... try { // 省略部分源码... // 判断 scope 作用域 // 若既不是 singleton 也不是 prototype,表明该 Bean 的作用域是自定义作用域或 web 作用域 if (mbd.isSingleton()) { // 省略部分源码... }else if (mbd.isPrototype()) { // 省略部分源码... }else { // 取得 Bean 的 scope 名称,这里指 thread String scopeName = mbd.getScope(); // 取得在 CustomScopeConfigurer 中定义的 scope 对象,这里指 ThreadScope 对象 final Scope scope = this.scopes.get(scopeName); if (scope == null) { // 抛出异常... } try { // 调用 scope 的 get 方法 Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() { // 若不存在该标识的 bean,则触发 map.put(name, objectFactory.getObject()) 的 getObject 方法 @Override public Object getObject() throws BeansException { beforePrototypeCreation(beanName); try { // 创建 Bean 实例 return createBean(beanName, mbd, args); } finally { afterPrototypeCreation(beanName); } } }); bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd); } catch (IllegalStateException ex) { // 抛出异常... } } } catch (BeansException ex) { cleanupAfterBeanCreationFailure(beanName); throw ex; } } // 省略部分源码...}
参考
- http://jinnianshilongnian.iteye.com/blog/1415463
- Spring 配置使用 - Bean 作用域
- spring配置bean作用域
- Spring配置bean的作用域
- spring 6 bean配置--bean的作用域
- Spring Bean--Bean的配置项、作用域、生命周期
- Spring bean 作用域
- spring bean作用域
- Spring bean作用域
- spring bean 作用域
- Spring - Bean作用域
- Spring bean作用域
- spring bean作用域
- Spring bean作用域
- Spring bean作用域
- Spring bean 作用域
- spring bean作用域
- spring-bean作用域
- Spring bean 作用域
- top命令查看每个逻辑cpu使用率
- JS preventDefault ,stopPropagation ,return false
- 【各大OJ】最短路专题
- 数据结构实验之链表二:逆序建立链表
- loadrunner录制脚本时报错Unable to connect to remote server: rc = -1 , le = 0
- Spring 配置使用 - Bean 作用域
- Oracle存储过程以及java调用
- 浙大 PAT b1052
- 欢迎使用CSDN-markdown编辑器
- C#_CombolBox添加数据的几种方法
- 树表中二叉树树查找方法以及平衡树的简解
- maven install Failed to execute goal org.apache.maven.plugins:maven-war-plugin:2.1.1:war (default-wa
- windows下apache、php、mysql以及wordpress配置步骤
- Android面试指南