3
votes

Comment demander le bean Prototype dans la classe de service Spring sans application

J'ai un composant défini avec une portée prototype. Je souhaite utiliser ce composant dans ma classe de service. Je veux que Spring me fournisse une nouvelle instance de ce Bean chaque fois que je l'appelle.

Classe de composant:

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

Classe de service:

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

En utilisant cette configuration, j'obtiens la même instance de ProtoTypeBean dans mon prototypeBeans ArrayList. La question est, comment pourrais-je faire comprendre à Spring de me donner une nouvelle instance de prototypeBean chaque fois que je l'appelle dans la boucle foreach? J'ai trouvé que je pouvais utiliser ApplicationContext.getBean () pour obtenir une nouvelle instance de la boucle Bean in foreach, mais j'ai également entendu dire que c'était une mauvaise pratique. Alors merci de m'aider avec les meilleures pratiques.


4 commentaires

Vous avez le même objet car il n'est injecté qu'une seule fois dans cette classe, donc une seule nouvelle instance est créée


stackoverflow.com/questions/25165507 /…


Copie possible du Haricot à portée Spring Prototype dans un singleton


Un proxy à portée ne fonctionnera pas car cela vous donnera une nouvelle instance pour chaque appel de méthode que vous effectuez. Probablement pas ce que vous voulez. A la place, vous pouvez injecter un Provider ou ObjectFactory à la place.


4 Réponses :


0
votes

J'ai récemment rencontré ce problème. Je suis sûr qu'il doit y avoir un meilleur moyen que le mien, mais voici comment je l'ai fait:

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

De cette façon, le conteneur Spring vous donne toujours une nouvelle instance. Et il est totalement géré par le conteneur Spring.

Comme vous l'avez essayé, j'ai essayé cela aussi, mais cela injecterait toujours une instance au moment du câblage automatique, ce qui va à l'encontre de l'objectif du prototypage. p>

Vous auriez pu utiliser le mot-clé new . Mais alors ce n'est qu'une instanciation Java régulière et je pense que la nouvelle instance n'est pas gérée par Spring car elle est annotée avec @Component au lieu de @Configuration code >. Mais je peux me tromper ici.


2 commentaires

Demander à ApplicationContext de fournir des beans est une solution possible, mais comme je l'ai déjà mentionné sur mon problème, c'est une mauvaise pratique. Le meilleur moyen d'y parvenir est d'utiliser la méthode getIfUnique () de la classe ObjectProvider . Merci pour le partage des connaissances.


@RitamDas J'ai regardé les deux autres réponses. Cela a augmenté mes connaissances et je suis reconnaissant d'avoir participé à cette question. Merci d'avoir posé une question stimulante.



1
votes

Vous avez déclaré ServiceClass comme @RestController , donc c'est un bean avec une portée singleton. Cela signifie qu'il est créé une fois et que ProtoTypeBean est également injecté une seule fois. C'est pourquoi chaque fois que vous avez le même objet.

Pour voir comment fonctionne le prototype, vous devrez injecter le bean dans un autre bean. Cela signifie qu'avoir deux instances de @Component , les deux autowiring ProtoTypeBean , les instances de ProtoTypeBean seraient différentes dans les deux.

Ce dont vous avez besoin est un objet standard créé avec le mot-clé new .


0 commentaires

3
votes

Utilisez un ObjectProvider pour obtenir paresseusement le résultat souhaité. Cependant, le premier prototype de haricot à portée ne sera pas représenté dans la liste des haricots car ils sont à portée de prototype.

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

De plus, si vous n'avez pas besoin de toute l'injection de dépendances, de la création de proxy, etc. pour votre objet, alors pourquoi s'embêter. Il n'y a rien de mal avec juste le mot-clé new dans une application Spring. Tout ne doit pas être géré par Spring.


1 commentaires

Parfait. C'est ce que je cherchais. Merci pour le partage des connaissances



1
votes

Configurez votre bean prototype comme ceci:

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

Maintenant, dans votre classe de service, utilisez un BeanFactory pour créer les beans pour vous:

@Getter
@Setter
@Component
@Scope("prototype")
public class ProtoTypeBean {

  final private String param;

  public ProtoTypeBean(final String p) {
    this.param = p;
  }
}


0 commentaires