java - example - spring-aop maven
Rastreando causa de Spring "no elegible para auto-proxying" (3)
Cuando empiezas a jugar con el auto-proxy de Spring, a menudo te encuentras con este comportamiento como está documentado:
Las clases que implementan la interfaz BeanPostProcessor son especiales, por lo que el contenedor las trata de manera diferente. Todos los BeanPostProcessors y sus beans directamente referenciados serán instanciados al inicio, como parte de la fase especial de inicio de ApplicationContext, luego todos esos BeanPostProcessors se registrarán de forma ordenada y se aplicarán a todos los beans adicionales. Como el Auto-Proxy de AOP se implementa como un BeanPostProcessor, ningún BeanPostProcessors o beans directamente referenciados son elegibles para el auto-proxying (y por lo tanto no tendrán aspectos ''tejidos'' en ellos).
Para cualquier tal bean, debería ver un mensaje de registro de información: "Bean ''foo'' no es elegible para ser procesado por todos los BeanPostProcessors (por ejemplo: no elegible para el auto-proxying)".
En otras palabras, si escribo mi propio BeanPostProcessor, y esa clase hace referencia directamente a otros beans en el contexto, esos beans a los que se hace referencia no serán elegibles para el auto-proxying, y se registra un mensaje para ese 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 teniendo la mitad de los beans en el contexto de la aplicación. Todo lo que Spring le ofrece es ese mensaje de información único, y no es de mucha ayuda, más allá de decirle cuándo se ha atrapado un frijol en esta red 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 están siendo excluidos de ser auto-proxy, de acuerdo con los mensajes de registro, pero no puedo ver dónde está ocurriendo esa dependencia.
¿Alguien ha encontrado una mejor manera de rastrear esto?
No estoy seguro si es de alguna ayuda, pero la vista de gráfico de Eclipse Spring IDE parece que podría ser útil para clasificar las referencias de bean.
Sigue esta receta:
- Abre
BeanPostProcessorChecker
en tu IDE (es una clase interna deAbstractApplicationContext
) - Establezca un punto de interrupción en
if (logger.isInfoEnabled()) {
en el métodopostProcessAfterInitialization
- Ejecuta tu código
Cuando llegue al punto de interrupción, busque llamadas a
getBean(String,Class<T>)
en su rastro de pila.Una de estas llamadas intentará crear un
BeanPostProcessor
. Ese frijol debería ser el culpable.
Fondo
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 todo BeanPostProcessor
debe aplicar 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 utiliza 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 BadSpringConfig
desde BadSpringConfig
, Spring necesita aplicar el fooPP
posterior al procesador, pero para 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 BeanFactory
en el procesador posterior. De esta forma, no necesita autovinculación.
Más adelante en el código, puede solicitar el bean de forma perezosa:
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 bean y el procesador posterior, debe configurar el postprocesador en un archivo de configuración XML. Spring puede leer eso y construir todas las estructuras sin confundirse.
Solo para cerrar esta cuestión, el colapso del gráfico de objeto no inicializado fue causado por BeanPostProcessor
usando @Autowired
para obtener sus dependencias, y el mecanismo de autowire causó que las demás definiciones de BeanPostProcessor
se inicializaran antes de que BeanPostProcessor
tuviera la oportunidad de tener una opinión en el asunto. La solución es no utilizar el autoenvío para sus BPP.