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

50 篇文章 0 订阅
22 篇文章 1 订阅

先看这么一段代码:

@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");
		}

		// ... ...
	}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值