2011-10-31 116 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