Spring MVC不要在@Service bean中保存状态

先看这么一段代码:

@Service
public class AccountService {
	private String message;

	public void foo1() {
		if (true) {
			this.message = "a";
		} else {
			this.message = "b";
		}
	}

	public void foo2() {
		// 修改this.message的代码...
		// ... ...
	}
}

如果你打算在@Controller里这么调用AccountService :

accountService.foo1();
		model.addAttribute(accountService.getMessage());

那么就有线程安全的危险了。

问题原因

在Spring中,bean的默认scope是singleton,也就是说容器中只有一个bean的实例。而在Java Web环境中,web服务器会为每一个请求创建一个线程来处理它。这样一来,在@Controller中调用@Service bean的方法就会导致有多个线程在执行@Service方法,例如线程A在执行foo1()方法,线程B在执行foo2()方法。那么问题来了,多个线程同时读写message成员变量,就可能让getMessage()方法返回错误的值

解决方法

1. 将@Service bean的scope改为 "request",即:

@Service
@Scope("request")
public class AccountService {
	private String message;

这样Spring会为每一个请求分别创建一个AccoutService对象,每个线程都有自己的message变量,就不会出错了。但坏处是创建@Service bean的开销往往比较大,会导致程序性能下降。

2. 使用不可变对象(Immuable Object)封装message变量

定义如下类:

class MessageWrapper {
	private String message;

	public MessageWrapper(String msg) {
		this.message = msg;
	}

	// 只提供get方法
	public String getMessage() {
		return this.message;
	}
}

AccountService的foo1()方法修改如下:

@Service
public class AccountService {
	public MessageWrapper foo1() {
		if (true) {
			return new MessageWrapper("a");
		} else {
			return new MessageWrapper("b");
		}

		// ... ...
	}

这样便可以完美避免线程安全问题,又不会带来过多的额外开销。

时间: 2024-10-03 13:44:22

Spring MVC不要在@Service bean中保存状态的相关文章

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

程序中保存状态的方式之 Cookies,之前写过一篇关于ViewState的.现在继续总结Cookies方式的 新建的测试页面login <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Login.aspx.cs" Inherits="Login" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML

在C 函数中保存状态:registry、reference和upvalues

在C函数中保存状态:registry.reference和upvalues C函数能够通过堆栈来和Lua交换数据,但有时候C函数须要在函数体的作用域之外保存某些Lua数据.那么我们想到全局变量或static变量,这样做的缺点是:(1)为Lua设计C函数库时,导致不可重入.(2)不是全部的Lua值都能非常好的保存到C变量中.那么可不能够将值保存在Lua全局变量里面呢,能够,Lua就提供了一个独立的被称为registry的表,可是Lua代码本身不能訪问它. 1.registry全局注冊表 解释:一个

[Lua]在C函数中保存状态--注册表,环境表,upvalue

什么叫做在C函数中保存状态?比如你现在使用Lua调用了C函数Func1,但是Func1中有一些数据在调用完以后保存下来,供以后使用.而这些数据就是所谓的状态,也就是我们需要保存的东东.有人就会说了,Lua调用C时,把所有的需要保存的状态都返回到Lua中,当调用下一个函数时,将需要的状态当做参数再传进去,不错,是一个办法,但是很麻烦.方法一:注册表:方法二:环境:方法三:upvalue. 注册表是一个全局的table,它只能被C代码访问.通常,可以用它来保存那种需要在几个模块中共享的数据: 但是,

程序中保存状态的方式之ViewState

程序中保存状态的方式有以下几种: 1.Application 2.Cookie 3.Session 4.ViewState:ViewState是保存状态的方式之一,ViewState实际就是一个Hidden字段,但是它是服务器控件状态保存的基础如下代码 1 <form method="post" action="./pay.aspx" id="form1"> 2 <div class="aspNetHidden&quo

spring MVC 管理HttpClient---实现在java中直接向Controller发送请求

在spring MVC中,大多数时候是由客户端的页面通过ajax等方式向controller发送请求,但有时候需要在java代码中直接向controller发送请求,这时可以使用HttpCilent实现. 首先用到的包是httpclient-4.3.5.jar和httpcore-4.3.2.jar 先看下面代码: package module.system.common; import java.io.IOException; import java.util.ArrayList; import

spring mvc 接收表单 bean

spring MVC如何接收表单bean 呢? 之前项目中MVC框架一直用struts2,所以我也就按照struts2 的思维来思考 页面loginInput.jsp: Html代码   <?xml version="1.0" encoding="UTF-8" ?> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEnco

利用Spring MVC搭建REST Service

之前写过一篇 利用JAX-RS快速开发RESTful 服务 今天来看下spring-mvc框架如何实现类似的功能: 一.pom.xml 1 <?xml version="1.0" encoding="UTF-8"?> 2 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-insta

mybatis+spring mvc 完美整合方案 查询,保存,更新,删除自动生成

Jeecg-Mybatis版本代码生成器演示视频 代码下载:JEECG-mybatis参考学习版本 简要说明 JEECG[J2EE Code Generation] 代码生成:根据表生成对应的Bean,Service,Dao,Action,XML,JSP等,增删改查功能直接使用,实现了快速开发 jeecg-mybatis-framework,采用SpringMVC+Mybatis等主流框架 支持数据库: Mysql,Oracle10g 前端:使用Jquery和Easyui技术.JS封装简洁,操作

spring mvc 配置后,web中的html页面报404,该怎么处理

问题描述: 在根目录webapp下的jsp页面可以通过url直接访问,而html页面就会报404错误. 解决方案1: 在spring-mvc.xml中添加如下配置: <!--将静态文件指定到某个特殊的文件夹中统一处理--> <mvc:resources mapping="/**"  location="/"/> 解决方案2: 在web.xml中添加: <servlet-mapping> <servlet-name>de