Rastreando la causa de Spring's "no es elegible para auto-proxy"
Pregunta
Cuando comienzas a jugar con las cosas de proxy automático de Spring, a menudo te encuentras con este comportamiento como se documenta:
Clases que implementan el Interfaz BeanPostProcessor son especial, y por eso son tratados diferente por el contenedor. Todos BeanPostProcessors y sus directamente los granos referenciados serán instanciados en el inicio, como parte de la especial fase de inicio de la ApplicationContext, entonces todos esos BeanPostProcessors serán registrados ordenado - y aplicado a todos los frijoles más. Desde AOP el auto-proxy se implementa como BeanPostProcessor en sí, no BeanPostProcessors o directamente los frijoles referenciados son elegibles para auto-proxy (y por lo tanto no tendrá aspectos 'entrelazados' en ellos.
Para cualquiera de esos frijoles, debería ver un Mensaje de registro de información: & # 8220; Bean 'foo' no es elegible para ser procesado por todos BeanPostProcessors (por ejemplo: no elegible para auto-proxy) & # 8221 ;.
En otras palabras, si escribo mi propio BeanPostProcessor, y esa clase hace referencia directamente a otros beans en el contexto, entonces esos beans referenciados no serán elegibles para el procesamiento automático, y se registra un mensaje a tal efecto.
Mi problema es que rastrear dónde está esa referencia directa puede ser muy difícil, ya que la "referencia directa" de hecho, puede ser una cadena de dependencias transitivas que termina tomando la mitad de los beans en el contexto de la aplicación. Todo lo que Spring le da es ese único mensaje de información, y en realidad no es de mucha ayuda, además de decirle cuándo un bean ha sido capturado en esta web de referencias.
El BeanPostProcessor que estoy desarrollando tiene referencias directas a otros beans, pero es un conjunto muy limitado de referencias. A pesar de esto, casi todos los beans en mi contexto se excluyen de la representación automática, según los mensajes de registro, pero no puedo ver dónde está sucediendo esa dependencia.
¿Alguien ha encontrado una mejor manera de rastrear esto?
Solución 2
Solo para cerrar un poco esta pregunta, el colapso del gráfico de objeto no inicializado fue causado por el BeanPostProcessor
usando @Autowired
para obtener sus dependencias y el mecanismo de autoconexión efectivamente causó que cualquier otra definición de bean se inicializara antes de que mi BeanPostProcessor
tuviera la oportunidad de opinar sobre el asunto. La solución es no utilizar el cableado automático para sus BPP.
Otros consejos
Sigue esta receta:
- Abra
BeanPostProcessorChecker
en su IDE (es una clase interna deAbstractApplicationContext
) - Establezca un punto de interrupción en
si (logger.isInfoEnabled ()) {
en el métodopostProcessAfterInitialization
- Ejecute su código
-
Cuando llegues al punto de interrupción, busca las llamadas a
getBean (String, Class < T >)
en el seguimiento de tu pila.Una de estas llamadas intentará crear un
BeanPostProcessor
. Ese frijol debería ser el culpable.
Trasfondo
Imagina esta situación:
public class FooPP implements BeanPostProcessor {
@Autowire
private Config config;
}
Cuando Spring tiene que crear config
(ya que es una dependencia de FooPP
), tiene un problema: el contrato dice que todos los BeanPostProcessor
debe aplicarse a cada bean que se está creando. Pero cuando Spring necesita config
, ¡hay al menos un PP (es decir, FooPP
) que no está listo para el servicio!
Esto empeora cuando usa una clase @Configuration
para definir este bean:
@Configuration
public class BadSpringConfig {
@Lazy @Bean public Config config() { return new Config(); }
@Lazy @Bean public FooPP fooPP() { return new FooPP(); }
}
Cada clase de configuración es un bean. Eso significa construir una fábrica de frijoles a partir de BadSpringConfig
, Spring necesita aplicar el fooPP
postprocesador, pero para hacer eso, primero necesita la fábrica de frijoles ...
En este ejemplo, es posible romper una de las dependencias cíclicas. Puede hacer que FooPP
implemente BeanFactoryAware
para que Spring inyecte el BeanFactory
en el postprocesador. De esa manera, no necesita cableado automático.
Más adelante en el código, puede pedir perezosamente el 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(...);
}
Para romper el ciclo entre la fábrica de frijoles y el postprocesador, debe configurar el postprocesador en un archivo de configuración XML. Spring puede leer eso y construir todas las estructuras sin confundirse.
No estoy seguro de si es de alguna ayuda, pero los Spring IDE de Eclipse vista gráfica parece que podría ser útil para ordenar las referencias de frijol ..