2015-05-10 11 views
6

Ho il seguente codice di controllo utilizzando SpringMVC:In strato SpringMVC Controller, @Scope ("prototipo") vs @Scope ("Singleton")

@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"; 
    } 
} 

Quando l'uso @Scope("prototype"), ogni richiesta viene, l'output di this.hashCode() sono diversi, il che significa che quando ogni richiesta arriva, verrà creata una nuova istanza MessageController.

Se non utilizzare @Scope("prototype"), predefinito sarà @Scope("singleton"), ogni richiesta arriva, l'uscita del this.hashCode() è stesso, cioè viene creato un solo MessageController istanza.

Non sono sicuro quando è necessario utilizzare @Scope("prototype"), quando no?

risposta

9

Diciamo che codice come un maiale e fare qualcosa di simile nel controller:

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

Il controller sarebbe diventato stateful: ha uno stato (allMessages) che non possono essere condivisi tra due richieste. Il controller non è più thread-safe. Se è stato chiamato contemporaneamente a gestire due richieste simultanee, potrebbe esserci una condizione di competizione.

È possibile evitare questo problema rendendo il controller un prototipo: ogni richiesta verrà gestita da un controller separato.

Oppure si potrebbe fare la cosa giusta e rendere il codice stateless, in tal caso la creazione di un nuovo controller per ogni richiesta sarebbe inutile, dal momento che il controller sarebbe stateless e quindi thread-safe. L'ambito potrebbe quindi mantenere il suo valore predefinito: 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

Sono un po 'bloccato con lo stesso problema. Puoi per favore guardare nella mia domanda? http://stackoverflow.com/questions/43868299/how-to-reload-configuration-bean-with-properties-from-database – Lucky

1

Se si utilizza Singleton, è necessario assicurarsi di non mantenere lo stato nel controller o che qualsiasi stato di attesa sia progettato per essere condiviso tra le chiamate. Solitamente i componenti del servizio aziendale sono costruiti in questo modo e sono sicuri da inserire in un controller singleton.

Esistono anche altri ambiti che è possibile considerare in base alle configurazioni e alle librerie della molla.

È possibile rendere la richiesta bean e la sessione con ambito.

Tenderei a fare una richiesta di classe di classe del controllore rispetto all'ambito del prototipo poiché ciò garantirebbe che più usi del controller da una singola richiesta ottengano lo stesso oggetto.

È possibile utilizzare l'ambito di sessione se si desidera mantenere lo stato mantenuto su più richieste in una sessione. Ma la primavera ha altri modi per raggiungere la stessa cosa con @SessionAttributes

Infine è possibile utilizzare l'iniezione di un java.inject.Provider in un bean singleton utilizzando ProviderCreatingFactoryBean.