2009-07-29 5 views
35

Lorsque vous commencez à déconner avec des trucs de proxy automatique de printemps, vous courez souvent dans ce comportement comme documenté:Traquer cause de « non admissible à l'auto-mandatement » Printemps

classes qui mettent en œuvre l'interface de BeanPostProcessor sont spéciale, et donc ils sont traités différemment par le conteneur. Tous les BeanPostProcessors et leurs directement haricots référencés seront instanciées au démarrage, dans le cadre de la phase de démarrage spéciale du ApplicationContext, tous ces BeanPostProcessors seront inscrits de façon reclassées - et appliqués à tous les autres haricots . Depuis AOP auto-mandatement est mis en œuvre en tant que BeanPostProcessor lui-même, pas BeanPostProcessors ou directement haricots référencés sont admissibles à auto-mandatement (et ne seront donc pas avoir aspects tissés »en eux.

Pour toute haricots, vous devriez voir un message de journal d'information : « 'foo' Bean n'est pas admissible à obtenir traitées par tous BeanPostProcessors (par exemple: non admissible à auto-mandatement) ».

En d'autres termes, si j'écris mon propre BeanPostProcessor et que cette classe référence directement d'autres beans dans le contexte, alors ces beans référencés ne seront pas éligibles pour l'auto-proxying, et un message est enregistré à cet effet. Mon problème est que dépister où cette référence directe est peut être très difficile, puisque la "référence directe" peut en fait être une chaîne de dépendances transitives qui finit par prendre la moitié des beans dans le contexte d'application. Tout le printemps vous donne ce message d'information unique, et ce n'est pas vraiment beaucoup d'aide, au-delà de vous dire quand un haricot a été pris dans ce réseau de références.

Le BeanPostProcessor que je développe possède des références directes à d'autres beans, mais c'est un ensemble très limité de références. Malgré cela, à peu près tous les beans dans mon contexte sont exclus de l'auto-proxy, selon les messages du journal, mais je ne peux pas voir où cette dépendance se produit.

Est-ce que quelqu'un a trouvé une meilleure façon de dépister cela?

+0

Vous pouvez également obtenir ce message d'information pour les classes 'PersistenceExceptionTranslator'. – Raedwald

Répondre

17

Juste pour apporter une fermeture à cette question, l'effondrement du Le graphique d'objet non initialisé a été provoqué par BeanPostProcessor en utilisant @Autowired pour obtenir ses dépendances, et le mécanisme autowire a effectivement causé l'initialisation de toutes les autres définitions de bean avant que mon BeanPostProcessor ait une chance d'avoir son mot à dire. La solution n'est pas d'utiliser autowiring pour vos BPPs.

+2

Ceci n'est pas une solution; qu'est-ce que je fais à la place quand j'ai besoin de haricots dans mon post-processeur? –

3

Je ne sais pas si elle est d'une aide, mais l » Eclipse Spring IDE graph view semble que cela pourrait être utile dans le tri des références de haricots ..

21

Suivre cette recette:

  1. Ouvrir BeanPostProcessorChecker dans votre IDE (il est une classe interne de AbstractApplicationContext)
  2. Définir un point d'arrêt sur if (logger.isInfoEnabled()) { dans la méthode postProcessAfterInitialization
  3. exécuter votre code
  4. Lorsque vous atteignez le point d'arrêt, recherchez les appels à getBean(String,Class<T>) dans votre trace de pile.

    L'un de ces appels tentera de créer un BeanPostProcessor. Ce haricot devrait être le coupable.

Contexte

Imaginez cette situation:

public class FooPP implements BeanPostProcessor { 
    @Autowire 
    private Config config; 
} 

Quand le printemps doit créer config (car il est une dépendance de FooPP), il a un problème: Le contrat dit que tous les BeanPostProcessor doit être appliqué à chaque bean en cours de création. Mais quand le printemps a besoin de config, il y a au moins un PP (à savoir FooPP) qui n'est pas prêt pour le service!

Cela devient pire lorsque vous utilisez une classe @Configuration pour définir ce haricot:

@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 haricot. Cela signifie pour construire une usine de haricots de BadSpringConfig, Spring doit appliquer le post-processeur fooPP mais pour ce faire, il a d'abord besoin de l'usine de haricots ...

Dans cet exemple, il est possible de casser l'un des dépendances cycliques. Vous pouvez faire FooPP mettre en œuvre BeanFactoryAware pour obtenir Spring injecter le BeanFactory dans le post-processeur. De cette façon, vous n'avez pas besoin d'autowiring.

Plus tard dans le code, vous pouvez paresseusement demander la fève:

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(...); 
} 

(source for LazyInit)

Pour briser le cycle entre l'usine de haricot et le post-processeur, vous devez configurer le poste processeur dans un fichier de configuration XML. Le printemps peut lire cela et construire toutes les structures sans se confondre.

+0

cette solution est-elle toujours adaptée aux applications multithread comme les services web? – hudi

+0

'LazyInit' est sans danger, alors oui. –

+0

J'ai été capable de l'utiliser pour traquer ce problème lorsque je convertissais une application Jersey en Spring Boot. Il s'avère que j'avais un bean appelé "conversionService", et ConfigurationPropertiesBindingPostProcessor recherche un bean (optionnel) avec ce nom. Comme il s'agissait d'un BeanPostProcessor référençant mon bean, mes beans référencés par mon service de conversion n'étaient pas câblés correctement (leurs champs @Autowired n'étaient pas définis, par exemple). –

Questions connexes