2015-05-10 44 views
6

我已用SpringMVC使用下面控制器代碼@Scope( 「單」):在控制器用SpringMVC層,@Scope( 「原型」)與

@Controller 
@Scope("prototype") 
@RequestMapping("/messages") 
public class MessageController { 
    @RequestMapping(value="/index", method=RequestMethod.GET) 
    @ResponseStatus(HttpStatus.OK) 
    @ResponseBody 
    public String displayAllMessages(ModelMap model) { 
     System.out.println(this.hashCode()); 
     // processing 
     return "messages"; 
    } 
} 

當使用@Scope("prototype"),每個請求到來時,輸出this.hashCode()是不同的,這意味着當每個請求到來時,將創建一個新的MessageController實例。

如果不使用@Scope("prototype"),默認情況下會@Scope("singleton"),每個請求到來時的this.hashCode()輸出相同,只創建一個實例MessageController意義。

我不確定什麼時候應該使用@Scope("prototype")

回答

9

比方說,你代碼像豬和做這樣的事情在你的控制器:

private List<String> allMessages; 

public String displayAllMessages(ModelMap model) { 
    allMessages = new ArrayList<>(); 
    fillMessages(); 
    model.put(messages, allMessages); 
    return "messages"; 
} 

private void fillMessages() { 
    allMessages.add("hello world"); 
} 

你的控制器將成爲狀態:它有一個狀態(allMessages)不能兩者之間共享要求。控制器不再是線程安全的。如果同時調用它來處理兩個併發請求,則可能存在競爭條件。

您可以通過使控制器成爲原型來避免此問題:每個請求都將由單獨的控制器處理。

或者你可以做正確的事情,並使代碼無狀態,在這種情況下爲每個請求創建一個新的控制器將是無用的,因爲控制器將是無狀態的,因此是線程安全的。範圍可以保持其默認值:singleton。

public String displayAllMessages(ModelMap model) { 
    List<String> messages = fillMessages(); 
    model.put(messages, allMessages); 
    return "messages"; 
} 

private List<String> fillMessages() { 
    List<String> allMessages = new ArrayList<>(); 
    allMessages.add("hello world"); 
    return allMessages; 
} 
+0

我有點陷入了同樣的問題。你能看看我的問題嗎? http://stackoverflow.com/questions/43868299/how-to-reload-configuration-bean-with-properties-from-database – Lucky

1

如果你使用單例,你必須確保你沒有在你的控制器中保持狀態,或者你所持有的任何狀態都被設計爲在調用之間共享。通常,業務服務組件是以這種方式構建的,並且可以安全地注入到單例控制器中。

還有其他範圍可以考慮取決於你的彈簧配置和庫。

您可以使bean請求和會話範圍。

我傾向於使控制器類的請求作用域而不是原型作用域,因爲這可以確保從單個請求中控制器的多次使用獲得相同的對象。

如果您想要保持跨會話中多個請求的狀態,可以使用會話範圍。但是Spring有其他方法可以通過@SessionAttributes實現同樣的事情

最後,您可以使用ProviderCreatingFactoryBean將java.inject.Provider注入單例bean。