Je souhaite utiliser un prototype de haricot annoté dans mon contrôleur. Mais le printemps crée plutôt un haricot singleton. Voici le code pour cela:
@Component
@Scope("prototype")
public class LoginAction {
private int counter;
public LoginAction(){
System.out.println(" counter is:" + counter);
}
public String getStr() {
return " counter is:"+(++counter);
}
}
Code du contrôleur:
@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;
}
}
Modèle de vélocité:
LoginAction counter: ${loginAction.str}
Printemps config.xml
a l'analyse de composant activée:
<context:annotation-config />
<context:component-scan base-package="com.springheat" />
<mvc:annotation-driven />
Je reçois un compte incrémenté à chaque fois. Je ne peux pas comprendre où je vais mal!
Mettre à jour
Comme suggéré par @gkamal , j'ai mis HomeController
webApplicationContext
- au courant et le problème a été résolu.
code mis à jour:
@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");
}
}
Par prototype, Scope signifie que chaque fois que vous demandez à spring (getBean ou une dépendance) de créer une instance, il crée une nouvelle instance et donne une référence à celle-ci.
Dans votre exemple, une nouvelle instance de LoginAction est créée et injectée dans votre HomeController. Si vous avez un autre contrôleur dans lequel vous injectez LoginAction, vous obtiendrez une instance différente.
Si vous voulez une instance différente pour chaque appel (vous devez alors appeler getBean à chaque fois), l'injection dans un bean singleton n'atteindra pas cet objectif.
Le fait que le haricot injecté dans le contrôleur ait une portée de prototype ne signifie pas que le contrôleur l’est!
Depuis le printemps 2.5 , il existe un moyen très simple (et élégant) d’y parvenir.
Vous pouvez simplement changer les paramètres proxyMode
et value
de l'annotation @Scope
.
Avec cette astuce, vous pouvez éviter d'écrire du code supplémentaire ou d'injecter ApplicationContext chaque fois que vous avez besoin d'un prototype dans un bean singleton.
Exemple:
@Service
@Scope(value="prototype", proxyMode=ScopedProxyMode.TARGET_CLASS)
public class LoginAction {}
Avec la configuration ci-dessus LoginAction
(dans HomeController
) est toujours un prototype même si le contrôleur est un singleton.
@controller est un objet singleton, et si vous injectez un bean prototype à une classe singleton, le bean prototype sera aussi singleton, sauf si vous spécifiez à l'aide de la propriété lookup-method qui crée une nouvelle instance de bean prototype pour chaque appel effectué.
Comme mentionné par nicholas.hauschild injecting Spring, le contexte n'est pas une bonne idée. Dans votre cas, @Scope ("request") suffit à résoudre le problème. Mais supposons que vous ayez besoin de plusieurs instances de LoginAction
dans la méthode du contrôleur. Dans ce cas, je recommanderais de créer le haricot de fournisseur ( Spring 4 solution):
@Bean
public Supplier<LoginAction> loginActionSupplier(LoginAction loginAction){
return () -> loginAction;
}
Puis l'injecter dans le contrôleur:
@Controller
public class HomeController {
@Autowired
private Supplier<LoginAction> loginActionSupplier;
utilisez request @Scope("request")
pour obtenir un bean pour chaque demande ou @Scope("session")
pour obtenir un bean pour chaque utilisateur
Utiliser ApplicationContextAware
vous lie à Spring (ce qui peut être un problème ou non). Je recommanderais de passer une LoginActionFactory
, à laquelle vous pouvez demander une nouvelle instance de LoginAction
à chaque fois que vous en avez besoin.
Un haricot prototype injecté à l'intérieur d'un haricot singelton se comportera comme un singleton jusqu'à ce qu'il soit explicitement appelé à créer une nouvelle instance par get bean.
context.getBean("Your Bean")
@ Composant
@ Scope (value = "prototype")
cours public TennisCoach implémente Coach {
// du code
}
Vous pouvez créer une classe statique dans votre contrôleur de la manière suivante:
@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();
}
}
}