24

Tengo una referencia circular en uno de mis proyectos en el trabajo usando la primavera, que no estoy en condiciones de solucionar, y se produce el siguiente error en el inicio:primavera ejemplo de referencia circular

'org.springframework.security.authenticationManager': Requested bean is currently in creation: Is there an unresolvable circular reference? 

Intenté Recrear el mismo problema en un nivel más pequeño en un proyecto de muestra (sin todos los detalles de mi proyecto de trabajo). Sin embargo, no he podido encontrar un escenario plausible donde la primavera falla con un error. Esto es lo que tengo:

public class ClassA { 
    @Autowired 
    ClassB classB; 
} 

public class ClassB { 
    @Autowired 
    ClassC classC; 
} 

@Component 
public class ClassC { 
    @Autowired 
    ClassA classA; 
} 

@Configuration 
public class Config { 
    @Bean 
    public ClassA classA() { 
     return new ClassA(); 
    } 

    @Bean 
    public ClassB classB() { 
     return new ClassB(); 
    } 
} 

Tengo un escenario similar en mi proyecto, lo que falla, y yo estaba esperando la primavera para quejarse en mi proyecto de ejemplo también. ¡Pero funciona bien! ¿Puede alguien darme un ejemplo simple de cómo romper la primavera con el error de referencia circular?

Edité: Resolví el problema usando javax.inject.Provider. La única otra diferencia en los 2 proyectos fue que las anotaciones utilizadas fueron javax.inject.Inject y javax.annotation.ManagedBean en lugar de @Autowired y @Component.

Respuesta

23

Este es un hilo viejo, así que supongo que casi te olvidas del problema, pero quiero que sepas sobre el misterio. Encontré el mismo problema, y ​​el mío no desapareció mágicamente, así que tuve que resolver el problema. Resolveré tus preguntas paso a paso.

1. ¿Por qué no se pudo reproducir la excepción de referencia circular?

Porque Spring takes care of it. It creates beans and injects them as required.

2. Entonces, ¿por qué su proyecto produce la excepción?

  • Como @sperumal Dicho esto, la primavera puede producir una excepción circular si se utiliza la inyección de constructor
  • De acuerdo con el registro, se utiliza la primavera de Seguridad en su proyecto
  • En la configuración de la primavera de Seguridad, que hacen uso de constructor
  • inyección
  • Sus granos que inyecta el authenticationManager tenido la referencia circular

3. Entonces ¿por qué la ex ception desapareció místicamente?

La excepción puede o no producirse depende del orden de creación de beans. Creo que se hicieron varios archivos *context.xml más o menos, y cargarlos con algo de configuración, como a continuación en web.xml

<context-param> 
    <param-name>contextConfigLocation</param-name> 
    <param-value>classpath:*-context.xml</param-value> 
</context-param> 

Los archivos XML serán cargados por XmlWebApplicationContext clase y el orden de carga de archivos no están garantizados. Solo carga archivos desde el sistema de archivos. El problema está aquí. No hay problema si la clase carga primero el archivo de contexto de la aplicación, porque sus beans ya están creados cuando se utilizan para la inyección de Spring Security. Pero, si primero carga el archivo de contexto de Spring Security, se produce el problema de referencia circular, porque Spring intenta utilizar los beans en la inyección del constructor antes de que se hayan creado.

4. ¿Cómo resolver el problema?

Fuerza el orden de carga de los archivos xml. En mi caso, cargué el archivo xml del contexto de seguridad al final del archivo de contexto de la aplicación usando <import resource="">. El orden de carga se puede cambiar depende de los entornos, incluso con el mismo código, por lo que recomiendo configurar el orden para eliminar posibles problemas.

2

leer un poco sobre el asunto:

http://blog.jdevelop.eu/?p=382

Tales dependencias circulares no son frescos y deberán evitarse siempre que sea posible

26

Usted podría utilizar @Lazy para indicar que el grano se crea con pereza, rompiendo el ansioso ciclo de autoenvío.

La idea es que algunos bean en el ciclo se puedan instanciar como un proxy, y justo en el momento en que realmente se necesita se inicializará. Esto significa que todos los beans se inicializan excepto el que es un proxy. Usarlo por primera vez activará la configuración y, como los otros beans ya están configurados, no será un problema.

De una cuestión en la primavera-Jira:

anotación @Lazy que puede ser utilizado en conjunción con @Configuration para indicar que todos los granos dentro de esa clase de configuración deben ser perezosamente inicializado. Por supuesto, @Lazy también se puede usar junto con con métodos @Bean individuales para indicar la inicialización diferida en una base de uno por uno. https://jira.springsource.org/browse/SJC-263

Lo que significa que anotar el bean como @Lazy sería suficiente. O si lo prefiere simplemente anotar la clase de configuración como @Lazy de la siguiente manera:

@Configuration 
@Lazy 
public class Config { 
    @Bean 
    public ClassA classA() { 
     return new ClassA(); 
    } 

    @Bean 
    public ClassB classB() { 
     return new ClassB(); 
    } 
} 

Si implementa una interfaz de sus granos de esto va a funcionar bastante bien.

+1

Solo para agregar a esto, nos encontramos con un problema similar (con la inyección de SpringTemplateEngine). La solución de "inyección setter" NO ayudó, pero la anotación '@ Lazy' hizo el truco. Se siente como una curita sobre una herida de bala, pero por ahora me llevaré la victoria y me iré. Gracias señor Spaeth. – demaniak

9

De acuerdo con la documentación de Spring, es posible obtener el problema de dependencia Circular o BeanCurrentlyInCreationException utilizando constructor injection.

La solución para solucionar el problema es utilizar setters en lugar de la inyección de Constructor.

Referencia http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/html/beans.html.

+2

Creo que malinterpretaste lo que estaba escrito allí: si usas predominantemente la inyección de constructor, ** es posible ** crear un escenario de dependencia circular no resuelto. –

+1

La inyección del constructor es preferible a la inyección setter: su bean no existe hasta que se completó la invocación del constructor, mientras que con la inyección setter puede omitir algunos parámetros necesarios y Bean existe durante algún tiempo en estado no configurado correctamente –

Cuestiones relacionadas