Dans mon application, j'ai deux classes ayant le même nom, mais bien sûr dans des packages différents.
Les deux classes doivent être injectées dans l'application; Malheureusement, je reçois le message d'erreur suivant:
package org.pmesmeur.springboot.training.service.feature2; import org.springframework.stereotype.Component; @Component public class MyFeature implements IMyFeature { @Override public void print() { System.out.print("FooBar"); } }
Mon problème peut être reproduit par l'exemple suivant:
package org.pmesmeur.springboot.training.service.feature2; public interface IMyFeature { void print(); }
XXX
package org.pmesmeur.springboot.training.service.feature1; import org.springframework.stereotype.Component; @Component public class MyFeature implements IMyFeature { @Override public void print() { System.out.print("HelloWorld"); } }
package org.pmesmeur.springboot.training.service.feature1; public interface IMyFeature { void print(); }
@Component @EnableConfigurationProperties(ServiceProperties.class) public class MyService implements IService { private final ServiceProperties serviceProperties; private final IProvider provider; private final org.pmesmeur.springboot.training.service.feature1.IMyFeature f1; private final org.pmesmeur.springboot.training.service.feature2.IMyFeature f2; @Autowired public MyService(ServiceProperties serviceProperties, IProvider provider, org.pmesmeur.springboot.training.service.feature1.IMyFeature f1, org.pmesmeur.springboot.training.service.feature2.IMyFeature f2) { this.serviceProperties = serviceProperties; this.provider = provider; this.f1 = f1; this.f2 = f2; } ...
Si j'utilise des noms différents pour mon classes MyFeature
, mon problème disparaît !!!
J'ai l'habitude de travailler avec Guice et ce framework n'a pas ce genre de problème / limitation
Il semble que le framework d'injection de dépendances Spring n'utilise que le nom-classe au lieu de nom-package + nom-classe afin de sélectionnez ses classes.
Dans la «vraie vie», j'ai ce problème avec un projet beaucoup plus grand et je préférerais fortement ne pas avoir à renommer mes classes: quelqu'un peut-il m'aider?
Un dernier point, je préférerais éviter les "trucs" comme l'utilisation
@Qualifier (value = "ABC")
lors de l'injection de mes classes: dans mon échantillon, il ne devrait y avoir aucune ambiguïté pour trouver l'instance correcte deMyFeature
car ils n'implémentent pas la même interface
4 Réponses :
Vous pouvez attribuer une valeur à chaque composant, par exemple @Component (value = "someBean")
puis injectez-le avec @Qualifier
par exemple
@Autowired public SomeService(@Qualifier("someBean") Some s){ //.... }
Vous pouvez faire quelque chose comme ceci;
dans le package a
@Configuration public class Configuration { @Bean public a.MyFeature f1() { return new a.MyFeature(); } @Bean public b.MyFeature f2() { return new b.MyFeature(); } }
dans le package b
p>
public class MyFeature implements IMyFeature { @Override public void print() { System.out.print("HelloWorld"); } }
et dans certaines config class ;
public class MyFeature implements IMyFeature { @Override public void print() { System.out.print("FooBar"); } }
Ensuite, vous pouvez les connecter automatiquement avec les noms f1
et f2
, qui sont les noms de leurs méthodes de constructeur de bean respectives.
Vous pouvez faire la même chose avec
@Component ("f1")
&@Component("f2")
Même si différentes interfaces sont implémentées et sont dans des packages différents, un nom de bean identique cause ce problème, et vous devez utiliser une sorte de dénomination personnalisée pour distinguer. Utilisation de la logique Spring personnalisée serait bien trop moche par rapport à ce que vous feriez avec les solutions ci-dessus.
Spring fournit un câblage automatique par type et par nom. Votre nom de classe est le même. Par défaut, Spring considère uniquement le nom de classe et non le package. Mais vous pouvez remplacer ce comportement en définissant une implémentation personnalisée de l'interface BeanNameGenerator dans laquelle vous pouvez générer un nom en utilisant à la fois le package et le nom. Je ne propose pas de solution de code car je pense que vous devriez en savoir plus à ce sujet.
La simple réimplémentation de BeanNameGenerator
ajoute un nouveau problème pour les beans déclarés / instanciés par des noms
static class BeanNameGeneratorIncludingPackageName extends AnnotationBeanNameGenerator { public BeanNameGeneratorIncludingPackageName() { } @Override public String buildDefaultBeanName(BeanDefinition beanDefinition, BeanDefinitionRegistry beanDefinitionRegistry) { return beanDefinition.getBeanClassName(); } }
J'ai résolu ce problème en étendant AnnotationBeanNameGenerator code > et redéfinition de la méthode
buildDefaultBeanName ()
@Component("HelloWorld") class MyComponent implements IComponent { ... } @Qualifier(value = "HelloWorld") IComponent component
lorsque les deux interfaces de différents packages ont les mêmes méthodes abstraites, pourquoi ne pouvez-vous pas utiliser une seule interface?
Utilisez-vous l'annotation
@Bean
n'importe où dans votre projet?Pouvez-vous donner un commentaire sur ma réponse? J'aimerais aider à finaliser ce numéro !!