Rintracciare la causa del "non idoneo al proxy automatico" di Spring
Domanda
Quando inizi a scherzare con le cose di proxy automatico di Spring, spesso ti imbatti in questo comportamento come documentato:
Classi che implementano il Interfaccia BeanPostProcessor sono speciale, e quindi vengono trattati diversamente dal contenitore. Tutti BeanPostProcessors e loro direttamente i bean referenziati verranno istanziati all'avvio, come parte dello speciale fase di avvio di ApplicationContext, quindi tutti quelli I processori BeanPost saranno registrati in modo ordinato - e applicato a tutti gli altri fagioli. Dal momento che AOP il proxy automatico è implementato come a BeanPostProcessor stesso, no BeanPostProcessors o direttamente i fagioli referenziati sono idonei per auto-proxy (e quindi non avrà aspetti "intrecciati" in essi.
Per tale bean, dovresti vedere un messaggio registro informazioni: "Fagiolo" non lo è idoneo per essere elaborato da tutti BeanPostProcessors (ad esempio: no idoneo al proxy automatico) ".
In altre parole, se scrivo il mio BeanPostProcessor e quella classe fa riferimento direttamente ad altri bean nel contesto, i bean a cui si fa riferimento non saranno idonei al proxy automatico e un messaggio verrà registrato in tal senso.
Il mio problema è che rintracciare dove si trova quel riferimento diretto può essere molto difficile, dal momento che il "riferimento diretto" può infatti essere una catena di dipendenze transitive che finisce per contenere metà dei bean nel contesto dell'applicazione. All Spring ti dà quel singolo messaggio informativo, e non è davvero di grande aiuto, oltre a dirti quando un bean è stato catturato in questa rete di riferimenti.
Il processore BeanPost che sto sviluppando ha riferimenti diretti ad altri bean, ma è un insieme di riferimenti molto limitato. Nonostante ciò, praticamente ogni bean nel mio contesto viene quindi escluso dall'auto-proxy, secondo i messaggi di log, ma non riesco a vedere dove sta avvenendo tale dipendenza.
Qualcuno ha trovato un modo migliore per rintracciarlo?
Soluzione 2
Solo per portare un po 'di chiusura a questa domanda, il crollo del grafico a oggetti non inizializzato è stato causato dal BeanPostProcessor
usando @Autowired
per ottenere le sue dipendenze e il meccanismo di autowire di fatto, ogni altra definizione di bean è stata inizializzata prima che il mio BeanPostProcessor
avesse avuto la possibilità di avere voce in capitolo. La soluzione non è utilizzare l'autowiring per i tuoi BPP.
Altri suggerimenti
Segui questa ricetta:
- Apri
BeanPostProcessorChecker
nel tuo IDE (è una classe interna diAbstractApplicationContext
) - Imposta un punto di interruzione su
if (logger.isInfoEnabled ()) {
nel metodopostProcessAfterInitialization
- Esegui il tuo codice
-
Quando raggiungi il punto di interruzione, cerca le chiamate a
getBean (String, Class < T >)
nella traccia dello stack.Una di queste chiamate proverà a creare un
BeanPostProcessor
. Quel fagiolo dovrebbe essere il colpevole.
Sfondo
Immagina questa situazione:
public class FooPP implements BeanPostProcessor {
@Autowire
private Config config;
}
Quando Spring deve creare config
(poiché è una dipendenza di FooPP
), ha un problema: il contratto afferma che tutti i BeanPostProcessor
deve essere applicato a ogni bean che viene creato. Ma quando Spring ha bisogno di config
, c'è almeno un PP (ovvero FooPP
) che non è pronto per il servizio!
Questo peggiora quando si utilizza una classe @Configuration
per definire questo bean:
@Configuration
public class BadSpringConfig {
@Lazy @Bean public Config config() { return new Config(); }
@Lazy @Bean public FooPP fooPP() { return new FooPP(); }
}
Ogni classe di configurazione è un bean. Ciò significa che per costruire un bean factory da BadSpringConfig
, Spring deve applicare il post-processor fooPP
ma per farlo, per prima cosa ha bisogno del bean factory ...
In questo esempio, è possibile interrompere una delle dipendenze cicliche. Puoi fare in modo che FooPP
implementi BeanFactoryAware
per fare in modo che Spring inietti BeanFactory
nel post processore. In questo modo, non è necessario il collegamento automatico.
Più avanti nel codice, puoi chiedere pigramente il bean:
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(...);
}
Per interrompere il ciclo tra il bean factory e il post processor, è necessario configurare il post processor in un file di configurazione XML. Spring può leggerlo e costruire tutte le strutture senza essere confuso.
Non sono sicuro che sia di aiuto, ma Eclipse IDE di primavera visualizzazione grafica sembra che potrebbe essere utile per ordinare i riferimenti di bean ..