2011-10-31 83 views
1

我使用spring mvc创建了一个非常简单的测验应用程序。它工作正常。但是现在,如果用户遇到第三个问题,而另一个浏览器(另一个用户)发出另一个请求,则会向第三个用户提出第四个问题。我不希望发生这种情况。每个新的请求都应该从第一个问题开始测验。如何在不为每个用户提供登录表单的情况下实现这一目标,并将来自不同浏览器的每个新请求标识为不同的用户?我知道这可以通过会话来实现。春季MVC中的Http会话

有人可以解释如何做到这一点?

package dmv2.spring.controller; 

import java.util.List; 
import org.springframework.context.annotation.Scope; 
import org.springframework.stereotype.Controller; 
import org.springframework.validation.BindingResult; 
import org.springframework.web.bind.annotation.ModelAttribute; 
import org.springframework.web.bind.annotation.RequestMapping; 
import org.springframework.web.bind.annotation.RequestMethod; 
import org.springframework.web.bind.annotation.SessionAttributes; 
import org.springframework.web.servlet.ModelAndView; 

import dmv2.form.QuestionForm; 
import dmv2.model.Exam; 
import dmv2.model.Question; 

@Controller 
@SessionAttributes 
@RequestMapping("/Exam") 
public class ExamController 
{ 
private List<Question> questions = (new Exam()).getQuestions(); 
private int   index  = 0; 
private int   score  = 0; 

@RequestMapping(method = RequestMethod.GET) 
public ModelAndView showQuestionForm() 
{ 
    Question q = questions.get(index); 
    return new ModelAndView("exam", "questionForm", new QuestionForm()).addObject("q", q); 
} 

@RequestMapping(method = RequestMethod.POST) 
public ModelAndView showQuestionForm2(@ModelAttribute("questionForm") QuestionForm questionForm, BindingResult result) 
{ 
    Question q = questions.get(index); 
    if(q.getAnswer().getRightChoiceIndex() == Integer.parseInt(questionForm.getChoice())) 
     score = score + 1; 
    index = index + 1; 
    if(index < questions.size()) 
    { 
     q = questions.get(index); 
    } 
    else 
     return new ModelAndView("result").addObject("score", score); 
    return new ModelAndView("exam", "questionForm", new QuestionForm()).addObject("q", q); 
} 

} 

回答

7

永远不要将状态置于Controller中,就像在Servlet中一样,它对每个人都是全局的,并且对它的访问必须进行同步。一般来说,一个好的规则就是不要将任何可变的东西作为控制器的字段。你甚至不应该把业务逻辑放在Controller中,你应该从服务层调用对象来完成所有使用强大服务的工作。

为了解决您的问题,您可以定义一个名为QuestSession的接口,它将充当用户对话状态的代理。如果你实施边界检查会更好。 (例如,我猜索引和分数不能为负数)。

public interface QuestSession { 
    public int getIndex(); 
    public void setIndex(int index); 
    public int getScore();  
    public void setScore(int score); 
} 

接下来你只需通过这个接口在你需要它,比如:

public ModelAndView showQuestionForm2(
    @ModelAttribute("questionForm") QuestionForm questionForm, 
    BindingResult result, QuestSession session) { 

要使其工作,你已经创建QuestSessionImpl并添加XML配置。

<bean id="questSessionImpl" class="package.QuestSessionImpl" scope="session"> 
    <aop:scoped-proxy /> 
</bean> 

AOP:作用域代理,是面向方面的编程位,做进行代理类的魔法使每个环节会跟不同的对象。您甚至可以在控制器字段上使用@Autowired,每个会话仍将接收不同的对象。

我不知道如何用注释来表达它,如果有人正在阅读这个知道它,请咨询。

一句警告。即使会话访问也不是线程安全的。当然,它比全局共享访问单个字段的危险性更低,但用户可能会打开两个浏览器选项卡或创建竞争条件的窗口。您开始感受AJAX的所有痛苦,因为许多异步请求可以聚集在一起。即使您不使用AJAX,也可能需要添加适当的同步。

+0

很好的答案!感谢您的回答。我自动编写了@Scope(“session”),它为每个不同的会话创建了不同的状态,并且一切正常。你是否还建议我创建一个类或接口来存储状态,就像你所做的那样,而不是将整个控制器连接到一个会话范围内?是的,我了解将业务逻辑远离控制器的重要性。这只是一个旨在了解spring mvc的简单应用程序。 – SerotoninChase