简体   繁体   English

Spring MVC中的Http Session

[英]Http Session in Spring MVC

I'm creating a very simple quiz application using spring mvc. 我正在使用Spring MVC创建一个非常简单的测验应用程序。 It is working fine. 一切正常。 But right now if a user is on the third question and another request comes in from another browser (another user) it will render the fourth question to the new user. 但是现在,如果用户在第三个问题上,并且另一个浏览器(另一个用户)发出另一个请求,它将把第四个问题呈现给新用户。 I don't want this to happen. 我不希望这种情况发生。 Every new request should start the quiz from the first question. 每个新请求都应从第一个问题开始测验。 How do I achieve this without having a login form for each user and yet identify each new request from a different browser as a different user? 如何在没有每个用户登录表单的情况下实现此目标,而又将来自不同浏览器的每个新请求标识为另一个用户? I know this can be achieved using sessions. 我知道可以使用会话来实现。

Can someone explain how to do this? 有人可以解释如何做吗?

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

}

Never put state in a Controller, like in a Servlet, it will be global to everyone, and access to it has to be synchronised. 永远不要像在Servlet中一样将状态放在控制器中,它对于每个人都是全局的,并且必须同步对其的访问。 In general is a good rule to just not put anything mutable as Controller's field. 通常,最好不要将任何可变的内容作为Controller的字段放置。 You shouldn't even put business logic in a Controller, you should call Objects from the service layer doing all the work with powerful services. 您甚至不应将业务逻辑放入控制器中,而应从服务层调用对象,以使用强大的服务来完成所有工作。

To come to your problem, you can define an interface called QuestSession that will act as proxy to the user conversational state. 要解决您的问题,您可以定义一个称为QuestSession的接口,该接口将充当用户对话状态的代理。 It's better if you implement boundary checks. 最好执行边界检查。 (I guess index and score can't be negative, for example). (例如,我想索引和分数不能为负)。

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

Next you simply pass this interface where you need it, such as: 接下来,您只需在需要的地方传递此接口,例如:

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

To make it work, you've to create a QuestSessionImpl and add in your XML configuration. 为了使其工作,您必须创建一个QuestSessionImpl并添加您的XML配置。

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

The aop:scoped-proxy , is the aspect oriented programming bit that does the magic of proxying your class so each session will talk to a different object. aop:scoped-proxy是面向方面的编程位,它具有代理类的魔力,因此每个会话都将与不同的对象通信。 You can even use @Autowired on a Controller field and each session will still receive a different Object. 您甚至可以在Controller字段上使用@Autowired ,每个会话仍将收到一个不同的Object。

I don't know how to express it with annotations, if anyone is reading this knows it, please advice. 我不知道如何用注解来表达它,如果有人正在阅读此书,请告知。

A word of warning. 一句话警告。 Even session access is not thread safe. 甚至会话访问也不是线程安全的。 Of course it's less dangerous than a global shared access to a single field, but it's still possible that a user opens two browser tabs or windows creating race conditions. 当然,它比全局共享访问单个字段的危险要小,但是用户仍然有可能打开两个浏览器选项卡或创建竞争条件的窗口。 You start to feel all the pain of it with AJAX, since many asynchronous request can come together. 使用AJAX,您会开始感到痛苦,因为可以将许多异步请求放在一起。 Even if you're not using AJAX, you may want to add proper synchronisation. 即使您没有使用AJAX,也可能需要添加适当的同步。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM