【问题标题】:How to ask for Prototype bean in Spring service class without applicationContext如何在没有 applicationContext 的 Spring 服务类中请求 Prototype bean
【发布时间】:2019-03-11 07:18:53
【问题描述】:

我有一个使用原型范围定义的组件。我想在我的服务类中使用该组件。我希望 spring 在我每次调用它时为我提供一个该 Bean 的新实例。

组件类:

@Getter
@Setter
@Component
@Scope("prototype")
public class ProtoTypeBean {
  //.. Field variables
}

服务类:

@AllArgsConstructor
@Service
public class ServiceClass {
    ProtoTypeBean prototypeBean;
    ArrayList<ProtoTypeBean> prototypeBeans;
    public void demoMethod(ArrayList<String> someArrayList) {

        for(var singleString: someArrayList) {
            prototypeBean.setFieldValue(singleString);

            prototypeBeans.add(prototypeBean);              
        }
        System.out.println(prototypeBeans.toString());
    }
}

通过使用此配置,我在我的prototypeBeans ArrayList 中获得了相同的ProtoTypeBean 实例。问题是,每次我在 foreach 循环中调用它时,我如何让 Spring 理解给我一个新的 prototypeBean 实例? 我发现我可以使用 ApplicationContext.getBean() 在 foreach 循环中获取 Bean 的新实例,但我也听说这是一种不好的做法。所以请帮助我进行最佳实践。

【问题讨论】:

标签: java spring spring-boot oop dependency-injection


【解决方案1】:

使用ObjectProvider 懒惰地得到你想要的结果。然而,第一个原型作用域的 bean 不会出现在 bean 列表中,因为它们是原型作用域的。

@AllArgsConstructor
@Service
public class ServiceClass {

    private final ObjectProvider<ProtoTypeBean> provider;

    public void demoMethod(ArrayList<String> someArrayList) {
        PrototypeBean pb = provider.getIfUnique();
        for(var singleString: someArrayList) {
            pb.setFieldValue(singleString);
            pb.add(prototypeBean);              
        }
        System.out.println(prototypeBean.toString());
    }
}

此外,如果您不需要为您的对象进行所有依赖注入、代理创建等,那么何必费心呢。 Spring 应用程序中的 new 关键字没有任何问题。并非所有事情都必须由 Spring 管理。

【讨论】:

  • 完美。这就是我一直在寻找的。感谢分享知识
【解决方案2】:

您已将ServiceClass 声明为@RestController,因此它是一个具有单例范围的bean。这意味着它被创建一次,ProtoTypeBean 也只被注入一次。这就是为什么每次都有相同的对象。

要查看原型如何工作,您必须将 bean 注入其他 bean。这意味着,拥有两个@Components,都自动装配ProtoTypeBeanProtoTypeBeans 实例将在两者中有所不同。

您需要的是使用new 关键字创建的常规对象。

【讨论】:

    【解决方案3】:

    设置您的原型 bean,如下所示:

    @Getter
    @Setter
    @Component
    @Scope("prototype")
    public class ProtoTypeBean {
    
      final private String param;
    
      public ProtoTypeBean(final String p) {
        this.param = p;
      }
    }
    

    现在,在您的服务类中使用 BeanFactory 为您创建 bean:

    @Service
    @AllArgsConstructor
    public class ServiceClass {
    
      private final BeanFactory factory;
      private List<ProtoTypeBean> prototypeBeans;
    
      @Autowired
      public ServiceClass(final BeanFactory f) {
        this.factory = f;
      }
    
      public void demoMethod(List<String> someArrayList) {
    
        this.prototypeBeans = someArrayList
            .stream()
            .map(param -> factory.getBean(ProtoTypeBean.class, param))
            .collect(Collectors.toList());
      }
    }
    

    【讨论】:

      【解决方案4】:

      我最近遇到了这个问题。我确信一定有比我更好的方法,但我就是这样做的:

      public class ServiceClass {
      
      ArrayList<ProtoTypeBean> prototypeBeans = new ArrayList<>();
      
      @Autowired
      ApplicationContext ctx;
      
      public void demoMethod(ArrayList<String> someArrayList) {
          for(var singleString: someArrayList) {
              //magic is in below line.. getting a bean from ApplicatioContext.
              ProtoTypeBean prototypeBean= ctx.getBean("protoTypeBean"); //Or ctx.getBean(ProtoTypeBean.class);
              prototypeBean.setFieldValue(qBean.getFieldValue());
      
              prototypeBeans.add(prototypeBean);              
          }
          System.out.println(prototypeBeans.toString());
      }
      

      这样,Spring 容器总是会给你一个新的实例。并且完全由 Spring 容器管理。

      你尝试过的方式,我也尝试过,但它总是会在自动装配时注入一个实例,因此违背了原型设计的目的。

      您本可以使用new 关键字。但这只是常规的 Java 实例化,我认为新实例不是由 Spring 管理的,因为它是用 @Component 而不是 @Configuration 注释的。不过我这里可能是错的。

      【讨论】:

      • 要求 ApplicationContext 提供 bean 是一种可能的解决方案,但正如我在问题中已经提到的那样,这是一种不好的做法。实现此目的的最佳方法是使用 ObjectProvider 类的 getIfUnique() 方法。感谢分享知识。
      • @RitamDas 我看了其他两个答案。它增加了我的知识,我很感激我参与了这个问题。感谢您提出发人深省的问题。
      猜你喜欢
      • 2023-03-23
      • 1970-01-01
      • 2015-04-26
      • 2011-10-14
      • 2015-03-12
      • 1970-01-01
      • 2014-11-20
      • 2014-09-29
      • 2020-11-10
      相关资源
      最近更新 更多