Buenas tardes, Khabrovites!
En este art铆culo, propongo discutir uno de los problemas que a menudo se encuentran en los proyectos que usan el marco Spring. El problema descrito en este art铆culo surge debido a uno de los errores t铆picos en las configuraciones de resorte. No es necesario intentar cometer tal error en la configuraci贸n y, por lo tanto, este error es bastante com煤n.
Planteamiento del problema
El problema presentado en este art铆culo est谩 relacionado con la configuraci贸n incorrecta de beans en el contexto de aplicaci贸n actual, que se toman de otros contextos de aplicaci贸n. Tal problema puede surgir en una aplicaci贸n industrial grande, que consiste en muchos frascos, cada uno de los cuales tiene su propio contexto de aplicaci贸n que contiene frijoles de primavera.
Como resultado de una configuraci贸n incorrecta, obtenemos varias copias de beans con un estado impredecible, incluso si tienen el alcance singleton. Adem谩s, la copia irreflexiva de beans puede conducir al hecho de que se crear谩n m谩s de una docena de copias de todos los beans de cualquier jar en la aplicaci贸n, que est谩 plagada de problemas de rendimiento de la aplicaci贸n y un aumento en el tiempo de inicio de la aplicaci贸n.
Un ejemplo de uso de un bean desde un contexto de aplicaci贸n externa en el actual
Imagine que estamos desarrollando uno de los m贸dulos de aplicaci贸n, en el que hay muchos otros m贸dulos, y que cada uno de ellos tiene su propio contexto de aplicaci贸n. Dicha aplicaci贸n debe tener un m贸dulo en el que se creen instancias del contexto de aplicaci贸n de todos los m贸dulos de aplicaci贸n.

Supongamos que, en el contexto de la aplicaci贸n de uno de los m贸dulos externos, se crea una instancia del bean de la clase NumberGenerator, que queremos obtener en nuestro m贸dulo. Supongamos tambi茅n que la clase NumberGenerator se encuentra en el paquete org.example.kruchon.generators, que almacena algunas clases que generan valores.

Este bean tiene un estado: el campo int count.
package org.example.kruchon.calculators public class NumberGenerator { private int count = 0; public synchronized int next() { return count++; } }
Se crea una instancia de este bean en la subconfiguraci贸n GeneratorsConfiguration.
@Configuration public class GeneratorsConfiguration { @Bean public NumberGenerator numberGenerator() { return new NumberGenerator(); } ... }
Tambi茅n en el contexto de la aplicaci贸n externa, hay una configuraci贸n principal en la que se importan todas las subconfiguraciones del m贸dulo externo.
@Configuration @Import({GeneratorsConfiguration.class, ...}) public class ExternalContextConfiguration { ... }
Ahora dar茅 algunos ejemplos en los que el bean singleton de la clase NumberGenerator est谩 configurado incorrectamente en la configuraci贸n del contexto de la aplicaci贸n actual.
Configuraci贸n incorrecta 1. Importar la configuraci贸n principal del contexto de la aplicaci贸n externa
La peor decisi贸n que podr铆a ser.
@Configuration @Import(ExternalContextConfiguration.class) public class CurrentContextConfiguration { ... }
- La aplicaci贸n vuelve a crear todas las instancias de beans desde el contexto de la aplicaci贸n externa. En otras palabras, se crea una copia de todo el m贸dulo externo, lo que afecta el consumo de memoria, el rendimiento y el tiempo de inicio de la aplicaci贸n.
- Obtenga una copia de NumberGenerator en el contexto actual de la aplicaci贸n. Una copia de NumberGenerator tiene su propio valor para el campo de recuento, inconsistente con la primera instancia de NumberGenerator. Esta inconsistencia da lugar a errores dif铆ciles de depurar en la aplicaci贸n.
Configuraci贸n incorrecta 2. Subconfiguraci贸n de importaci贸n del contexto de aplicaci贸n externa
La segunda opci贸n es incorrecta y a menudo se encuentra en la pr谩ctica.
@Configuration @Import(GeneratorsConfiguration.class) public class CurrentContextConfiguration { ... }
En esta realizaci贸n, ya no se crea una copia completa del m贸dulo externo, sin embargo, nuevamente obtenemos el segundo bean de la clase NumberGenerator.
Configuraci贸n incorrecta 3. Busque la inyecci贸n directamente en el bean, donde queremos usar NumberGenerator
public class OrderFactory { private final NumberGenerator numberGenerator; public OrderFactory() { ApplicationContext externalApplicationContext = getExternalContext(); numberGenerator = externalApplicationContext.getBean(NumberGenerator.class); } public Order create() { Order order = new Order(); int id = numberGenerator.next(); order.setId(id); order.setCreatedDate(new Date()); return order; } private ApplicationContext getExternalContext(){ ... } }
En este m茅todo, la duplicaci贸n de un bean que tiene el alcance singleton puede considerarse resuelta. De hecho, ahora reutilizamos el bean de otro contexto de aplicaci贸n y no lo recreamos.
Pero de esta manera:
- Complica la clase desarrollada y sus pruebas unitarias.
- Excluye la implementaci贸n autom谩tica del bean de la clase NumberGenerator en los beans del m贸dulo actual.
- No es habitual usar lookUp para inyectar un bean singleton en casos generales.
Por lo tanto, dicha soluci贸n es m谩s una soluci贸n torpe que una soluci贸n racional a un problema.
Considere c贸mo configurar correctamente un bean desde un contexto de aplicaci贸n externa.
Soluci贸n 1. Obtenga el bean desde el contexto de la aplicaci贸n externa en la configuraci贸n
Este m茅todo es muy similar al 3er ejemplo de una configuraci贸n incorrecta con una diferencia: obtenemos un bean haciendo una b煤squeda desde el contexto externo en la configuraci贸n, y no directamente en el bean.
@Configuration public class CurrentContextConfiguration { @Bean public NumberGenerator numberGenerator() { ApplicationContext externalApplicationContext = getExternalContext(); return externalApplicationContext.getBean(NumberGenerator.class); } private ApplicationContext getExternalContext(){ ... } }
Ahora podemos incrustar autom谩ticamente este bean en beans desde nuestro propio m贸dulo.
Soluci贸n 2. Haga que el contexto de la aplicaci贸n externa sea primario
Es probable que la funcionalidad del m贸dulo actual ampl铆e la funcionalidad del externo. Puede haber un caso cuando en uno de los m贸dulos externos se desarrollan beans auxiliares comunes a toda la aplicaci贸n, y en otros m贸dulos se usan estos beans. En este caso, es l贸gico indicar que el m贸dulo externo es padre del anterior. En este caso, todo el bean del m贸dulo principal se puede usar en el m贸dulo actual, y luego el
bean del m贸dulo principal no necesita configurarse en la configuraci贸n del contexto de la aplicaci贸n actual.
Es posible especificar una relaci贸n principal al crear una instancia de contexto utilizando el constructor con el par谩metro principal:
public AbstractApplicationContext(ApplicationContext parent) { ... }
O usa el setter:
public void setParent(ApplicationContext parent) { ... }
Si el contexto de la aplicaci贸n se declara en xml, podemos usar el constructor:
public ClassPathXmlApplicationContext(String[] configLocations, ApplicationContext parent) throws BeansException { ... }
Conclusi贸n
Por lo tanto, tenga cuidado al configurar beans de primavera, siga las recomendaciones dadas en el art铆culo e intente no copiar los beans que tienen alcance singleton. Estar茅 encantado de responder a sus preguntas!