【问题标题】:@Scope("prototype") bean scope not creating new bean@Scope("prototype") bean 范围不创建新 bean
【发布时间】:2011-11-29 03:10:00
【问题描述】:

我想在我的控制器中使用带注释的原型 bean。但是 spring 正在创建一个单例 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 />

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

更新

作为suggested by @gkamal,我做了HomeController webApplicationContext-aware,它解决了问题。

更新代码:

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

【问题讨论】:

  • 我希望我能加倍支持您在您的代码中实现正确答案,以便其他人看到实际差异

标签: spring spring-mvc


【解决方案1】:

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

像这样:

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

}

【讨论】:

  • 为什么你认为控制器也需要原型?
【解决方案2】:

默认情况下,Spring bean 是单例的。当我们出现问题时 尝试连接不同范围的bean。例如,一个原型 bean 变成单例。这被称为作用域 bean 注入问题。

解决问题的另一种方法是使用@Lookup 注解进行方法注入。

这里有一篇很好的文章,介绍了将原型 bean 注入到单例实例中的问题,并提供了多种解决方案。

https://www.baeldung.com/spring-inject-prototype-bean-into-singleton

【讨论】:

    【解决方案3】:

    @Component

    @Scope(value="prototype")

    public class TennisCoach 实现 Coach {

    // 一些代码

    }

    【讨论】:

      【解决方案4】:

      在 singelton bean 中注入的原型 bean 将表现得像 singelton,直到通过 get bean 显式调用创建新实例。

      context.getBean("Your Bean")
      

      【讨论】:

        【解决方案5】:

        自 Spring 2.5 以来,有一种非常简单(且优雅)的方法可以实现这一目标。

        您只需更改@Scope 注释的参数proxyModevalue

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

        例子:

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

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

        【讨论】:

        • 所以我们现在在春季 5 没有它?
        【解决方案6】:

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

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

        然后将其注入控制器:

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

        【讨论】:

        • 我建议注入弹簧ObjectFactory,其作用与供应商相同,但可以定义为普通@Bean,我的意思是不需要返回lambda。
        【解决方案7】:

        使用请求范围 @Scope("request") 获取每个请求的 bean,或使用 @Scope("session") 获取每个会话“用户”的 bean

        【讨论】:

          【解决方案8】:

          使用ApplicationContextAware 会将您绑定到 Spring(这可能是也可能不是问题)。我建议传入LoginActionFactory,每次需要时都可以请求LoginAction 的新实例。

          【讨论】:

          • 虽然已经有了 Spring 特定的注解;看起来这不是什么大问题。
          • @Dave,好点子。有一些 DI 东西(JSR 311)的替代品,但是在这个例子中摆脱所有依赖 Spring 的东西可能更难。我想我真的只是在这里提倡factory-method...
          • +1 用于将单例LoginActionFactory 注入控制器,但factory-method 似乎无法解决问题,因为它只是通过工厂创建了另一个spring bean。将该 bean 注入单例控制器不会解决问题。
          • 好点布拉德,我将从我的回答中删除该建议。
          【解决方案9】:

          @controller 是一个单例对象,如果将原型 bean 注入到单例类中,则原型 bean 也将成为单例,除非您指定使用查找方法属性,该属性实际上为您进行的每个调用创建一个原型 bean 的新实例.

          【讨论】:

            【解决方案10】:

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

            【讨论】:

              【解决方案11】:

              Scope 原型意味着每次你向 spring(getBean 或依赖注入)请求一个实例时,它都会创建一个新实例并给出一个引用。

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

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

              【讨论】:

              • 我制作了控制器 ApplicationContextAware 并做了 getBean ,我每次都得到新鲜的 bean。谢谢大家!!!
              • 如果 bean 具有 request 范围而不是 prototype 范围,这将如何工作。您还需要使用context.getBean(..) 检索 bean 吗?
              • 或者使用作用域代理,即@Scope(value="prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
              猜你喜欢
              • 2014-09-29
              • 2019-12-01
              • 1970-01-01
              • 1970-01-01
              • 2017-09-06
              • 1970-01-01
              • 2018-06-25
              • 2012-02-24
              • 2013-07-10
              相关资源
              最近更新 更多