@Scope("prototype")bean范围没有创建新bean

tin*_*tin 120 spring spring-mvc

我想在我的控制器中使用带注释的原型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);
  }
}
Run Code Online (Sandbox Code Playgroud)

控制器代码:

@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;
    }
    }
Run Code Online (Sandbox Code Playgroud)

速度模板:

 LoginAction counter: ${loginAction.str}
Run Code Online (Sandbox Code Playgroud)

Spring config.xml启用了组件扫描:

    <context:annotation-config />
    <context:component-scan base-package="com.springheat" />
    <mvc:annotation-driven />
Run Code Online (Sandbox Code Playgroud)

我每次都得到一个递增的计数.无法弄清楚我哪里出错了!

更新

正如@gkamal建议的,我做了 - 意识到HomeController webApplicationContext并解决了这个问题.

更新的代码:

@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");
    }
}
Run Code Online (Sandbox Code Playgroud)

gka*_*mal 141

范围原型意味着每次向实例请求spring(getBean或依赖注入)时,它都会创建一个新实例并提供对它的引用.

在您的示例中,将创建一个新的LoginAction实例并将其注入HomeController.如果你有另一个控制器注入LoginAction,你将得到一个不同的实例.

如果你想为每个调用一个不同的实例 - 那么你每次都需要调用getBean - 注入单例bean将无法实现.

  • 我制作了控制器ApplicationContextAware并且做了getBean,我每次都得到了新鲜的bean.多谢你们!!! (6认同)
  • 或者使用作用域代理,即@Scope(value="prototype", proxyMode = ScopedProxyMode.TARGET_CLASS) (2认同)

db8*_*b80 18

从Spring 2.5开始,就有了一种非常简单(优雅)的方法来实现这一目标.

你可以只改变PARAMS proxyModevalue中的@Scope注释.

使用此技巧,您可以避免每次在单例bean中需要原型时编写额外的代码或注入ApplicationContext.

例:

@Service 
@Scope(value="prototype", proxyMode=ScopedProxyMode.TARGET_CLASS)  
public class LoginAction {}
Run Code Online (Sandbox Code Playgroud)

使用上面的配置LoginAction(内部HomeController)始终是原型,即使控制器是单例.

  • 所以我们现在不在春季5吗? (2认同)

Dav*_*ton 13

仅仅因为注入控制器的bean是原型范围的并不意味着控制器就是!


小智 9

@controller是一个单例对象,如果将一个原型bean注入一个单独的类,它将使原型bean也成为单例,除非你指定使用lookup-method属性,它实际上为你做的每个调用创建一个新的prototype bean实例.


Igo*_*bak 5

正如nicholas.hauschild所提到的,注入 Spring 上下文不是一个好主意。在您的情况下,@Scope("request") 足以修复它。但是假设您需要几个LoginActionin 控制器方法的实例。在这种情况下,我建议创建供应商的 bean ( Spring 4解决方案):

    @Bean
    public Supplier<LoginAction> loginActionSupplier(LoginAction loginAction){
        return () -> loginAction;
    }
Run Code Online (Sandbox Code Playgroud)

然后将其注入控制器:

@Controller
public class HomeController {
    @Autowired
    private  Supplier<LoginAction> loginActionSupplier;  
Run Code Online (Sandbox Code Playgroud)

  • 我建议注入与供应商具有相同目的的弹簧`ObjectFactory`,但可以定义为普通的`@Bean`,我的意思是不需要返回lambda。 (2认同)