Web系统是最常见的Java应用系统之一,现在流行的Web项目多使用ssm或ssh框架,使用spring进行bean的管理,这为我们编写web项目带来了很多方便,通常,我们的controler层使用注入的service层的bean对象,service层使用注入的dao层的bean对象。但是大家在使用的时候有没有思考这样一个问题:如果有两个一样的请求进入我们的系统,那么他们使用的spring注入的service层的bean对象和dao层的bean对象,是同一个对象吗?

  这个问题的答案就要讲到bean的作用域了,在spring2.0之前bean只有2种作用域即:singleton(单例)、non-singleton(也称 prototype), Spring2.0以后,增加了session、request、global session三种专用于Web应用程序上下文的Bean。singleton是默认的作用域,当一个bean的 作用域设置为singleton, 那么Spring IOC容器中只会存在一个共享的bean实例,并且所有对bean的请求,只要id与该bean定义相匹配,则只会返回bean的同一实例。而prototype作用域部署的bean,每一次请求(将其注入到另一个bean中,或者以程序的方式调用容器的 getBean()方法)都会产生一个新的bean实例,相当与一个new的操作。我以前没有关注过这个问题,是因为我在项目中通常使用的bean,无论是service层的,还是dao层的,都是没有状态的bean,里面只有方法,没有成员变量。在使用这样的bean的时候,多个线程访问同一个bean不会产生线程安全问题。

  而当我们需要使用有状态的bean的时候,这个bean中具有可变的成员变量,不同的特定用户使用的bean具有不同的状态(成员变量的值),这些状态不在整个Web工程中通用。当多线程访问bean修改成员变量时,就会产生与预期不符的结果。

  举个例子,下面自定义了一个bean,使用注解的方式进行注册,其中只有一个String的param参数:  

@Component
public class RequestInfo { private String param; public String getParam() {
return param;
} public void setParam(String param) {
this.param = param;
} }

  然后定义两个java类(我使用的webx框架进行的测试,功能类似与struts2,读者可自行编写类似的两个请求及对应java类),一个将url中携带的param参数值存放到RequestInfo对象中:

public class SetParamValve implements Valve {

    @Autowired
private HttpServletRequest request; @Autowired
private RequestInfo requestInfo; @Override
public void invoke(PipelineContext pipelineContext) throws Exception { System.out.println("SetParamValve--now param is:" + requestInfo.getParam()); //获取请求中的参数值
String param = HttpUtil.getRequestParameter(request, "param");

     //存放参数
requestInfo.setParam(param); //睡眠10s
TimeUnit.SECONDS.sleep(10); pipelineContext.invokeNext(); } }

  一个取出RequestInfo对象中存放的参数值:

public class GetParamValve implements Valve {

    @Autowired
private RequestInfo requestInfo; @Override
public void invoke(PipelineContext pipelineContext) throws Exception { System.out.println("GetParamValve--now param is:" + requestInfo.getParam()); pipelineContext.invokeNext(); } }

  

  下面开始实验:当一个请求url:”http://localhost:8080/index.htm?param=a“进行访问时,控制台打印:

  

  这是符合预期输出的,我们再使用两个url:”http://localhost:8080/index.htm?param=a“ 和 ”http://localhost:8080/index.htm?param=b“ 在同一浏览器先后进行请求,打印:

  

  可以看到,GetParamValve输出了两个”b“,而我们的预期是:”http://localhost:8080/index.htm?param=a“的请求打印a,”http://localhost:8080/index.htm?param=b“的请求打印b,与预期不一致的原因是因为两个请求共用一个”RequestInfo“bean对象,param的值只能保存一份。

  解决这个问题的一个方式就是使用spring的scope作用域,scope的取值及区别如下:

singleton

在每个Spring IoC容器中一个bean定义对应一个对象实例。

prototype

一个bean定义对应多个对象实例。

request

在一次HTTP请求中,一个bean定义对应一个实例;即每次HTTP请求将会有各自的bean实例, 它们依据某个bean定义创建而成。该作用域仅在基于web的Spring ApplicationContext 情形下有效。

session

在一个HTTP Session 中,一个bean定义对应一个实例。该作用域仅在基于web的SpringApplicationContext 情形下有效。

global session

在一个全局的HTTP Session 中,一个bean定义对应一个实例。典型情况下,仅在使用portlet context的时候有效。该作用域仅在基于web的Spring ApplicationContext 情形下有效。

  我们使用”requset“范围的scope,使用注解进行配置:

@Component
@Scope(value="request")

  当使用request和session范围时,需要在web.xml中加入如下配置,

<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>

  启动项目,却发现项目报错如下:

  大意是一个singleton类型的bean对象如果使用我们的bean,我们需要提供一个代理,解决方法如下:

@Component
@Scope(value="request",proxyMode=ScopedProxyMode.TARGET_CLASS)

  这时还有另一个问题,当没有requset请求时(在非web环境中使用),我们调用这个bean对象失败怎么办?解决方法是我们为每一个使用这个bean对象的线程提供一个默认的request上下文:

<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="session">
<bean class="org.springframework.context.support.SimpleThreadScope"/>
</entry>
</map>
</property>
</bean>

  做好这些,我们启动项目,使用”http://localhost:8080/index.htm?param=a“和”http://localhost:8080/index.htm?param=b“在同一浏览器先后进行请求时,打印如下:

  

  可以发现两次请求分别拥有独自的RequestInfo bean对象,它们的param值分别是我们期望的a和b。

  小结:本文主要说了在多线程环境下使用spring的有状态bean对象可能会发生的线程安全问题以及解决方法,并举了一个小例子进行说明,可能会有大牛觉得我又把简单的问题说复杂了。。。但是如果有读者原来不了解spring的scope,看了这篇文章后感觉有一点收获,我就很开心啦,文中有说的不好的地方欢迎大家指出~

  

 

Spring Scope:Web项目中如何安全使用有状态的Bean对象?的更多相关文章

  1. 重新学习Spring一--Spring在web项目中的启动过程

    1 Spring 在web项目中的启动过程 Spring简介 Spring 最简单的功能就是创建对象和管理这些对象间的依赖关系,实现高内聚.低耦合.(高内聚:相关性很强的代码组成,既单一责任原则:低耦 ...

  2. Spring在Web项目中的三种启动加载的配置

    在最近的项目中,使用到了spring相关的很多东西,有点把spring的配置给搞混了,从网上查到的资料以及整理了一下. 在Web项目中,启动spring容器的方式有三种,ContextLoaderLi ...

  3. Java Web学习系列——Maven Web项目中集成使用Spring、MyBatis实现对MySQL的数据访问

    本篇内容还是建立在上一篇Java Web学习系列——Maven Web项目中集成使用Spring基础之上,对之前的Maven Web项目进行升级改造,实现对MySQL的数据访问. 添加依赖Jar包 这 ...

  4. web项目中获取spring的bean对象

    Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架,如何在程序中不通过注解的形式(@Resource.@Autowired)获取Spring配置的bean呢? Bean工厂(c ...

  5. 在普通WEB项目中使用Spring

    Spring是一个对象容器,帮助我们管理项目中的对象,那么在web项目中哪些对象应该交给Spring管理呢? 项目中涉及的对象 ​ 我们回顾一下WEB项目中涉及的对象 Servlet Request ...

  6. web项目中加入struts2、spring的支持,并整合两者

    Web项目中加入struts2 的支持 在lib下加入strut2的jar包 2. 在web.xml中添加配置 <filter> <filter-name>struts2< ...

  7. web项目中 集合Spring&使用junit4测试Spring

    web项目中 集合Spring 问题: 如果将 ApplicationContext applicationContext = new ClassPathXmlApplicationContext(& ...

  8. 06_在web项目中集成Spring

    在web项目中集成Spring 一.使用Servlet进行集成测试 1.直接在Servlet 加载Spring 配置文件 ApplicationContext applicationContext = ...

  9. Axis2在Web项目中整合Spring

    一.说明: 上一篇说了Axis2与Web项目的整合(详情 :Axis2与Web项目整合)过程,如果说在Web项目中使用了Spring框架,那么又改如何进行Axis2相关的配置操作呢? 二.Axis2 ...

随机推荐

  1. canvas初探2

    2.2 canvas的绘图环境 canvas仅仅只是一个绘图的容器,其内存在一个绘图环境,该环境对象提供了全部的绘图功能. 目前canvas的绘图环境是2d,但canvas规范在着手准备支持其他类型的 ...

  2. WiFi流量劫持—— JS脚本缓存投毒

    在上一篇<WiFi流量劫持—— 浏览任意页面即可中毒>构思了一个时光机原型,让我们的脚本通过HTTP缓存机制,在未来的某个时刻被执行,因此我们可以实现超大范围的入侵了. 基于此原理,我们用 ...

  3. NodeJs 开发微信公众号(二)测试环境部署

    由于卤煮本人是做前端开发的,所以在做公众号过程中基本上没有遇到前端问题,在这方面花的时间是最少的.加上用了mui框架(纯css界面)和自己积累的代码,很快地开发出了界面来.接着是后台开发.卤煮选的是n ...

  4. clojure基础入门(一)

    最近在看storm的源码,就学习分享下clojure语法. 阅读目录: 概述 变量 运算符 流程控制 总结 概述 clojure是一种运行在JVM上的Lisp方言,属于函数式编程范式,它和java可以 ...

  5. AutoMapper使用手册(一)

    阅读目录 1. 介绍 2. 基本使用 3. 自动分割映射(Flattening) 4. 自定义字段映射(Projection) 5. 验证配置(Configuration validation) 介绍 ...

  6. Azure PowerShell (9) 使用PowerShell导出订阅下所有的Azure VM的Public IP和Private IP

    <Windows Azure Platform 系列文章目录> 笔者在之前的工作中,有客户提出想一次性查看Azure订阅下的所有Azure VM的Public IP和Private IP. ...

  7. C# 服务程序 - 调试服务

    前言:本篇文章环境是VS2015,win10.如果有任何的差别,请注意 1. 创建服务程序 1)用VC创建服务程序,叫做 MyTestWindowsService 创建完成之后,可以看到 2)添加安装 ...

  8. css+div常用属性备忘录

    学习软件设计有一年多了,明年五月就要毕业了.回头看看发现自己其实挺差劲的. 最近开通了博客所以就整理了一下笔记,在这里发布一下自己以前学习css时总是记不住去翻书又很常用的属性,都是一些很基础的. 大 ...

  9. Nginx负载均衡的详细配置及使用案例详解.

    感谢看过这一些列博文和评论的小伙伴, 我把自己所看到的学到的拿到这里来分享是想和大家一起学习进步, 想听听园友给出的意见, 也是对自己学习过程的一个总结. 技术无止境, 我们仍需努力! 1,话不多说, ...

  10. 程序中保存状态的方式之Cookies

    程序中保存状态的方式之 Cookies,之前写过一篇关于ViewState的.现在继续总结Cookies方式的 新建的测试页面login <%@ Page Language="C#&q ...