web-dev-qa-db-fra.com

La portée du bean @Scope ("prototype") ne crée pas de nouveau bean

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

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.

130
gkamal

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!

13
Dave Newton

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.

10
db80

@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é. 

9
kartheek

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;  
4
Igor Rybak

utilisez request @Scope("request") pour obtenir un bean pour chaque demande ou @Scope("session") pour obtenir un bean pour chaque utilisateur

3
Bassem Reda Zohdy

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.

3

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")
0
Ujjwal Choudhari

@ Composant

@ Scope (value = "prototype")

cours public TennisCoach implémente Coach {

// du code

}

0

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();
            }
        }
}
0
Jacob