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 Réponses :
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.
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
@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.
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
.
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.
Parfait. C'est ce que je cherchais. Merci pour le partage des connaissances
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; } }
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
ouObjectFactory
à la place.