2011-10-01 644 views
84

Quiero usar un bean prototipo anotado en mi controlador. Pero la primavera está creando un frijol singleton en su lugar. Aquí está el código para que:@Scope ("prototype") bean scope no crea nuevo bean

@Component 
@Scope("prototype") 
public class LoginAction { 

    private int counter; 

    public LoginAction(){ 
    System.out.println(" counter is:" + counter); 
    } 
    public String getStr() { 
    return " counter is:"+(++counter); 
    } 
} 

código de controlador:

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

plantilla Velocity:

LoginAction counter: ${loginAction.str} 

primavera config.xml tiene la exploración componente habilitado:

<context:annotation-config /> 
    <context:component-scan base-package="com.springheat" /> 
    <mvc:annotation-driven /> 

Recibo un recuento incrementado cada vez. ¡No puedo entender dónde me estoy equivocando!

actualización

Como suggested by @gkamal, hice HomeControllerwebApplicationContext sea conscientes y se resolvió el problema.

código actualizado:

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

Me gustaría poder duplicar upvote que para la implementación de la respuesta correcta en su código para que otros vean la diferencia real –

Respuesta

104

Ámbito prototipo significa que cada vez que se pide la primavera (getBean o inyección de dependencias) para una instancia que va a crear una nueva instancia y dar una referencia a eso.

En su ejemplo, se crea y se inyecta una nueva instancia de LoginAction en su HomeController. Si tienes otro controlador en el que inyectas LoginAction obtendrás una instancia diferente.

Si desea una instancia diferente para cada llamada - entonces necesita llamar a getBean cada vez - inyectar en un bean Singleton no lo logrará.

+5

Hice el controlador ApplicationContextAware y getBean y obtengo el bean nuevo todo el tiempo. ¡¡¡Gracias chicos!!! – tintin

+0

¿Cómo funciona esto si el bean hubiera tenido el alcance 'request' en lugar del alcance' prototype'. ¿Todavía necesitarías recuperar el bean con 'context.getBean (..)'? –

+0

O use un proxy con ámbito, es decir @Scope (value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS) – svenmeier

12

¡El hecho de que el bean inyectado en el controlador tenga un alcance de prototipo no significa que el controlador sí lo esté!

2

El uso de ApplicationContextAware lo está atando a Spring (que puede o no ser un problema). Yo recomendaría pasar un LoginActionFactory, que puede solicitar una nueva instancia de LoginAction cada vez que lo necesite.

+1

Sin embargo, ya hay anotaciones específicas de Spring; no parece ser una gran preocupación. –

+1

@Dave, Buen punto. Hay alternativas para algunas de las cosas de DI (JSR 311), pero puede ser más difícil deshacerse de todo lo que depende de Spring en este ejemplo. Supongo que realmente defiendo el 'método de fábrica' aquí ... –

+1

+1 para inyectar un singleton 'LoginActionFactory' en el Controlador, pero' factory-method' no parece que resolvería el problema ya que solo crea otro grano de primavera a través de la fábrica. Inyectar ese frijol en el controlador único no resolverá el problema. –

-6

su controlador también necesitan el @Scope ("prototipo") defind

así:

@Controller 
@Scope("prototype") 
public class HomeController { 
..... 
..... 
..... 

} 
+0

¿por qué crees que el controlador también necesita ser un prototipo? –

+5

Esto es simplemente incorrecto. –

7

@controller es un objeto único, y si inyectar un grano de prototipo a una clase Singleton hará el prototipo bean también como singleton a menos que se especifique el uso de la propiedad lookup-method, que en realidad crea una nueva instancia de prototype bean para cada llamada que realice.

3

utilización solicitud alcance @Scope("request") para obtener frijol para cada solicitud, o @Scope("session") para obtener frijol para cada 'usuario' sesión de

2

Como se ha mencionado por nicholas.hauschild inyectar contexto primavera no es una buena idea. En su caso, @Scope ("solicitud") es suficiente para solucionarlo. Pero supongamos que necesita varias instancias de LoginAction en el método de controlador.En este caso, yo recomendaría para crear el grano de Proveedor (Spring 4 solución):

@Bean 
    public Supplier<LoginAction> loginActionSupplier(LoginAction loginAction){ 
     return() -> loginAction; 
    } 

Entonces inyectarlo en el controlador:

@Controller 
public class HomeController { 
    @Autowired 
    private Supplier<LoginAction> loginActionSupplier;