Chcę użyć w kontrolerze prototypowego komponentu bean z adnotacjami. Ale zamiast tego wiosna tworzy singleton. Oto kod:
@Component
@Scope("prototype")
public class LoginAction {
private int counter;
public LoginAction(){
System.out.println(" counter is:" + counter);
}
public String getStr() {
return " counter is:"+(++counter);
}
}
Kod kontrolera:
@Controller
public class HomeController {
@Autowired
private LoginAction loginAction;
@RequestMapping(value="/view", method=RequestMethod.GET)
public ModelAndView display(HttpServletRequest req){
ModelAndView mav = new ModelAndView("home");
mav.addObject("loginAction", loginAction);
return mav;
}
public void setLoginAction(LoginAction loginAction) {
this.loginAction = loginAction;
}
public LoginAction getLoginAction() {
return loginAction;
}
}
Szablon prędkości:
LoginAction counter: ${loginAction.str}
Spring config.xml
ma włączone skanowanie komponentów:
<context:annotation-config />
<context:component-scan base-package="com.springheat" />
<mvc:annotation-driven />
Za każdym razem otrzymuję zwiększoną liczbę. Nie mogę dowiedzieć się, dokąd zmierzam źle!
Aktualizacja
Zgodnie z sugestią @gkamal zrobiłem HomeController
webApplicationContext
-świadomość i rozwiązałem problem.
zaktualizowany kod:
@Controller
public class HomeController {
@Autowired
private WebApplicationContext context;
@RequestMapping(value="/view", method=RequestMethod.GET)
public ModelAndView display(HttpServletRequest req){
ModelAndView mav = new ModelAndView("home");
mav.addObject("loginAction", getLoginAction());
return mav;
}
public LoginAction getLoginAction() {
return (LoginAction) context.getBean("loginAction");
}
}
spring
spring-mvc
tintin
źródło
źródło
Odpowiedzi:
Prototyp zakresu oznacza, że za każdym razem, gdy zapytasz spring (getBean lub dependency injection) o instancję, utworzy on nową instancję i poda do niej odniesienie.
W twoim przykładzie nowe wystąpienie LoginAction jest tworzone i wstrzykiwane do HomeController. Jeśli masz inny kontroler, do którego wstrzykniesz LoginAction, otrzymasz inną instancję.
Jeśli chcesz mieć inną instancję dla każdego wywołania - wtedy musisz wywoływać getBean za każdym razem - wstrzyknięcie do pojedynczego ziarna nie zapewni tego.
źródło
request
zasięg zamiastprototype
zakresu. Czy nadal będziesz musiał pobrać fasolęcontext.getBean(..)
?Od wiosny 2.5 można to osiągnąć w bardzo łatwy (i elegancki) sposób.
Można po prostu zmienić params
proxyMode
orazvalue
z@Scope
adnotacją.Dzięki tej sztuczce możesz uniknąć pisania dodatkowego kodu lub wstrzykiwania ApplicationContext za każdym razem, gdy potrzebujesz prototypu wewnątrz pojedynczego komponentu bean.
Przykład:
@Service @Scope(value="prototype", proxyMode=ScopedProxyMode.TARGET_CLASS) public class LoginAction {}
Z powyższą konfiguracją
LoginAction
(wewnątrzHomeController
) jest zawsze prototypem, mimo że kontroler jest singletonem .źródło
Tylko dlatego, że fasola wprowadzona do kontrolera ma zakres prototypowy, nie oznacza, że kontroler jest!
źródło
@controller jest obiektem singleton i jeśli wstrzykniesz fasolę prototypu do klasy singleton, ten komponent bean będzie również singletonem, chyba że określisz właściwość lookup-method, która w rzeczywistości tworzy nową instancję prototypowego komponentu bean dla każdego wywołania.
źródło
Jak wspomniano przez nicholas.hauschild wstrzykiwanie kontekstu wiosny nie jest dobrym pomysłem. W Twoim przypadku @Scope („request”) wystarczy, aby to naprawić. Ale powiedzmy, że potrzebujesz kilku instancji
LoginAction
metody kontrolera. W tym przypadku poleciłbym stworzyć fasolę Dostawcy ( rozwiązanie Spring 4 ):@Bean public Supplier<LoginAction> loginActionSupplier(LoginAction loginAction){ return () -> loginAction; }
Następnie wstrzyknij go do kontrolera:
@Controller public class HomeController { @Autowired private Supplier<LoginAction> loginActionSupplier;
źródło
ObjectFactory
które służą temu samemu celowi co dostawca, ale można je zdefiniować jako normalne,@Bean
przez co mam na myśli brak konieczności zwracania lambda.Używanie
ApplicationContextAware
wiąże cię ze Springiem (co może, ale nie musi, stanowić problem). Poleciłbym przejść przez aLoginActionFactory
, o który możesz poprosić o nową instancję zaLoginAction
każdym razem, gdy jej potrzebujesz.źródło
factory-method
tutaj ...LoginActionFactory
do kontrolera, alefactory-method
nie wydaje się, aby to rozwiązało problem, ponieważ po prostu tworzy kolejną wiosenną fasolkę za pośrednictwem fabryki. Wstrzyknięcie tego ziarna do pojedynczego kontrolera nie rozwiąże problemu.użyj zakresu żądania,
@Scope("request")
aby uzyskać komponent bean dla każdego żądania lub@Scope("session")
uzyskać bean dla każdej sesji „user”źródło
Fasola-protoype wstrzyknięta do fasoli singelton będzie zachowywać się jak singelton, dopóki nie zostanie natychmiast wezwana do utworzenia nowej instancji przez get bean.
context.getBean("Your Bean")
źródło
@Składnik
@Scope (wartość = "prototyp")
Klasa publiczna TennisCoach wdraża Coach {
// jakiś kod
}
źródło
Możesz stworzyć statyczną klasę wewnątrz swojego kontrolera w następujący sposób:
@Controller public class HomeController { @Autowired private LoginServiceConfiguration loginServiceConfiguration; @RequestMapping(value = "/view", method = RequestMethod.GET) public ModelAndView display(HttpServletRequest req) { ModelAndView mav = new ModelAndView("home"); mav.addObject("loginAction", loginServiceConfiguration.loginAction()); return mav; } @Configuration public static class LoginServiceConfiguration { @Bean(name = "loginActionBean") @Scope("prototype") public LoginAction loginAction() { return new LoginAction(); } } }
źródło
Innym sposobem rozwiązania problemu jest wstrzyknięcie metody z adnotacją @Lookup .
Oto fajny artykuł na temat wstrzykiwania prototypowych ziaren do pojedynczej instancji z wieloma rozwiązaniami.
https://www.baeldung.com/spring-inject-prototype-bean-into-singleton
źródło
Twój kontroler również potrzebuje
@Scope("prototype")
zdefiniowanego plikulubię to:
@Controller @Scope("prototype") public class HomeController { ..... ..... ..... }
źródło