zoukankan      html  css  js  c++  java
  • 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环境中,webserver会为每个请求创建一个线程来处理它。这样一来。在@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");
    		}
    
    		// ... ...
    	}

    这样便能够完美避免线程安全问题,又不会带来过多的额外开销。
  • 相关阅读:
    第九周学习报告
    人月神话阅读笔记02
    数组 分步 详细
    第八周学习总结
    课堂练习
    学习进度报告09
    用户模板和用户场景
    学习进度报告08
    课堂随笔
    学习进度报告07
  • 原文地址:https://www.cnblogs.com/gcczhongduan/p/5243104.html
Copyright © 2011-2022 走看看