web-dev-qa-db-fra.com

Exemple de référence circulaire à ressort

J'ai un référence circulaire dans l'un de mes projets au travail en utilisant spring, que je ne peux pas résoudre et échoue avec l'erreur suivante au démarrage:

'org.springframework.security.authenticationManager': Requested bean is currently in creation: Is there an unresolvable circular reference?

J'ai essayé de recréer le même problème à un niveau plus petit dans un exemple de projet (sans tous les détails de mon projet de travail). Je n'ai cependant pas été en mesure de proposer un scénario plausible où le printemps échoue avec une erreur. Voici ce que j'ai:

public class ClassA {
    @Autowired
    ClassB classB;
}

public class ClassB {
    @Autowired
    ClassC classC;
}

@Component
public class ClassC {
    @Autowired
    ClassA classA;
}

@Configuration
public class Config {
    @Bean
    public ClassA classA() {
        return new ClassA();
    }

    @Bean
    public ClassB classB() {
        return new ClassB();
    }
}

J'ai un scénario similaire dans mon projet, qui échoue, et je m'attendais à ce que le printemps se plaigne également dans mon exemple de projet. Mais ça marche bien! Quelqu'un peut-il me donner un exemple simple de la façon de casser le ressort avec l'erreur de référence circulaire?

Edit: J'ai résolu le problème en utilisant javax.inject.Provider. La seule autre différence dans les 2 projets était que les annotations utilisées étaient javax.inject.Inject et javax.annotation.ManagedBean à la place de @Autowired et @Component.

26
robbin

Ceci est un vieux fil, donc je suppose que vous avez presque oublié le problème, mais je veux vous informer du mystère. J'ai rencontré le même problème et le mien ne s'est pas éteint comme par magie, j'ai donc dû résoudre le problème. Je vais résoudre vos questions étape par étape.

1. Pourquoi vous ne pouviez pas reproduire l'exception de référence circulaire?

Parce que Spring s'en occupe. Il crée des haricots et les injecte au besoin .

2. Alors pourquoi votre projet produit-il l'exception?

  • Comme l'a dit @sperumal, Spring peut produire une exception circulaire si vous utilisez l'injection de constructeur
  • Selon le journal, vous utilisez Spring Security dans votre projet
  • Dans la configuration de Spring Security, ils utilisent l'injection de constructeur
  • Vos beans qui injectent le authenticationManager avaient la référence circulaire

3. Alors pourquoi l'exception a-t-elle disparu mystiquement?

L'exception peut ou non se produire dépend de l'ordre de création des beans. Je suppose que vous avez fait plusieurs *context.xml fichiers ou plus, et chargez-les avec config quelque chose comme ci-dessous dans web.xml

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:*-context.xml</param-value>
</context-param>

Les fichiers xml seront chargés par la classe XmlWebApplicationContext et l'ordre de chargement des fichiers n'est pas garanti. Il charge simplement les fichiers du système de fichiers. Le problème est là. Il n'y a pas de problème si la classe charge d'abord le fichier de contexte d'application, car vos beans sont déjà créés lorsqu'ils sont utilisés pour l'injection de construction de Spring Security. Mais, s'il charge d'abord le fichier de contexte Spring Security, le problème de référence circulaire se produit, car Spring essaie d'utiliser vos beans dans l'injection de constructeur avant qu'ils aient été créés.

4. Comment résoudre le problème?

Forcez l'ordre de chargement des fichiers xml. Dans mon cas, j'ai chargé le fichier xml de contexte de sécurité à la fin du fichier de contexte d'application en utilisant <import resource="">. L'ordre de chargement peut être modifié en fonction des environnements, même avec le même code, je recommande donc de définir l'ordre pour supprimer les problèmes potentiels.

26
Sanghyun Lee

Vous pouvez utiliser @Lazy pour indiquer que le haricot est créé paresseusement, rompant ainsi le vif cycle de câblage automatique.

L'idée est qu'un haricot sur le cycle pourrait être instancié en tant que proxy, et juste au moment où il est vraiment nécessaire, il sera initialisé. Cela signifie que tous les beans sont initialisés sauf celui qui est un proxy. L'utiliser pour la première fois déclenchera la configuration et comme les autres beans sont déjà configurés, ce ne sera pas un problème.

Extrait d'un numéro de Spring-Jira:

Annotation @Lazy qui peut être utilisée conjointement avec @Configuration pour indiquer que tous les beans de cette classe de configuration doivent être initialisés paresseusement. Bien sûr, @Lazy peut également être utilisé conjointement avec des méthodes @Bean individuelles pour indiquer une initialisation paresseuse sur une base un par un. https://jira.springsource.org/browse/SJC-26

Cela signifie que l'annotation de votre bean comme @Lazy serait suffisant. Ou si vous préférez simplement annoter la classe de configuration comme @Lazy comme suit:

@Configuration
@Lazy
public class Config {
    @Bean
    public ClassA classA() {
        return new ClassA();
    }

    @Bean
    public ClassB classB() {
        return new ClassB();
    }
}

Si vous implémentez une interface de vos beans, cela fonctionnera très bien.

26
Francisco Spaeth

Selon la documentation de Spring, il est possible d'obtenir un problème de dépendance circulaire ou BeanCurrentlyInCreationException en utilisant injection de constructeur .

La solution pour résoudre le problème consiste à utiliser des setters au lieu de l'injection de constructeur.

Référence http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/html/beans.html .

9
sperumal

Quelques lectures à ce sujet:

http://blog.jdevelop.eu/?p=382

Ces dépendances circulaires ne sont pas cool et doivent être évitées autant que possible

2