Détecter la cause de Spring «non éligible à l'auto-proxy»
Question
Lorsque vous commencez à bricoler avec les éléments du proxy automatique de Spring, vous rencontrez souvent ce problème tel que documenté:
Les classes qui implémentent le Interface BeanPostProcessor sont spécial, et donc ils sont traités différemment par le conteneur. Tout BeanPostProcessors et leurs directement les haricots référencés seront instanciés au démarrage, dans le cadre de la spéciale phase de démarrage du ApplicationContext, puis tous ceux BeanPostProcessors sera enregistré de manière triée - et appliquée à tous les autres haricots. Depuis AOP l'auto-proxy est implémenté en tant que BeanPostProcessor lui-même, non BeanPostProcessors ou directement les haricots référencés sont éligibles pour l'auto-proxy (et n'aura donc pas aspects 'tissés' en eux.
Pour un tel haricot, vous devriez voir un message du journal d’information: «Le haricot 'foo' n’est pas éligible pour être traité par tous BeanPostProcessors (par exemple: not éligible à l'auto-proxy) ".
En d'autres termes, si j'écris mon propre BeanPostProcessor et que cette classe référence directement d'autres beans dans le contexte, ces beans référencés ne seront pas éligibles pour le proxy automatique et un message sera consigné à cet effet.
Mon problème est qu'il peut être très difficile de localiser cette référence directe, car la "référence directe" peut en fait être une chaîne de dépendances transitives qui finit par englober la moitié des fèves dans le contexte de l'application. Tout ce que Spring vous envoie, c’est ce message d’information unique, qui ne vous aidera pas beaucoup, si ce n’est à vous dire qu’un haricot est pris dans ce réseau de références.
Le BeanPostProcessor que je développe contient des références directes à d'autres beans, mais il s'agit d'un ensemble de références très limité. Malgré cela, pratiquement tous les haricots de mon contexte sont exclus de l'auto-proxy, selon les messages du journal, mais je ne vois pas où cette dépendance se produit.
Quelqu'un a-t-il trouvé un meilleur moyen de localiser cela?
La solution 2
Juste pour mettre un terme à cette question, l’effondrement du graphe d’objet non initialisé a été provoqué par le BeanPostProcessor
qui utilise @Autowired
pour obtenir ses dépendances et le mécanisme autowire. a effectivement provoqué l'initialisation de toutes les autres définitions de beans avant que mon BeanPostProcessor
n'ait eu la possibilité de s'exprimer. La solution consiste à ne pas utiliser le câblage automatique pour vos BPP.
Autres conseils
Suivez cette recette:
- Ouvrez
BeanPostProcessorChecker
dans votre IDE (c'est une classe interne deAbstractApplicationContext
) - Définissez un point d'arrêt sur
if (logger.isInfoEnabled ()) {
dans la méthodepostProcessAfterInitialization
- Exécutez votre code
-
Lorsque vous atteignez le point d'arrêt, recherchez les appels à
getBean (String, Class < T >)
dans le suivi de votre pile.L'un de ces appels tentera de créer un
BeanPostProcessor
. Ce haricot devrait être le coupable.
Arrière-plan
Imaginez cette situation:
public class FooPP implements BeanPostProcessor {
@Autowire
private Config config;
}
Lorsque Spring doit créer config
(puisqu'il s'agit d'une dépendance de FooPP
), il existe un problème: le contrat indique que tous BeanPostProcessor
doit être appliqué à chaque haricot en cours de création. Mais lorsque Spring a besoin de config
, il existe au moins un PP (à savoir FooPP
) qui n'est pas prêt pour le service!
Cela empire lorsque vous utilisez une classe @Configuration
pour définir ce bean:
@Configuration
public class BadSpringConfig {
@Lazy @Bean public Config config() { return new Config(); }
@Lazy @Bean public FooPP fooPP() { return new FooPP(); }
}
Chaque classe de configuration est un bean. Cela signifie que pour construire une fabrique de beans à partir de BadSpringConfig
, Spring doit appliquer le post-processeur fooPP
, mais pour cela, il a d'abord besoin de la fabrique de beans ...
Dans cet exemple, il est possible de rompre l'une des dépendances cycliques. Vous pouvez faire en sorte que FooPP
implémente BeanFactoryAware
pour que Spring injecte BeanFactory
dans le post-processeur. De cette façon, vous n'avez pas besoin du câblage automatique.
Plus tard dans le code, vous pourrez demander paresseusement le haricot:
private LazyInit<Config> helper = new LazyInit<Config>() {
@Override
protected InjectionHelper computeValue() {
return beanFactory.getBean( Config.class );
}
};
@Override
public Object postProcessBeforeInitialization( Object bean, String beanName ) throws BeansException {
String value = helper.get().getConfig(...);
}
Pour rompre le cycle entre la fabrique de beans et le post-processeur, vous devez configurer le post-processeur dans un fichier de configuration XML. Spring peut lire cela et construire toutes les structures sans être dérouté.
Je ne sais pas si cela peut vous aider, mais les Spring IDE d'Eclipse La vue de graphe semble pouvoir être utile pour trier les références aux beans ..