`

spring scope自定义

阅读更多
BeanFactory除了拥有作为IoC Service Provider的职责,作为一个轻量级容器,它还有着其他一些职责,其中就包括对象的生命周期管理。

本节主要讲述容器中管理的对象的scope这个概念。多数中文资料在讲解bean的scope时喜欢用"作用域"这个名词,应该还算贴切吧。不过,我更希望告诉你scope这个词到底代表什么意思,至于你怎么称呼它反而不重要。

scope用来声明容器中的对象所应该处的限定场景或者说该对象的存活时间,即容器在对象进入其相应的scope之前,生成并装配这些对象,在该对象不再处于这些scope的限定之后,容器通常会销毁这些对象。打个比方吧!我们都是处于社会(容器)中,如果把中学教师作为一个类定义,那么当容器初始化这些类之后,中学教师只能局限在中学这样的场景中;中学,就可以看作中学教师的scope。

Spring容器最初提供了两种bean的scope类型:singleton和prototype,但发布2.0之后,又引入了另外三种scope类型,即request、session和global session类型。不过这三种类型有所限制,只能在Web应用中使用。也就是说,只有在支持Web应用的ApplicationContext中使用这三个scope才是合理的。

我们可以通过使用<bean>的singleton或者scope属性来指定相应对象的scope,其中,scope属性只能在XSD格式的文档声明中使用,类似于如下代码所演示的形式:


DTD:
<bean id="mockObject1" class="...MockBusinessObject" singleton="false"/>
XSD:
<bean id="mockObject2" class="...MockBusinessObject" scope="prototype"/>
让我们来看一下容器提供的这几个scope是如何限定相应对象的吧!

1. singleton

配置中的bean定义可以看作是一个模板,容器会根据这个模板来构造对象。但是要根据这个模板构造多少对象实例,又该让这些构造完的对象实例存活多久,则由容器根据bean定义的scope语意来决定。标记为拥有singleton scope的对象定义,在Spring的IoC容器中只存在一个实例,所有对该对象的引用将共享这个实例。该实例从容器启动,并因为第一次被请求而初始化之后,将一直存活到容器退出,也就是说,它与IoC容器"几乎"拥有相同的"寿命"。

图4-5是Spring参考文档中所给出的singleton的bean的实例化和注入语意演示图例,或许可以更形象地说明问题。


(点击查看大图)图4-5 singleton scope

需要注意的一点是,不要因为名字的原因而与GoF 所提出的Singleton模式相混淆,二者的语意是不同的: 标记为singleton的bean是由容器来保证这种类型的bean在同一个容器中只存在一个共享实例; 而Singleton模式则是保证在同一个Classloader中只存在一个这种类型的实例。

可以从两个方面来看待singleton的bean所具有的特性。

对象实例数量。singleton类型的bean定义,在一个容器中只存在一个共享实例,所有对该类型bean的依赖都引用这一单一实例。这就好像每个幼儿园都会有一个滑梯一样,这个幼儿园的小朋友共同使用这一个滑梯。而对于该幼儿园容器来说,滑梯实际上就是一个singleton的bean。

对象存活时间。singleton类型bean定义,从容器启动,到它第一次被请求而实例化开始,只要容器不销毁或者退出,该类型bean的单一实例就会一直存活。

通常情况下,如果你不指定bean的scope,singleton便是容器默认的scope,所以,下面三种配置形式实际上达成的是同样的效果:


<!-- DTD or XSD -->
<bean id="mockObject1" class="...MockBusinessObject"/>
<!-- DTD -->
<bean id="mockObject1" class="...MockBusiness
Object" singleton="true"/>
<!-- XSD -->
<bean id="mockObject1" class="...MockBusiness
Object" scope="singleton"/>
2. prototype

针对声明为拥有prototype scope的bean定义,容器在接到该类型对象的请求的时候,会每次都重新生成一个新的对象实例给请求方。虽然这种类型的对象的实例化以及属性设置等工作都是由容器负责的,但是只要准备完毕,并且对象实例返回给请求方之后,容器就不再拥有当前返回对象的引用,请求方需要自己负责当前返回对象的后继生命周期的管理工作,包括该对象的销毁。也就是说,容器每次返回给请求方一个新的对象实例之后,就任由这个对象实例"自生自灭"了。

让我们继续幼儿园的比喻,看看prototype在这里应该映射到哪些事物。儿歌里好像有句"排排坐,分果果",我们今天要分苹果咯!将苹果的bean定义的scope声明为prototype,在每个小朋友领取苹果的时候,我们都是分发一个新的苹果给他。发完之后,小朋友爱怎么吃怎么吃,爱什么时候吃什么时候吃。但是,吃完后要记得把果核扔到果皮箱哦! 而如果你把苹果的bean定义的scope声明为singleton会是什么情况呢?如果第一个小朋友比较谦让,那么他可能对这个苹果只咬一口,但是下一个小朋友吃多少就不知道了。当吃得只剩一个果核的时候,下一个来吃苹果的小朋友肯定要哭鼻子的。

所以,对于那些请求方不能共享使用的对象类型,应该将其bean定义的scope设置为prototype。这样,每个请求方可以得到自己对应的一个对象实例,而不会出现上面"哭鼻子"的现象。通常,声明为prototype的scope的bean定义类型,都是一些有状态的,比如保存每个顾客信息的对象。

从Spring 参考文档上的这幅图片(见图4-6),你可以再次了解一下拥有prototype scope的bean定义,在实例化对象并注入依赖的时候,它的具体语意是个什么样子。



(点击查看大图)图4-6 prototype scope
你用以下形式来指定某个bean定义的scope为prototype类型,效果是一样的:


<!-- DTD -->
<bean id="mockObject1" class="...MockBusiness
Object" singleton="false"/>
<!-- XSD -->
<bean id="mockObject1" class="...MockBusiness
Object" scope="prototype"/>
4.3.5 bean的scope(2)

3. request、session和global session

这三个scope类型是Spirng 2.0之后新增加的,它们不像之前的singleton和prototype那么"通用",因为它们只适用于Web应用程序,通常是与XmlWebApplicationContext共同使用,而这些将在第6部分详细讨论。不过,既然它们也属于scope的概念,这里就简单提几句。

注意 只能使用scope 属性才能指定这三种"bean的scope类型"。也就是说,你不得不使用基于XSD文档声明的XML配置文件格式。

request

request通常的配置形式如下:

<bean id="requestProcessor" class="...
RequestProcessor" scope="request"/>
Spring容器,即XmlWebApplicationContext会为每个HTTP请求创建一个全新的Request- Processor对象供当前请求使用,当请求结束后,该对象实例的生命周期即告结束。当同时有10个HTTP请求进来的时候,容器会分别针对这10个请求返回10个全新的RequestProcessor对象实例,且它们之间互不干扰。从不是很严格的意义上说,request可以看作prototype的一种特例,除了场景更加具体之外,语意上差不多。

session

对于Web应用来说,放到session中的最普遍的信息就是用户的登录信息,对于这种放到session中的信息,我们可使用如下形式指定其scope为session:

<bean id="userPreferences" class="com.foo.
UserPreferences" scope="session"/>
Spring容器会为每个独立的session创建属于它们自己的全新的UserPreferences对象实例。与request相比,除了拥有session scope的bean的实例具有比request scope的bean可能更长的存活时间,其他方面真是没什么差别。

global session

还是userPreferences,不过scope对应的值换一下,如下所示:

<bean id="userPreferences" class="com.foo.
UserPreferences" scope="globalSession"/>
global session只有应用在基于portlet的Web应用程序中才有意义,它映射到portlet的global范围的session。如果在普通的基于servlet的Web应用中使用了这个类型的scope,容器会将其作为普通的session类型的scope对待。

4. 自定义scope类型

在Spring 2.0之后的版本中,容器提供了对scope的扩展点,这样,你可以根据自己的需要或者应用的场景,来添加自定义的scope类型。需要说明的是,默认的singleton和prototype是硬编码到代码中的,而request、session和global session,包括自定义scope类型,则属于可扩展的scope行列,它们都实现了org.springframework.beans.factory.config.Scope接口,该接口定义如下:

public interface Scope {
     Object get(String name, ObjectFactory objectFactory);
     Object remove(String name);
    void registerDestructionCallback(String name,
Runnable callback);
     String getConversationId();
}
要实现自己的scope类型,首先需要给出一个Scope接口的实现类,接口定义中的4个方法并非都是必须的,但get和remove方法必须实现。我们可以看一下http://www.jroller.com/eu/entry/implementing _efficinet_id_generator中提到的一个ThreadScope的实现(见代码清单4-28)。

代码清单4-28 自定义的ThreadScope的定义

public class ThreadScope implements Scope {
  private final ThreadLocal threadScope = new ThreadLocal() {
      protected Object initialValue() {
        return new HashMap();
       }
     };
 
  public Object get(String name, ObjectFactory objectFactory) {
     Map scope = (Map) threadScope.get();
     Object object = scope.get(name);
    if(object==null) {
       object = objectFactory.getObject();
       scope.put(name, object);
     }
    return object;
   }
  public Object remove(String name) {
     Map scope = (Map) threadScope.get();
    return scope.remove(name);
   }
  public void registerDestructionCallback(String name,
Runnable callback) {
   }
   ...
}
更多Scope相关的实例,可以参照同一站点的一篇文章"More fun with Spring scopes"(http://jroller. com/eu/entry/more_fun_with_spring_scopes),其中提到PageScope的实现。

有了Scope的实现类之后,我们需要把这个Scope注册到容器中,才能供相应的bean定义使用。通常情况下,我们可以使用ConfigurableBeanFactory的以下方法注册自定义scope:

void registerScope(String scopeName, Scope scope);
其中,参数scopeName就是使用的bean定义可以指定的名称,比如Spring框架默认提供的自定义scope类型request或者session。参数scope即我们提供的Scope实现类实例。

对于以上的ThreadScope,如果容器为BeanFactory类型(当然,更应该实现Configurable- BeanFactory),我们可以通过如下方式来注册该Scope:

Scope threadScope = new ThreadScope();
beanFactory.registerScope("thread",threadScope);
之后,我们就可以在需要的bean定义中直接通过"thread"名称来指定该bean定义对应的scope为以上注册的ThreadScope了,如以下代码所示:

<bean id="beanName" class="..." scope="thread"/>
除了直接编码调用ConfigurableBeanFactory的registerScope来注册scope,Spring还提供了一个专门用于统一注册自定义scope的BeanFactoryPostProcessor实现(有关BeanFactoryPost- Processor的更多细节稍后将详述),即org.springframework.beans.factory.config.Custom- ScopeConfigurer。对于ApplicationContext来说,因为它可以自动识别并加载BeanFactoryPost- Processor,所以我们就可以直接在配置文件中,通过这个CustomScopeConfigurer注册来Thread- Scope(如代码清单4-29所示)。

代码清单4-29 使用CustomScopeConfigurer注册自定义scope

<bean class="org.springframework.beans.factory.
config.CustomScopeConfigurer">
     <property name="scopes">
         <map>
             <entry key="thread" value="com.foo.ThreadScope"/>
         </map>
     </property>
</bean>
在以上工作全部完成之后,我们就可以在自己的bean定义中使用这个新增加到容器的自定义scope"thread"了,如下代码演示了通常情况下"thread"自定义scope的使用:

<bean id="beanName" class="..." scope="thread">
     <aop:scoped-proxy/>
</bean>
由于<aop:scoped-proxy/>涉及Spring AOP相关知识,这里不会详细讲述。需要注意的是,使用了自定义scope的bean定义,需要该元素来为其在合适的时间创建和销毁相应的代理对象实例。对于request、session和global session来说,也是如此。

分享到:
评论

相关推荐

    如何在Spring中自定义scope的方法示例

    主要介绍了如何在Spring中自定义scope的方法示例,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

    spring-custom-scope:Spring 自定义范围概述

    脾气暴躁的危险地带 Spring Custom Scope Web 项目的简短而有趣的示例。 在这种情况下,自定义范围是多线程的,并且对话的范围是基于注解的 AOP 建议。 您还可以访问以下 URL 中的自定义范围演示:

    spring-scopes:自定义 Spring Bean 范围

    自定义 Spring Bean Scopes 用于默​​认设置不起作用的情况 可用范围 Route Scope - 提供每个路由执行范围的范围 页面范围 - 提供每页范围的范围 线程范围 - 提供每个线程范围的范围 继承的线程范围 - 提供每个线程...

    Spring IOC Bean标签属性介绍(教学视频+源代码)

    Spring IOC Bean标签属性介绍 0.Bean标签属性介绍 1.0 新建一个Maven工程 1.1 pom.xml 1.2 实体类JavaBean 1.2.1 User类 1.3 当Scope="singleton"时 1.4 当 Scope="singleton" 且 lazy-init="true" 时 1.5 当scope=...

    JavaEE开发的颠覆者SpringBoot实战[完整版].part3

    2.1 Bean 的Scope 30 2.1.1 点睛 30 2.1.2 示例 31 2.2 Spring EL 和资源调用. 33 2.2.1 点睛 33 2.2.2 示例 33 2.3 Bean 的初始化和销毁 37 2.3.1 点睛 37 2.3.2 演示 38 2.4 Profile 40 2.4.1 点睛 40 2.4.2 演示 ...

    JavaEE开发的颠覆者SpringBoot实战[完整版].part2

    2.1 Bean 的Scope 30 2.1.1 点睛 30 2.1.2 示例 31 2.2 Spring EL 和资源调用. 33 2.2.1 点睛 33 2.2.2 示例 33 2.3 Bean 的初始化和销毁 37 2.3.1 点睛 37 2.3.2 演示 38 2.4 Profile 40 2.4.1 点睛 40 2.4.2 演示 ...

    JavaEE开发的颠覆者SpringBoot实战[完整版].part1

    2.1 Bean 的Scope 30 2.1.1 点睛 30 2.1.2 示例 31 2.2 Spring EL 和资源调用. 33 2.2.1 点睛 33 2.2.2 示例 33 2.3 Bean 的初始化和销毁 37 2.3.1 点睛 37 2.3.2 演示 38 2.4 Profile 40 2.4.1 点睛 40 2.4.2 演示 ...

    springboot学习思维笔记.xmind

    Bean的Scope Singleton Prototype Request Session GlobalSession SpringEL和资源调用 注入普通字符 注入操作系统属性 注入表达式云算结果 注入其他Bean的属性 注入文件内容 ...

    springboot-oauth:Spring boot 2 + Spring security 5 实现 SSO + OAuth认证

    springboot-oauth ...client-4:资源服务器,自定义ExpressionHandler实现scope匹配,即client的scopes为rea.*,将正常通过hasScopeMatching('read')的鉴权 client-5:资源服务器,使用JWT公钥验证token

    ssh(structs,spring,hibernate)框架中的上传下载

    数据表Blob字段在Hibernate持久化映射文件中的type为org.springframework.orm.hibernate3.support.BlobByteArrayType,即Spring所提供的用户自定义的类型,而非java.sql.Blob。 3在Spring中使用org.springframework...

    Angular.js前台传list数组由后台spring MVC接收数组示例代码

    本文主要给大家介绍了关于Angular.js前台传list数组由后台spring MVC接收数组的相关内容,分享出来供大家参考学习,下面话不多说,来一起看看详细的介绍吧。 在开发中有时候需要在前台自定义对象,然后把对象封装在...

    Java秒杀系统方案优化高性能高并发学习实战源代码以及笔记..zip

    &lt;scope&gt;provided&lt;/scope&gt; 编写启动类 public class ServletApplication extends SpringBootServletInitializer { @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {...

    kellerMapper.jar

    &lt;scope&gt;runtime&lt;/scope&gt; 目录结构 mybatis:MyBatis 通用 Mapper 相关 annotation:注解相关 FieldAttribute.java:字段注解,标示成员变量为数据库字段,并设置相应的约束信息,如:长度、非空、查询字段...

    mybatis-plus-generator-ui:对mybatis-plus-generator进行封装,通过Web UI快速生成兼容的Spring boot,mybatis-plus框架的各类业务代码

    提供一致的Web UI用于生成兼容mybatis-plus框架的相关功能代码,包括Entity,Mapper,Mapper.xml,Service,Controller等,可以自定义模板以及各种输出参数,也可以通过SQL查询语句直接生成代码。 使用方法 ♡maven...

    red5连接池

    Object o = scope.getContext().getBean("xbDataSource"); JdbcTemplate t = new JdbcTemplate((BasicDataSource) o); final List l = t.query("SELECT * FROM jt_employee", new RowMapper() { public Object ...

    社区协作配置标准Tamaya.zip

    Apache Tamaya是一个孵化项目,旨在...这里很棒的一点在于所有的扩展都不会依赖于核心模块,除非运行在测试作用域(test scope)中,这个作用域提供了一个功能性的实现,用来执行我们的测试用例。 标签:Tamaya

    Maven权威指南 很精典的学习教程,比ANT更好用

    Including and Excluding Dependencies by Scope 12.5.4.4. Fine Tuning: Dependency Includes and Excludes 12.5.4.5. Transitive Dependencies, Project Attachments, and Project Artifacts 12.5.4.6. ...

    SpringMVC-Mybatis-Shiro-redis-master 权限集成缓存中实例

    ... ...建议自己下载源码,让Demo跑起来,然后跑的更快,有问题加群解决。 声明: ... &lt;scope&gt;provided&lt;/scope&gt; 如果还是不能解决问题,请在官方群(群号:259217951)内搜索“jstl” 如图下载依赖包。

    discord-bot-template

    在Google中输入以下链接: : client_id YOUR-ID permissions scope bot,但将YOUR-ID替换为机器人的客户端ID(您可以通过访问 ,单击您的应用程序,然后复制客户端ID)。 这应该会弹出邀请屏幕。

    npm-cli-login

    npm-cli-login 允许您不使用STDIN,STDOUT登录到NPM。 在CI构建系统等地方使用。...NPM_RC_PATH :(可选)要更新的自定义.npmrc文件的路径(默认: ~/.npmrc ) 设置所需的密码后,您只需运行以下命令即可登录

Global site tag (gtag.js) - Google Analytics