web-dev-qa-db-fra.com

Pourquoi Spring a-t-il des problèmes de dépendance circulaires sur une machine et pas sur une autre?

Je ne parviens pas à exécuter une application Spring Data dans mon environnement. Je suis sous Debian, mais mes collègues utilisent soit Mac, soit Ubuntu. Je n'ai rien de particulier à configurer dans les variables de mon environnement et j'utilise exactement la même version de Java que les autres.

J'ai vu cela dans les journaux, ce qui suggère que c'est un problème de référence circulaire qui conduit à l'échec de l'instanciation:

nested exception is
org.springframework.beans.factory.BeanCreationException: Error
creating bean with name 'flyway.CONFIGURATION_PROPERTIES':
Initialization of bean failed;
...
nested exception is
org.springframework.beans.factory.BeanCurrentlyInCreationException:
Error creating bean with name 'flyway': Requested bean is currently in
creation: Is there an unresolvable circular reference?

Le problème semble donc être que la voie de migration a besoin de certaines dépendances et qu'elle a besoin d'une voie de migration.

La question qui se pose est la suivante: pourquoi cela ne se produit-il que dans mon environnement et non dans les autres? Même sur les tests utilisant H2 en mémoire, je vois le problème, donc ce n'est pas ma base de données qui est en cause.

Est-il possible que l'auto-câblage de Spring soit confondu et tente de faire les choses dans le mauvais ordre, de sorte que le référentiel soit nul lorsqu'il tente de le définir?

Spring a-t-il un tri topologique mal implémenté pour les dépendances de commande?

Pourquoi cela se conduirait-il mal dans mon environnement?

L'ordre du classpath pourrait-il influencer son comportement?

=======================

L'application ne démarrera pas avec cette erreur:

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'contentItemRepository': FactoryBean threw exception on object creation; nested exception is Java.lang.IllegalArgumentException: Repository interface must not be null on initialization!
    at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.Java:175)
    at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.getObjectFromFactoryBean(FactoryBeanRegistrySupport.Java:127)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getObjectForBeanInstance(AbstractBeanFactory.Java:1517)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.Java:251)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.Java:194)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.Java:1127)

============================

La signature ContentItemRepository est:

@Repository
@Transactional
public interface ContentItemRepository extends JpaRepository<ContentItem, String>, JpaSpecificationExecutor<ContentItem> {

============================

Cela fonctionnait pour moi, et j'ai pu identifier le commit qui a cassé la construction, en parcourant tous les commits, en effectuant une nouvelle installation MVN et en essayant de démarrer le serveur, jusqu'à ce que je trouve le delta qui l'a brisé.

Le 'contentItemRepository' qui ne peut pas être null est celui-ci:

@Component
+public class UrlAliasRequestConverter implements Mapper<UrlAliasRequest, UrlAlias> {
+
+    /**
+     * The content item contentItemType repository.
+     */
+    @Autowired
+    private ContentItemRepository contentItemRepository;
19
user2800708

Je ne comprends pas pourquoi c'est arrivé, mais voici la seule solution que j'ai trouvée:

Installez Debian 8 et ça marche.

Je l'ai essayé dans une autre installation propre de Debian 7, et j'ai moins d'erreurs là-bas, mais j'en ai quand même eu. Une installation propre de Debian 8 semblait fonctionner.

Je ne peux que conclure que Java doit appeler une bibliothèque système, ce qui affecte en quelque sorte l'ordre dans lequel les dépendances de Spring sont résolues. Cette bibliothèque doit être mise à niveau dans Debian 8, me mettant en ligne avec les installations Ubuntu utilisées par d’autres développeurs et producteurs.

Je ne sais pas ce que cette bibliothèque pourrait être ... Quelque chose qui analyse les fichiers dans le système de fichiers, en les rapportant dans un ordre différent? Quelque chose qui décompresse un fichier .jar, en rapportant son contenu dans un ordre différent?

Il me semble faux que notre code soit si sensible à l'ordre exact de résolution de dépendance et d'injection, mais cela semble être le cas. Il ne semble pas non plus que notre code comporte quoi que ce soit qui devrait le rendre sensible à la commande, nous ne faisons rien de fou et nous suivons des schémas d'utilisation assez standard.

Trop de magie du printemps garde le château de cartes debout si vous me le demandez. Mes autres projets sont sur DropWizard, et là l'injection de dépendance est codée manuellement, donc pas de surprises.

=== Mise à jour

J'ai mis à niveau la boîte Debian 7 à 8 et le problème persiste. Par conséquent, mon hypothèse selon laquelle il s'agit d'une version de bibliothèque est fausse. Doit être quelque chose à propos de mon environnement.

J'ai créé un nouvel utilisateur sur la boîte. Le problème est toujours là pour cet utilisateur. Il y a quelque chose dans cette boîte qu'il n'aime pas vraiment, mais je ne peux pas comprendre ce que c'est.

J'aimerais bien comprendre la cause réelle, mais je ne pense pas pouvoir consacrer plus de temps à la solution.

Quoi qu’il en soit, une nouvelle installation de Debian 8 résout le problème.

1
user2800708

Ceci est très probablement lié à l'ordre dans lequel les fichiers de classe sont lus en ligne

dir.listFiles() in PathMatchingResourcePatternResolver.doRetrieveMatchingFiles()

Étant donné que l'ordre d'affichage des fichiers (fichiers de classe) dépend de la plate-forme et qu'aucun tri n'est effectué sur le tableau, l'ordre dans lequel les classes sont chargées dépend de la manière dont la plate-forme les renvoie.

ref: http://forum.spring.io/forum/spring-projects/container/115998-circular-dependency-identification-inconsistent

4
xki

J'ai le même problème sur Ubuntu 16.04.

J'ai trouvé que le problème avec

@ComponentScan(basePackages = "com.my.app")

Le code utilise au moins 5 machines différentes (windows, ubuntu 15.04 et ubuntu 16.04 desktop) mais ne démarre pas notre serveur CI (serveur ubuntu 16.04).

Après avoir changé

@ComponentScan(basePackages = "com.my.app")

à

@ComponentScan(basePackages = {"com.my.app.service", "com.my.app.config", "com.my.app"})

le code est également exécuté sur le serveur CI.

Je pense que c'est un problème de printemps avec un chargeur de haricots ...

UPDATE:

https://github.com/spring-projects/spring-boot/issues/6045

https://jira.spring.io/browse/SPR-14307

4
István Pató

J'ai eu le même problème hier. Je ne sais pas pourquoi, mais j'ai trouvé un moyen de résoudre le problème: marquez tous les beans impliqués dans l'arbre de dépendance circulaire comme étant lazy-init. Non seulement ceux qui sont directement circulaires dépendent les uns des autres, mais tous les haricots qui les référencent!

Le projet est ancien et n'utilise donc que le printemps 3. Je ne suis pas sûr que les versions ultérieures de Spring rencontrent toujours ce problème.

2
fifman

Quelle est l’interface de votre référentiel? Vous pouvez consulter le code source Spring et voir pourquoi l'exception est levée:

https://github.com/spring-projects/spring-data-commons/blob/master/src/main/Java/org/springframework/data/repository/core/support/RepositoryFactoryBeanSupport.Java

@SuppressWarnings("unchecked")
public Class<? extends T> getObjectType() {
    return (Class<? extends T>) (null == repositoryInterface ? Repository.class : repositoryInterface);
}

Voici un exemple de mon référentiel:

@Repository
public interface GameRepository extends JpaRepository<Game, Long> {
2
Brian Kates

Je ne sais pas pourquoi ce problème particulier se produirait sur une machine et non sur une autre, mais je pense que le problème est probablement que les deux beans utilisent l'injection de constructeur pour se passer des références, ce qui crée une dépendance circulaire insoluble. sa construction, qui à son tour n'a pas besoin de, ne peut être créée sans que l'autre ait déjà été créé. Si les deux objets nécessitent une référence à l'autre, vous devez plutôt la transmettre via l'injection de setter après que les objets aient déjà été créés. Les réponses à cette question sont pertinentes pour le problème que vous rencontrez.

1
JacksonWeekes

Nous avons rencontré ce problème aujourd'hui, dans une situation presque identique: le démarrage de l'application échouait à cause d'une référence circulaire, apparemment lors de la construction d'instances Spring Data @Repository, et uniquement sur la machine d'un développeur.

Dans notre cas, la solution consistait à refactoriser notre configuration de sorte qu'un ApplicationListener<BeforeSaveEvent> ne soit plus une classe anonyme définie dans une classe @Configuration et devienne un @Component de niveau supérieur.

Il semble que de manière non évidente, les référentiels Spring Data conservent une sorte de contrôle sur ces écouteurs d’applications. En ayant notre classe interne anonyme, le référentiel conservait implicitement une référence à son @Configuration (et donc une boucle de dépendance détectée dû aux beans instanciés via ce @Configuration qui a finalement été automatiquement armé du référentiel).

Toutes les réponses décrivant le changement de lazy init, la modification de votre ordre d'analyse des composants, la réinstallation de votre système d'exploitation, sa mise hors puis sous tension, etc. ne font que masquer le problème plutôt que de le résoudre réellement. La raison pour laquelle les choses fonctionnent ou ne fonctionnent pas sur différentes machines est simplement que, comme @xki y fait allusion, l'ordre de construction du graphe d'objet n'est pas déterministe.

0
ryanp