在没有applicationContext的情况下如何在Spring服务类中请求原型bean

时间:2019-03-11 07:18:53

标签: java spring spring-boot

我有一个用原型范围定义的组件。我想在服务类中使用该组件。我希望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循环中调用SpringBean时,如何使Spring理解给我一个新的prototypeBean实例? 我发现我可以使用ApplicationContext.getBean()在foreach循环中获取Bean的新实例,但我也听说这是一种不好的做法。因此,请以最佳实践帮助我。

4 个答案:

答案 0 :(得分:3)

使用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管理。

答案 1 :(得分:2)

您已将ServiceClass声明为@RestController,因此它是具有单例作用域的bean。这意味着它仅创建一次,并且ProtoTypeBean也仅注入一次。这就是为什么每次您都有相同的对象。

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

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

答案 2 :(得分:1)

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

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进行注释的。我可能在这里错了。

答案 3 :(得分:1)

设置类似于以下内容的原型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());
  }
}