当前位置:首页 > 时尚生活

sessionscope Spring 源码解析- Scopes 之 Request 、Session 、Application

来自:开源中国,作者:Max

链接:my.oschina.net/wang5v/blog/3017934

请求、会话和应用的概念

在这个Spring源代码分析-Singleton Scope(单例)和Prototype Scope(多例)的博客中,介绍了两个常用的作用域,同时在这个博客中简单介绍了这三个不常用的作用域的概念。今天,我们将详细揭示这三个不常用的范围。这三个只能在web应用中使用,也就是说,要在Web中使用的Spring应用上下文(例如,XmlWebApplicationContext)。如果您在非web应用程序中使用它们(例如,ClassPathXmlApplicationContext),您将引发异常。

请求范围

首先要介绍的是Request。顾名思义,如果bean定义了这个范围,那么它表明这个bean的生命周期是在每个HTTP请求级别。换句话说,在不同的HTTP请求中,请求范围的bean将根据bean定义重新实例化,并保存在RequestAttribute中。也就是说,这个实例只在请求的整个过程中有效和可见。当请求完成时,这个bean将被丢弃,它的生命周期非常短。因为每个请求都是独立的,所以您修改请求范围的bean仅对内部可见,而由同一bean定义创建的其他实例是不可察觉的。如何定义请求范围?Spring提供了两种方式:一种是XML配置;

& ltbean id = " TestRequest " class = " com . demo . TestRequest " scope = " request "/& gt;

另一种是注释的方式:

@RequestScope

@组件

publicclassTestRequest{

// ...

}

这样,我们指定这个Bean的范围是请求的范围。但是,请求、会话和应用程序的使用不限于此。稍后,我们将更详细地介绍如何在应用程序中使用这三样东西。

会话范围

让我们来谈谈这个会话范围,会话级别。作用域被指定为Session的bean,Spring容器使用bean定义在单个HTTP会话的生命周期中创建新的bean实例,也就是说,在每个会话中,Session Bean都会被实例化并保存在RequestAttribute中。与请求不同,每个会话将只实例化一次,而请求将为每个请求实例化一次。当我们定义Session的bean时,它标志着这个bean的生命周期处于一个完整的会话中,所以bean的内部状态在一个特定的HTTP Session中被修改,而在另一个HTTP Session实例中按照相同的bean定义创建的实例是无法察觉的。当会话结束时,bean将被丢弃。还有两种定义会话范围的方法:一种是XML方法:

& ltbean id = " TestRequest " class = " com . demo . TestRequest " scope = " session "/& gt;

另一种是注释的方式:

@SessionScope

@组件

publicclassTestRequest{

// ...

}

适用范围

由应用程序标记的bean表示Spring容器通过为整个网络应用程序使用一次bean定义来创建bean的新实例。也就是说,Application Scope bean的作用域是在ServletContext级别,它被存储为一个常规的ServletContext属性。这个单一的例子是相似的,但不同。每个ServletContext都是一个单独的实例,但是对于Spring的每个ApplicationContext并不一定是这样。还有两种定义应用程序范围的方法:一种是XML方法:

& ltbean id = " TestRequest " class = " com . demo . TestRequest " scope = " application "/& gt;

另一种是注释的方式:

@应用范围

@组件

publicclassTestRequest{

// ...

}

将短期bean依赖注入到长期bean中

当我们想要将一个生命周期短的bean(比如Request)注入到一个生命周期长的bean(比如Singleon)中时,我们不能像定义单个实例那样注入一个实例。众所周知,依赖注入只发生在bean实例化之后,并且依赖注入之后的实例不会再发生变化,也就是说bean只会实例化一次,然后就不会发生变化,这显然违反了Request、Session等生命周期短的原则。因为像请求一样,在每个请求中都需要重新实例化一个对象。如果只是简单的使用一个简单的bean定义,这显然是不一致的。让我们来介绍一下

& ltbean id = " TestRequest " class = " com . demo . TestRequest " scope = " session " & gt;

& ltaop:scoped-proxy/

& lt/bean>。

添加这个标记后发生了什么变化将在下面的源代码分析中介绍。其实在实践中,如果你依赖长命豆中的短命豆,如果你不加

请求、会话和应用的简单应用

请求、会话和应用程序范围只能在支持web的Spring ApplicationContext的实现中使用(如XmlWebApplicationContext)。如果这些作用域与常规的Spring IoC容器一起使用,如ClassPathXmlApplicationContext,将引发IllegalStateException异常。因此,我们只能在web应用中使用上述三种范围。为了使网络应用程序支持请求、会话和应用程序级范围,在定义bean之前,有必要进行一些小的初始配置。标准范围、单例和原型不需要这个初始设置。如果我们使用Spring MVC,我们可以使用上面的三个作用域而不需要设置任何东西,因为DispatcherServlet公开了这三个作用域。但是,如果您正在使用诸如Strust之类的web应用程序,您需要对其进行配置,并将以下段落添加到web.xml中:

& lt过滤器>设置。

& lt过滤器-名称>;requestContextFilter<。/filter-name>。

& ltfilter- class >;org . spring framework . web . filter . RequestContextFilter & lt;/filter- class>。

& lt/filter>。

& lt过滤器-映射>

& lt过滤器-名称>;requestContextFilter<。/filter-name>。

& lturl模式>。/* & lt;/URL-模式>。

& lt/filter-mapping>。

请求、会话和应用程序的源代码分析

作用域代理改变了什么

将普通bean定义添加到这个标记之后,spring会将这个bean变成代理工厂的bean定义。具体代码如下:

publicationstationdefinitionholder createScopedProxy(bean definition onholder定义,

BeanDefinitionRegistry注册表,布尔代理目标类){

string original BanName = definition . GetBeanName();

BeanDefinition TargetDefinition = definition . GetBeanDefinition();

string TargetBeanName = GetTargetBeanName(original BeanName);

//为原始bean名称创建一个范围代理定义,

//在内部目标定义中隐藏目标bean。

rootbandefinition proxyDefinition = new rootbandefinition(ScopedProxyFactoryBean。类);

proxydefinition . setdecoratedefinition(new eandedefinition onholder(target definition,target bean name));

proxydefinition . setoriginatingbedefinition(target definition);

proxyDefinition . SetSource(definition . GetSource());

proxyDefinition . SetRole(TargetDefinition . GetRole());

proxy definition . GetPropertyValues()。添加(" targetBeanName ",TargetBeanName);

if(proxyTargetClass) {

target definition . SetAttribute(AutoProxyTils。PRESERVE _ TARGET _ CLASE _ ATTRIBUTE,布尔值。真);

// ScopedProxyFactoryBean的“proxyTargetClass”默认值为TRUE,因此我们不需要在这里显式设置。

}

else{

proxy definition . GetPropertyValues()。添加(" proxyTargetClass ",布尔值。FALSE);

}

//从原始bean定义中复制autowire设置。

proxyDefinition.setAutowireCandidate考生(targetDefinition.isAutowireCandidate考生());

proxydefinition . set primary(target definition . is primary());

if(TargetDefinition instance of AbstractBeanDefinition){

proxydefinition . copy qualifiers from((抽象定义)目标定义);

}

//忽略目标bean,用作用域代理替换它。

targetDefinition.setAutowireCandidate考生(false);

target definition . set primary(false);

//在工厂中将目标bean注册为单独的bean。

registry . RegisterBeanDefinition(TargetBeanName,TargetDefinition);

//将作用域代理定义作为主bean定义返回

//(可能是内部bean)。

returnNewBeanDefinitionHolder(ProxyDefinition,originalBeanName,definition . Getaliases());

}

从上面的代码可以看出

@覆盖

publicObject getObject(){

if( this.proxy == null) {

throwNewfactorybeannotinitializeexception();

}

returnthis.proxy

}

实例化将返回代理的一个实例,代理是在哪里创建的?继续找,由于ScopedProxyFactoryBean实现了BeanFactoryWare接口,代理实例是在BeanFactoryWare接口的setBeanFactory方法中实现的。我们来看看具体的实现:

@覆盖

public void setBeanFactory(BeanFactory BeanFactory){

if(!(BeanFactory instance of configurationableBeanFactory)){

thrownewIllegalStateException("不在可配置的BeanFactory中运行:"+bean factory);

}

可配置的工厂cbf =(可配置的工厂)工厂;

this . SCOpedTargetSource . SetBeanFactory(BeanFactory);

proxy factory pf = new proxy factory();

pf . copy from(this);

pf . settargetsource(this . scopedtargetsource);

类别<。?>。BeanType = BeanFactory . GetType(this . TargetBeanName);

if(beanType == null) {

thrownewIllegalStateException("无法为bean创建作用域代理' "+ this.targetBeanName +

“”:在创建代理时无法确定目标类型。");

}

if(!isProxyTargetClass()| | BeanType . Isinterface()| | Modifier . isPrivate(BeanType . GetModifiers())){

pf . setinterfaces(Classutils . GetAllinterfacesForClass(BeanType,CBF . GetBeanClassLoader()));

}

//添加只实现ScopedObject上的方法的介绍。

scope object scope object = new defaultscope object(CBF,this . scopedtargetsource . gettargetbeanname());

pf . addadvice(new delegatingintroductioninterceptor(scope object));

//添加AopInfrastructureBean标记,以指示作用域代理

//本身不受自动代理!只有它的目标bean是。

pf . addinterface(aopinfrastructureBean。类);

this . proxy = pf . GetProxy(CBF . GetBeanClassLoader());

}

这样就创建了一个新的代理,就是通过编程来编写AOP。具体细节不讨论。只要我们知道这个代理,代理的来源就是pf . settargetsource(this . scopedtargetsource);当代理执行时,它将获得这个目标源并执行它的一个方法getTarget,如下所示:

@覆盖

publicObject getTarget()引发异常{

returngetBeanFactory()。GetBean(GetTargetBeanName());

}

因此,可以看出,标签

实例化请求、会话、应用程序Bean

实例化请求、会话等。将进入Spring的实例化过程。在Spring源代码分析-Singleton Scope(单个案例)和Prototype Scope(多个案例)中,提到了单个案例和多个案例的创建。其实还有第三个过程,就是范围的实例化过程,除了单个案例和多个异常。一些代码如下:

else{

//如果不是单个案例,也不是多个案例,就进入这个分支

string SCOpe name = mbd . GetScope();

//先获取支持的sope

final Scope Scope = this . scopes . get(ScopeName);

if(scope == null) {

thrownewIllegalStateException("没有为作用域名称' "+作用域名称+" ' "注册的作用域");

}

尝试{

//实例化并放入RequestAttribute或ServletContext属性中

object SCOpedinstance = scope . get(BeanName,newObjectFactory & lt对象>() {

@覆盖

publicObject getObject()引发BeansException {

BeforePrototype creation(BeanName);

尝试{

returncreateBean(beanName,mbd,args);

}

最后{

afterPrototypeCreation(BeanName);

}

}

});

//处理上一篇文章中提到的工厂对象

bean = GetObjectForBeanInstance(ScopeDinstance,name,beanName,mbd);

}

catch(IllegalStateException ex){

throwNewBeacreationException(BeanName,

“作用域“+”对当前线程无效;考虑"+

如果您打算从单例引用它,请为此bean定义一个作用域代理,

ex);

}

}

关键代码是作用域的get方法。接下来,让我们看看它们的具体实现:请求范围的实现:

@覆盖

公共对象获取(字符串名称,对象工厂& lt?>。objectFactory){

RequestAttributes attributes = RequestContextholder . CurrentRequestAttributes();

object scope object = attributes . GetAttribute(name,GetScope());

if(scopedObject == null) {

scope object = objectFactory . GetObject();

attributes.setAttribute(name,scopedObject,GetScope());

}

returnscopedObject

}

从代码中可以看出,首先从属性中获取范围对应的对象实例;如果它被直接返回,那么如果它不能被获取,对象将被重新实例化;请求和会话都将执行上述代码,这些代码是在抽象类抽象请求属性范围中实现的。接下来,看看Session的具体实现,如下所示:

@覆盖

公共对象获取(字符串名称,对象工厂& lt?>。objectFactory){

对象互斥= RequestContextholder . CurrentRequestAttributes()。getsessionMutex();

同步(互斥){

returnsuper.get(名称,ObJectFactory);

}

}

首先他会先获取当前会话互斥,同步操作,保证会话创建实例只创建一次,其他的都从会话中取,虽然都是RequestAttribute存储的,但是内部实现不是。看看代码:

@覆盖

publicvoidsetAttribute(字符串名称,对象值,intscope){

//请求范围

if(scope == SCOPE_REQUEST) {

if(!isRequestActive()) {

thrownewIllegalStateException(

"无法设置请求属性-请求不再活动!");

}

this.request.setAttribute(名称,值);

}

//范围是会话

else{

http session session = GetSession(true);

this . sessionattributestoupdate . remove(name);

session.setAttribute(名称,值);

}

}

SetAttribute实际上是根据不同的作用域进行不同的处理。会话放在http会话中,请求放在http请求的属性中。所以从这里可以看出,请求会针对每个请求进行实例化,在单个请求中是同一个实例,请求完成时会被销毁;会话在一个完整的会话中只实例化一次,实例化后将缓存在会话中。

总结

至此,Spring的所有作用域都已经基本说明了,我们基本可以知道这些作用域的使用方法以及各自的原理。我们可以根据业务需求使用不同的范围,使用单个异常以外的范围需要谨慎使用,否则不会有效果,可能会适得其反。

1.《sessionscope Spring 源码解析- Scopes 之 Request 、Session 、Application》援引自互联网,旨在传递更多网络信息知识,仅代表作者本人观点,与本网站无关,侵删请联系页脚下方联系方式。

2.《sessionscope Spring 源码解析- Scopes 之 Request 、Session 、Application》仅供读者参考,本网站未对该内容进行证实,对其原创性、真实性、完整性、及时性不作任何保证。

3.文章转载时请保留本站内容来源地址,https://www.lu-xu.com/shehui/1051826.html

上一篇

女子花3万请道士作法 为求与前男友复合也是够拼的

下一篇

北京初雪 真相原来是这样!

自定义戒指图片 不只是刻字印!钻戒定制里这6个细节都可以自定义!

  • 自定义戒指图片 不只是刻字印!钻戒定制里这6个细节都可以自定义!
  • 自定义戒指图片 不只是刻字印!钻戒定制里这6个细节都可以自定义!
  • 自定义戒指图片 不只是刻字印!钻戒定制里这6个细节都可以自定义!

低聚异麦芽糖的作用与功效 快美康带你了解什么是低聚异麦芽糖?

  • 低聚异麦芽糖的作用与功效 快美康带你了解什么是低聚异麦芽糖?
  • 低聚异麦芽糖的作用与功效 快美康带你了解什么是低聚异麦芽糖?
  • 低聚异麦芽糖的作用与功效 快美康带你了解什么是低聚异麦芽糖?

煅牡蛎的功效与作用 牡蛎的功效与作用

  • 煅牡蛎的功效与作用 牡蛎的功效与作用
  • 煅牡蛎的功效与作用 牡蛎的功效与作用
  • 煅牡蛎的功效与作用 牡蛎的功效与作用

吉林大范围雾凇景观 还原事发经过及背后原因!

  • 吉林大范围雾凇景观 还原事发经过及背后原因!
  • 吉林大范围雾凇景观 还原事发经过及背后原因!
  • 吉林大范围雾凇景观 还原事发经过及背后原因!
苏打水的危害 苏打水的作用 喝苏打水有这么多好处

苏打水的危害 苏打水的作用 喝苏打水有这么多好处

苏打水是一种非常受欢迎的饮料。味道很特别,很甜。你有什么?很多人不知道汽水是什么,所以?汽水对身体有好处,那么汽水有什么好处呢? 苏打水的作用 1.喝苏打水往往能让大脑保持活力,对提高记忆力有好处,所以青少年可以多喝苏打水。苏打水还含有大量的微量元素,以及有助于改善记忆的维生素E。所以青少年...

吃长期避孕药有什么副作用 短效避孕药,每天都吃的那种,都有哪些副作用?

吃长期避孕药有什么副作用 短效避孕药,每天都吃的那种,都有哪些副作用?

避孕是计划生育工作的重点。育龄夫妇如何选择合适有效的避孕方法,是关系到夫妻关系和育龄妇女身心健康的重要问题。除宫内节育器外,最常用的避孕药是避孕套和短效口服避孕药。 避孕套通过阻断精子进入女性生殖道来达到避孕目的,具有操作方便、不受女性月经周期限制、无药物毒副作用、对性传播疾病有一定预防作用...

黄豆的功效与作用禁忌 黄豆酱的功效与作用与禁忌,其实吃它很省钱

  • 黄豆的功效与作用禁忌 黄豆酱的功效与作用与禁忌,其实吃它很省钱
  • 黄豆的功效与作用禁忌 黄豆酱的功效与作用与禁忌,其实吃它很省钱
  • 黄豆的功效与作用禁忌 黄豆酱的功效与作用与禁忌,其实吃它很省钱
益阳烈士赵宇 益阳市总工会女职工委员发挥“半边天”作用!

益阳烈士赵宇 益阳市总工会女职工委员发挥“半边天”作用!

《益阳大客户报》(记者吴婷通讯员刘芳)3月6日,益阳市工会第五届女职工委员会第一次全体(扩大)会议召开。会议的目的是团结动员全市广大女职工,听党的话,坚定不移地跟党走,为建设“五个益阳”而不懈努力。市人大常委会副主任、市总工会主席王刚强希望妇联组织和各级工会女职工加强责任,推动工作创新发展。...