2015-05-10 10 views
6

Mam następujący kod kontrolera używając SpringMVC:W warstwie SpringMVC Controller, @Scope ("prototyp") 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"; 
    } 
} 

Kiedy użycie @Scope("prototype") każde żądanie pochodzi, wyjście this.hashCode() są różne, co oznacza, że ​​po każdym żądaniu zostanie utworzona nowa instancja MessageController.

Jeśli nie używać @Scope("prototype"), domyślnie będzie @Scope("singleton") każde żądanie pochodzi, wyjście this.hashCode() są takie same, co oznacza tylko jedno MessageController instancja jest tworzona.

Nie jestem pewien, kiedy należy użyć @Scope("prototype"), kiedy nie?

Odpowiedz

9

Powiedzmy, że kod jak świnia i zrobić coś takiego w swoim kontrolerze:

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

Twój kontroler będzie stać Stateful: ma stan (allMessages), które nie mogą być dzielone pomiędzy dwa upraszanie. Sterownik nie jest już bezpieczny dla wątków. Jeśli był on wywoływany jednocześnie w celu obsługi dwóch jednoczesnych żądań, może występować sytuacja wyścigu.

Można uniknąć tego problemu, zmieniając kontroler w prototyp: każde żądanie będzie obsługiwane przez osobny kontroler.

Albo możesz postąpić słusznie i uczynić kod bezpaństwowym, w którym to przypadku utworzenie nowego kontrolera dla każdego żądania byłoby bezużyteczne, ponieważ kontroler byłby bezpaństwowcem, a zatem wątkiem bezpiecznym. Zakres może następnie zachować jego wartość domyślną: 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

Trochę utknąłem z tym samym problemem. Czy możesz zajrzeć do mojego pytania? http://stackoverflow.com/questions/43868299/how-to-reload-configuration-bean-with-properties- from-database – Lucky

1

Jeśli używasz pojedynczego singla, musisz upewnić się, że nie masz stanu w kontrolerze lub że jakikolwiek stan, który trzymasz, ma być współdzielony między wywołaniami. Zwykle komponenty usług biznesowych są budowane w ten sposób i są bezpieczne do wstrzyknięcia do kontrolera pojedynczego.

Istnieją również inne zakresy, które można rozważyć w zależności od konfiguracji sprężyn i bibliotek.

Możesz utworzyć żądanie fasoli i zakres sesji.

Byłbym skłonny do stworzenia zakresu klasy kontrolera o zasięgu raczej niż prototypowego, ponieważ zapewniłoby to, że wiele zastosowań kontrolera z pojedynczego żądania otrzymywa ten sam obiekt.

Możesz użyć zakresu sesji, jeśli chcesz zachować stan przechowywany w wielu żądaniach w sesji. Ale wiosna ma inne sposoby osiągnięcia tego samego z @SessionAttributes

Wreszcie można użyć wtrysku java.inject.Provider do pojedynczej fasoli za pomocą ProviderCreatingFactoryBean.