`

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的方法示例

    然而,当这些内置的scope仍无法满足特定需求时,Spring允许我们自定义scope。要实现自定义scope,你需要实现`org.springframework.beans.factory.config.Scope`接口。这个接口提供了四个方法: 1. `Object get...

    详解Spring中bean的scope以后使用

    此外,Spring还允许开发者自定义作用域,以满足特定的需求。 #### 三、Singleton作用域 **Singleton**是Spring中最常用的作用域之一。当一个Bean被定义为Singleton时,Spring容器只会为这个Bean创建一个实例,并将...

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

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

    Spring java注解,元注解和自定义注解

    ### Spring Java注解、元注解与自定义注解详解 #### 一、Java注解简介 在深入了解Spring框架中的注解应用之前,我们首先需要对Java注解有一个基本的认识。Java注解(Annotation)是一种元数据,可以为程序代码添加...

    尚学堂_Spring_0600_IOC_Bean_Scope

    此外,Spring还提供了自定义Scope的机制,开发者可以根据需求定义自己的Bean作用域。 为了深入学习这个主题,可以参考提供的博文链接(虽然这里没有给出具体的链接内容,通常这些链接会包含博主的个人理解和实践...

    SpringBoot中自定义日志配置logback-spring.xml示例源码

    &lt;springProperty scope="context" name="logLevel" source="logging.level.root"/&gt; &lt;pattern&gt;%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n &lt;file&gt;logs/app.log &lt;pattern&gt;%d{HH:mm:...

    spring 2.5 开发基础包

    9. **Bean的生命周期管理**:Spring允许自定义bean的初始化和销毁方法,以及定义bean的scope(如单例、原型等),提供了对bean生命周期的精细控制。 10. **集成测试**:Spring提供了JUnit集成,通过`@...

    ssh2框架整合,struts2和hibernate均交由spring管理,用注解的方式由spring注入

    另外,`@Component`、`@Service`、`@Repository`和`@Controller`等注解用于定义不同类型的bean,`@Scope`注解可以指定bean的作用范围。 6. **整合步骤**: - 配置Struts2,添加Struts2和Spring的整合插件。 - ...

    SPRING技术内幕:深入解析SPRING架构与设计原理

    3. **Bean的生命周期管理**:书中详细解释了Spring如何创建、初始化、配置、销毁Bean,以及如何自定义这些过程,包括Bean的scope、初始化方法、销毁方法等。 4. **数据访问集成**:Spring支持多种数据库访问方式,...

    spring-context-4.2.xsd.zip

    `spring-context-4.2.xsd`还定义了处理bean作用域(scope)、AOP代理(proxy)、事件发布(event publishing)等功能的相关元素。例如,`scope`属性允许我们设置bean的作用域,可以是`singleton`(单例)、`...

    Spring Bean 加载顺序 .

    Spring允许自定义BeanPostProcessor,它们在Bean实例化后执行特定的处理,比如AOP代理。这些后处理器将在Bean实例化后,初始化前介入。 7. **Bean初始化**: 实例化完成后,Spring会执行Bean的初始化方法,包括...

    Restlet与Spring 集成

    `BaseApplication`类是自定义的Restlet应用程序,它使用`component.context`和`restRoute`。`restRoute`是一个Spring路由器,通过`attachments`映射将URL路径与处理资源的Spring Bean关联。 - **userContext.xml**...

    spring-webflow-reference

    - Spring Web Flow 2.3引入了多项新功能,包括流程嵌入页面、支持JSR-303 Bean验证、流程管理的持久化上下文传播、Portlet 2.0资源请求和自定义对话管理等。 - Spring Web Flow 2.2则增加了对JSF 2的支持、Spring ...

    spring-demo03-spring创建对象的5种方式.zip

    Spring的`FactoryBean`接口允许自定义Bean的创建逻辑。`FactoryBean`的`getObject()`方法返回实际的对象。这种方式适用于更复杂的实例化场景,如创建JDBC数据源或线程池。例如: ```java public class ...

    spring注解笔记

    默认情况下,Spring Bean是单例模式(scope="singleton"),意味着每个Spring容器中只有一个Bean实例。如果我们需要每次请求都创建一个新的Bean实例,可以使用@Scope("prototype")来声明Bean的作用域为原型。这样,...

    spring 中特殊bean用法

    特殊Bean用法主要涉及Spring提供的多种高级特性,包括但不限于工厂方法、 prototype scope、AOP代理、SpEL表达式、条件注解等。下面将详细介绍这些特殊用法。 1. **工厂方法**: 在某些情况下,我们可能需要自定义...

    Spring AOP IOC源码笔记.pdf

    在Spring中,Bean可以具有不同的作用域(scope),如singleton(单例)、prototype(原型)、request、session等。此外,通过`lazy-init`属性可以设置延迟初始化,`init-method`和`destroy-method`指定初始化和销毁...

    spring-reference.pdf

    Spring允许用户通过实现特定接口来自定义容器的行为,例如定义自定义Bean后处理器、自定义作用域等。 以上是Spring 2.0.6版本的一些关键知识点总结,这些知识点覆盖了Spring框架的核心概念和技术细节,对于理解和...

    spring 4.0.3

    9. **AOP增强**:Spring的AOP框架在4.0.3中引入了更多的切面编程特性,例如支持JSR-330标准的`@Scope`注解,以及对环绕通知的改进。 10. **安全框架**:Spring Security 4.0.3版本提供了更强大的认证和授权机制,...

Global site tag (gtag.js) - Google Analytics