@Scope(“ prototype”)bean范围未创建新bean


133

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

控制器代码:

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

速度模板:

 LoginAction counter: ${loginAction.str}

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

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

我每次都得到递增计数。无法弄清楚我要去哪里错了!

更新资料

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

12
我希望我能加倍支持您在您的代码中实施正确的答案,以使其他人看到实际的差异
Ali Nem 2016年

Answers:


156

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

在您的示例中,将创建一个LoginAction的新实例并将其注入到HomeController中。如果您要向其中注入LoginAction的另一个控制器,则将获得一个不同的实例。

如果您希望每次调用都使用不同的实例-那么您每次都需要调用getBean-向单例bean中注入将无法实现这一点。


7
我做了控制器ApplicationContextAware并做了getBean,每次都获取新鲜的bean。多谢你们!!!
tintin 2011年

如果bean将具有request作用域而不是prototype作用域,这将如何工作。您是否还需要使用来检索bean context.getBean(..)
jerry博士

2
或使用有范围的代理,即@Scope(value =“ prototype”,proxyMode = ScopedProxyMode.TARGET_CLASS)
svenmeier

25

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

你可以只改变PARAMS proxyModevalue@Scope注释。

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

例:

@Service 
@Scope(value="prototype", proxyMode=ScopedProxyMode.TARGET_CLASS)  
public class LoginAction {}

即使控制器是单身人士,上面LoginAction(内部HomeController)的配置也始终是原型


2
所以我们现在不在春季5吗?
Raghuveer

16

仅仅因为注入控制器的bean是原型作用域的,并不意味着控制器就是!


11

@controller是一个单例对象,如果将原型bean注入到一个singleton类中,则将使原型bean也成为singleton,除非您使用lookup-method属性指定它实际上为您进行的每个调用创建一个原型bean的新实例。


5

正如nicholas.hauschild提到的,注入Spring上下文不是一个好主意。就您而言,@Scope(“ request”)足以修复它。但是可以说,您需要LoginActionin控制器方法的多个实例。在这种情况下,我建议创建Supplier的bean(Spring 4解决方案):

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

然后将其注入控制器:

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

1
我建议注入ObjectFactory与供应商具有相同目的的弹簧,但可以将其定义为正常状态@Bean,也就是说不需要返回lambda。
xenoterracide

3

使用ApplicationContextAware将您与Spring捆绑在一起(这可能会或可能不会成为问题)。我建议您传入一个LoginActionFactoryLoginAction每次需要时您都可以要求一个新实例。


1
不过,已经有了特定于Spring的注释;似乎并没有太大的问题。
戴夫·牛顿

1
@戴夫,好点。一些DI东西(JSR 311)可以替代,但是在此示例中,摆脱与Spring相关的所有事情可能会更加困难。我想我真的只是在factory-method这里提倡...
nicholas.hauschild

1
+1用于将单例LoginActionFactory注入Controller中,但factory-method似乎并不能解决问题,因为它只是通过工厂创建了另一个spring bean。将该bean注入到singleton Controller中将无法解决该问题。
布拉德·库比

很好,布拉德,我将在回答中删除该建议。
nicholas.hauschild




0

您可以像这样在控制器内部创建静态类:

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


-11

您的控制器还需要 @Scope("prototype")定义

像这样:

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

}

1
您为什么认为控制器也需要原型?
吉加尔·帕雷克
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.