El código fuente de la configuración XML analizada de Wanzi asignada a BeanDefinition

Este artículo es compartido por Huawei Cloud Community " Spring Master's Road 16: análisis de la asignación de configuración XML en el código fuente de BeanDefinition ", autor: Zhuan Yeyang__.

1. Análisis de la etapa BeanDefinition

SpringIOCLos pasos específicos de la fase del contenedor de inversión de control () en el marco BeanDefinitionimplican principalmente Beandefinición, carga, análisis y posterior inyección programática y posprocesamiento. Esta fase es una de las primeras etapas del ciclo de vida Springdel marco y es fundamental para comprender todo el marco.BeanSpring

  • Cargar archivos de configuración y clases de configuración.

En este paso, Springel contenedor utiliza archivos de configuración o clases de configuración para comprender qué se debe administrar Bean. Para XMLla configuración basada en , normalmente utilice ClassPathXmlApplicationContexto FileSystemXmlApplicationContext.

  • Analizar archivos de configuración y clases de configuración y encapsularlos como BeanDefinition

SpringEl marco analiza los archivos de configuración utilizando BeanDefinitionReaderinstancias como XmlBeanDefinitionReaderDespués del análisis, cada Beanconfiguración se encapsulará en un BeanDefinitionobjeto, que contiene el nombre de la clase, el alcance, la devolución de llamada del ciclo de vida y otra información.

  • Inyectar BeanDefinitions adicionales mediante programación

Además de lo que se define en el archivo de configuración , también se puede agregar dinámicamente al contenedor Beanmediante programación , lo que aumenta la flexibilidad.BeanDefinitionIOC

  • Postprocesamiento de BeanDefinition

BeanDefinitionEl posprocesamiento significa que el contenedor permite el uso BeanDefinitionRegistryPostProcessoro el procesamiento adicional BeanFactoryPostProcessorde los datos analizados , como atributos modificados, etc.BeanDefinitionBean

2. Cargue el archivo de configuración xml

2.1 Ejemplo de código para cargar beans en un archivo de configuración XML

Primero dé el ejemplo de código más simple y luego analícelo paso a paso.

El código completo es el siguiente:

paquete com.ejemplo.demo.bean; 

// HolaMundo.java 
clase pública HolaMundo { 
    mensaje de cadena privada; 

    setMessage público vacío (mensaje de cadena) { 
        this.message = mensaje; 
    } 

    public void decirHola() { 
        System.out.println("Hola, " + mensaje + "!"); 
    } 
}

Programa principal:

paquete com.example.demo; 

importar com.example.demo.bean.HelloWorld; 
importar org.springframework.context.support.ClassPathXmlApplicationContext; 

clase pública DemoApplication { 

    public static void main(String[] args) { 
        // Crear contexto Spring ( Container) 
        ClassPathXmlApplicationContext context = 
                new ClassPathXmlApplicationContext("ApplicationContext.xml"); 

        // Obtenemos el bean del contenedor, asumiendo que tenemos un bean llamado 'helloWorld' 
        HelloWorld helloWorld = context.getBean("helloWorld", HelloWorld.class); 

        / / Usar bean 
        helloWorld.sayHello(); 

        // Cerrar contexto 
        context.close(); 
    } 
}

archivo xml

<?xml versión="1.0" codificación="UTF-8"?> 
<beans xmlns="http://www.springframework.org/schema/beans" 
       xmlns:xsi="http://www.w3.org /2001/XMLSchema-instance" 
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
       http://www.springframework.org/schema/beans/spring-beans.xsd"> 

    <!-- Bean定义 --> 
    <bean id="helloWorld" class="com.example.demo.bean.HelloWorld"> 
        <!-- 设置属性 --> 
        <property name="message" value="World"/> 
    < /frijol> 

</frijol>

resultado de la operación:

Entonces comenzaremos a analizar este código.

2.2 setConfigLocations: establecer y guardar rutas de archivos de configuración

Tomemos Spring 5.3.7el código fuente como ejemplo para analizar.

//Crear contexto Spring (contenedor) 
        ClassPathXmlApplicationContext contexto = 
                new ClassPathXmlApplicationContext("ApplicationContext.xml");

ideaHicimos clic para analizar este código y finalmente vimos la ruta al archivo de configuración ClassPathXmlApplicationContextque se llama en el método de sobrecarga .setConfigLocations

veamos setConfigLocationscomo

setConfigLocations() La función principal del método es establecer la ruta del archivo de configuración que debe leerse cuando el contenedor carga la definición. Estas rutas pueden ser recursos en el classpath, recursos en el sistema de archivos o cualquier otro recurso ubicado. Este método garantiza que todas las rutas de configuración proporcionadas se guarden y se utilicen en operaciones posteriores de actualización del contenedor. Spring  Bean URL

El código fuente se muestra para su análisis:

public void setConfigLocations(@Nullable String... ubicaciones) { 
    if (ubicaciones! = nulo) { 
        // Utilice la clase Assert de Spring para verificar que no haya elementos nulos en la matriz de ubicaciones de configuración entrante. 
        Assert.noNullElements(locations, "Las ubicaciones de configuración no deben ser nulas"); 
        
        // Inicializa la matriz que almacena internamente las ubicaciones de configuración según la cantidad de ubicaciones de configuración pasadas. 
        this.configLocations = new String[locations.length]; 

        // Recorre la matriz de ubicación de configuración pasada. 
        for(int i = 0; i < ubicaciones.length; ++i) { 
            // Llame al método resolvePath para procesar cada ubicación de configuración (posiblemente realizando la resolución de ruta necesaria, como analizar marcadores de posición). 
            // trim() se utiliza para eliminar espacios al principio y al final de la cadena para garantizar que la ruta guardada esté purificada. 
            this.configLocations[i] = this.resolvePath(locations[i]).trim(); } 
        } 
    else { 
        // Si la ubicación de configuración entrante es nula, borre todas las ubicaciones de configuración establecidas. 
        this.configLocations = nulo; 
    } 
}

Cuando se actualiza el contexto, se leerán estas ubicaciones de archivos de configuración y Springel contenedor resolverá las definiciones allí beansy las registrará en el contenedor. setConfigLocations() El método simplemente establece estas ubicaciones y el proceso de carga y registro real se realiza cuando se actualiza el contexto.

setConfigLocationsPor lo general, el usuario no llama a este método directamente, sino ApplicationContextque lo llama el marco durante el proceso de inicialización. Por ejemplo, en la XMLconfiguración basada, proporcionaremos la ruta al archivo de configuración durante la inicialización ClassPathXmlApplicationContexto .FileSystemXmlApplicationContext

En debugeste momento, puede ver que la ruta al archivo de configuración establecida en el código de prueba está guardada. xml 

2.3 actualización: activa la actualización del contenedor, la carga y el análisis de archivos de configuración

Vimos arriba ClassPathXmlApplicationContextque en el método, setConfigLocationsdespués , hay un refreshmétodo, echemos un vistazo.

En Springel marco, refresh()los métodos son muy críticos, son ApplicationContextparte de la interfaz. Las funciones principales de este método son actualizar el contexto de la aplicación, cargar o recargar los definidos en el archivo de configuración Bean, inicializar todos los singleton, configurar recursos de mensajes, publicadores de eventos, etc.

El código se muestra para su análisis:

public void refresco() throws BeansException, IllegalStateException { 
    // Bloque sincronizado para garantizar la seguridad de los subprocesos del proceso de actualización del contenedor 
    sincronizado(this.startupShutdownMonitor) { 
        // Iniciar el registro de pasos de actualización del contexto, utilizado para monitoreo y diagnóstico 
        StartupStep contextRefresh = this.applicationStartup. start("spring.context.refresh"); 
        
        // Prepara el proceso de actualización, establece la hora de inicio, el indicador de estado, etc. 
        this.prepareRefresh(); 
        
        // Obtenga una nueva BeanFactory y cree una BeanFactory si es la primera actualizar 
        ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory(); 
        
        // Configurar BeanFactory, registrar interfaces de dependencia ignoradas, etc. 
        this.prepareBeanFactory(beanFactory); 

        try { 
            // Permitir que el postprocesador de BeanFactory lo modifique 
            this.postProcessBeanFactory(beanFactory); 
            
            // Inicie el monitoreo de Bean de los pasos de posprocesamiento de fábrica 
            StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process"); 
            
            // Invoque BeanFactoryPostProcessors 
            this.invokeBeanFactoryPostProcessors(beanFactory); 
            
            // Registre BeanPostProcessors en BeanFactory 
            this. RegisterBeanPostProcessors (beanFactory); 
            
            // El paso de posprocesamiento del Bean finaliza 
            beanPostProcess.end(); 
            
            // Inicializa el componente MessageSource para la internacionalización y otras funciones 
            this.initMessageSource(); 
            
            // Inicializa el transmisor de eventos 
            this.initApplicationEventMulticaster(); 
            
            // Los métodos personalizados quedan para ser anulados por subclases 
            this.onRefresh(); 
            
            // Registra oyentes 
            this.registerListeners(); 
            
            // Inicializa los beans singleton restantes 
            this.finishBeanFactoryInitialization(beanFactory); 
            
            // Completa el proceso de actualización y notifica el procesamiento del ciclo de vida LifecycleProcessor El proceso de actualización libera el evento ContextRefreshedEvent 
            this.finishRefresh(); 
        } catch (BeansException var10) { 
            // Captura BeansException, registra información de advertencia y destruye el Bean creado 
            if (this.logger.isWarnEnabled()) { 
                this.logger.warn( "Excepción encontrada durante la inicialización del contexto - cancelación del intento de actualización: " + var10); 
            } 

            // Destruye el Bean singleton inicializado 
            this.destroyBeans(); 
            
            // Cancela la actualización y restablece el indicador en el monitor de sincronización 
            this. cancelRefresh(var10); 
            
            / / Lanzar una excepción y finalizar el proceso de actualización 
            throw var10; 
        } finalmente { 
            // Al final de la actualización, restablece el caché compartido en el kernel Spring 
            this.resetCommonCaches(); 
            
            // Finaliza el registro del paso de actualización de contexto 
            contextRefresh. fin(); 
        } 
    } 
}

Este método realiza una secuencia precisa de pasos para configurar ApplicationContext, incluida Beanla carga, el registro y la inicialización. El proceso de actualización incluye una serie de pasos complejos, como Beanla carga de definiciones, el registro y la inicialización.Bean

En Springlos marcos modernos, ApplicationContextgeneralmente se actualiza una vez cuando se inicia el contenedor. Una vez que se inicia el contenedor y se actualiza el contexto, todo Beanse carga y crea. Aunque técnicamente es posible llamar refresh()a un método varias veces, esto no es común en la práctica, ya que significaría restablecer el estado del contexto de la aplicación y comenzar de nuevo. Al hacerlo, se destruirán todos los singleton Beany se reinicializarán, lo cual no es aconsejable en la mayoría de las aplicaciones. No solo es costoso sino que también puede provocar problemas como pérdida de estado e inconsistencia de datos.

Para basados ​​en xml( ApplicationContextcomo ClassPathXmlApplicationContext), refresh()el archivo de configuración se vuelve a leer y analizar cuando se llama al método, y se vuelven a crear las definiciones de BeanFactoryy Bean. Si el contenedor se ha actualizado, todos los singleton deben destruirse Bean, cerrarse BeanFactoryy luego volverse a crear. Por lo general, esta característica se utiliza durante el desarrollo o las pruebas y no se recomienda su uso en entornos de producción debido a su alta sobrecarga y riesgos.

Echemos un vistazo a los puntos clave: ¿Dónde está la operación de cargar el archivo de configuración? Lo marqué en la imagen aquí y obtainFreshBeanFactoryhay un método en el método refreshBeanFactory.

refreshBeanFactoryEl método es un método abstracto, echemos un vistazo a cómo se implementa la clase de implementación y busquemos refreshBeanFactoryel método para implementar la clase según la relación de herencia.

refreshBeanFactory()Los métodos refresh()generalmente se llaman dentro de los métodos. Este método garantiza que el actual ApplicationContextcontenga un estado limpio BeanFactory.

El código se muestra para su análisis:

protected final void refrescoBeanFactory() throws BeansException { 
    // Comprobar si el contexto de la aplicación actual ya contiene un BeanFactory 
    if (this.hasBeanFactory()) { 
        // Si ya existe un BeanFactory, destruye todos los beans que administra 
        this.destroyBeans(); 
        / / Cerrar BeanFactory existente y liberar cualquier recurso que pueda contener 
        this.closeBeanFactory(); 
    } 

    try { 
        // Crear una nueva instancia de DefaultListableBeanFactory, que es la implementación predeterminada de la interfaz ConfigurableListableBeanFactory en Spring DefaultListableBeanFactory 
        beanFactory = this.createBeanFactory(); 
        // Establecer un ID de serialización para beanFactory, que luego se puede usar para deserializar 
        beanFactory.setSerializationId(this.getId()); 
        // Permitir que las subclases personalicen el beanFactory recién creado 
        this.customizeBeanFactory(beanFactory); 
        // Cargar definiciones de beans desde recursos subyacentes (como archivos XML) a beanFactory 
        this.loadBeanDefinitions(beanFactory); 
        // Asigna el nuevo beanFactory a la propiedad beanFactory de este contexto 
        this.beanFactory = beanFactory; 
    } catch (IOException var2) { 
        // If An I/ O se produce una excepción durante el análisis del recurso de definición del bean, envuélvalo y vuelva a lanzarlo como ApplicationContextException. throw 
        new ApplicationContextException("Error de E/S al analizar la fuente de definición del bean para " + this.getDisplayName() + "", var2); 
    } 
}

Este método AbstractApplicationContextse anula en la implementación específica. Proporciona beanuna plantilla para actualizar la fábrica: si ya existe una, destrúyala y ciérrela; luego cree una nueva beanfábrica, personalícela y complete beanlas definiciones. Si se encuentra una excepción al cargar beanuna definición (por ejemplo, al leer XMLun archivo), I/Ose genera una ApplicationContextException, que proporciona más información contextual sobre la naturaleza del error.

Podemos ver en este código que existen loadBeanDefinitionsmétodos para cargar definiciones desde recursos subyacentes (como XMLarchivos) . La lógica es muy complicada, analicémosla por separado a continuación.beanbeanFactory

2.4 loadBeanDefinitions: lógica de carga específica de BeanDefinition

this.loadBeanDefinitions Los métodos se implementan en subclases. Este patrón es un ejemplo típico de patrón de diseño de método de plantilla . En el patrón de diseño del método de plantilla, el marco de un algoritmo (es decir, una serie de pasos) se define en el método de la clase principal, pero la implementación específica de algunos pasos se retrasará hasta la subclase. AbstractApplicationContext 

AbstractApplicationContext Proporciona un marco de métodos que definen los pasos de actualización, pero deja su implementación específica a las subclases. Las subclases deben implementar este método en función de tipos de recursos de almacenamiento específicos (como archivos, anotaciones, scripts, etc.). refreshBeanFactory  BeanFactory  loadBeanDefinitions  XML Java Groovy 

AbstractXmlApplicationContextLos métodos implementados por las subclases loadBeanDefinitions son los siguientes:

cke_134.png

loadBeanDefinitions()Los métodos son los métodos centrales Springen el marco para cargar, analizar y registrar Beandefiniciones. Su responsabilidad básica es leer información de configuración de una o más fuentes y luego convertir esta información en definiciones Springque el contenedor pueda administrar . BeanEste método generalmente Springse llama durante la inicialización del contexto y es un paso clave en la definición Springde carga del contenedor Bean.

El código se muestra para su análisis:

// Utilice DefaultListableBeanFactory como fábrica de destino para el registro de definiciones de Bean, cargue la definición de Bean 
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { 
    // Cree un lector que lea definiciones de Bean XML y pase la fábrica para las definiciones de registro 
    XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); 
    // Establece el objeto de entorno, que puede incluir la configuración del entorno relacionada con la resolución de atributos 
    beanDefinitionReader.setEnvironment(this.getEnvironment()); 
    // Configura el cargador de recursos para permitir al lector cargar recursos XML 
    beanDefinitionReader. setResourceLoader (this); 
    // Establece el analizador de entidades para analizar entidades en XML como DTD 
    beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); 
    // Inicializa el lector de definiciones de Bean y puede establecer algunos parámetros, como si se valida XML 
    this. initBeanDefinitionReader(beanDefinitionReader); 
    // Llama al loadBeanDefinitions sobrecargado y carga la definición de Bean de acuerdo con los recursos configurados y la ubicación 
    this.loadBeanDefinitions(beanDefinitionReader); 
} 

// Inicializa el lector de definiciones de Bean, configurando principalmente si se debe realizar la verificación XML 
protected void initBeanDefinitionReader ( Lector XmlBeanDefinitionReader) { 
    // Establecer el modo de validación XML, generalmente depende de la configuración del contexto de la aplicación 
    lector.setValidating(this.validating); 
} 

// Cargar definiciones de Bean a través de XmlBeanDefinitionReader 
protected void loadBeanDefinitions(lector XmlBeanDefinitionReader) throws BeansException, IOException { 
    // Obtener una matriz de todos los recursos de configuración (como archivos de configuración XML) 
    Resource[] configResources = this.getConfigResources(); 
    // Si los recursos de configuración no están vacíos, cargue estos recursos 
    if (configResources != null) { 
        lector.loadBeanDefinitions(configResources ); 
    } 

    // Obtener la matriz de todas las ubicaciones de los archivos de configuración 
    String[] configLocations = this.getConfigLocations(); 
    // Si las ubicaciones de los archivos de configuración no están vacías, cargue los archivos de configuración especificados en estas ubicaciones 
    if (configLocations != null) { 
        lector.loadBeanDefinitions( configLocations); 
    } 
}

En loadBeanDefinitions(DefaultListableBeanFactory beanFactory)el método, primero se crea una instancia XmlBeanDefinitionReader. Este lector se utiliza especialmente para analizar XMLel archivo de configuración y Beancargar la definición en DefaultListableBeanFactoryél. beanDefinitionReaderSe establecen propiedades relacionadas, incluidas variables de entorno, cargadores de recursos y solucionadores de entidades. Esta configuración garantiza beanDefinitionReaderque los archivos se analicen correctamente XMLy que se resuelvan los marcadores de posición y los recursos externos de los archivos.

Luego, al llamar initBeanDefinitionReaderal método, puede XmlBeanDefinitionReaderrealizar alguna configuración adicional en la instancia, como configurar XMLla validación. Finalmente, loadBeanDefinitions(XmlBeanDefinitionReader reader)se llama al método para realizar realmente la operación de carga. Este método llamará al lector para que lea y analice XMLel archivo, Beancargando la definición en Springel contenedor.

En el método, primero intente obtener los recursos del archivo de configuración loadBeanDefinitions(XmlBeanDefinitionReader reader)del método , si dichos recursos existen, luego cargue estas definiciones. En segundo lugar, intenta obtener información de ubicación del archivo de configuración y, si está presente, carga los archivos de configuración especificados en esas ubicaciones. Este diseño permite cargar la configuración desde diferentes fuentes, como directamente desde un archivo de recursos o desde una ruta de archivo específica.getConfigResourcesXMLreaderreader

La depuración puede ver el estado detallado del lector y las ubicaciones de configuración.

cke_135.png

Aquí vemos otro lector.loadBeanDefinitions(configLocations); ¿Qué está haciendo esto? ¡Míralo a continuación!

2.5 loadBeanDefinitions: implementado por XmlBeanDefinitionReader

Al depurar, puede ver que el lector aquí es XmlBeanDefinitionReader. Haga clic para rastrear el método Reader.loadBeanDefinitions (configLocations);. El método llamado está en AbstractBeanDefinitionReader, y XmlBeanDefinitionReader hereda de AbstractBeanDefinitionReader.

cke_136.png

Aquí el archivo de configuración se carga cíclicamente y hay un recuento += this.loadBeanDefinitions (ubicación); ¡Continúe rastreando!

cke_137.png

La acción lógica de este código es aproximadamente la siguiente:

  1. Según la cadena de ubicación del recurso entrante, el recurso correspondiente se obtiene a través del cargador de recursos (ResourceLoader).
  2. Si el cargador de recursos es un solucionador de patrones de recursos (ResourcePatternResolver), procesará el patrón en la ruta (como los caracteres comodín) y cargará todos los recursos coincidentes.
  3. Lea el recurso, analice y registre todas las definiciones de beans definidas en él.
  4. Si se proporciona una colección de recursos reales (actualResources), los recursos analizados se agregarán a esta colección.
  5. Devuelve el número de definiciones de beans cargados y registrados.

Centrémonos en los puntos clave y sigamos rastreando las loadBeanDefinitions en su interior.

cke_138.png

El código se muestra para su análisis:

public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {

// 将Resource包装为EncodedResource,允许指定编码,然后继续加载Bean定义

return this.loadBeanDefinitions(new EncodedResource(resource));

}

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {

// 断言传入的EncodedResource不为空

Assert.notNull(encodedResource, "EncodedResource must not be null");

// 如果日志级别为trace,则输出跟踪日志

if (this.logger.isTraceEnabled()) {

this.logger.trace("Loading XML bean definitions from " + encodedResource);

}

// 获取当前线程正在加载的资源集合

Set<EncodedResource> currentResources = (Set)this.resourcesCurrentlyBeingLoaded.get();

// 检查资源是否已经在加载中,如果是,则抛出BeanDefinitionStoreException异常,避免循环加载

if (!currentResources.add(encodedResource)) {

throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!");

} else {

int var6; // 这将用来存储加载的Bean定义数量

try {

// 打开资源的InputStream进行读取

InputStream inputStream = encodedResource.getResource().getInputStream();

Throwable var4 = null;

try {

// 将InputStream封装为InputSource,XML解析器可以接受这个类型

InputSource inputSource = new InputSource(inputStream);

// 如果资源编码不为空,设置资源的编码

if (encodedResource.getEncoding() != null) {

inputSource.setEncoding(encodedResource.getEncoding());

}

// 实际加载Bean定义的方法,返回加载的Bean定义数量

var6 = this.doLoadBeanDefinitions(inputSource, encodedResource.getResource());

} catch (Throwable var24) {

// 捕获Throwable以便在finally块中处理资源释放

var4 = var24;

throw var24;

} finally {

// 关闭InputStream资源

if (inputStream != null) {

if (var4 != null) {

try {

inputStream.close();

} catch (Throwable var23) {

// 添加被抑制的异常

var4.addSuppressed(var23);

}

} else {

inputStream.close();

}

}

}

} catch (IOException var26) {

// 抛出IOException异常,如果解析XML文档失败

throw new BeanDefinitionStoreException("IOException parsing XML document from " + encodedResource.getResource(), var26);

} finally {

// 从当前加载的资源集合中移除该资源

currentResources.remove(encodedResource);

// 如果当前加载的资源集合为空,则从ThreadLocal中移除

if (currentResources.isEmpty()) {

this.resourcesCurrentlyBeingLoaded.remove();

}

}

// 返回加载的Bean定义数量

return var6;

}

}

En este código, loadBeanDefinitions primero convierte el recurso en un EncodedResource, lo que le permite conservar información sobre la codificación del recurso. Luego intenta cargar el recurso como InputStream y convertirlo en InputSource, que es necesario para el análisis XML. Luego llama al método doLoadBeanDefinitions, que en realidad es responsable de analizar el XML y registrar la definición del bean.

En este proceso, el código garantiza que el mismo recurso no se cargue en un bucle y, si se produce una excepción mientras se carga el recurso, el recurso se limpia adecuadamente y se informa el error. El número de definiciones de beans cargados se devuelve al finalizar.

Centrémonos en los pasos clave de este código: ¡método doLoadBeanDefinitions!

2.6 doLoadBeanDefinitions: leer y analizar el contenido del archivo de configuración XML

cke_139.png

¿Qué hace el método doLoadBeanDefinitions?

Los pasos específicos son los siguientes:

  1. Utilice el método doLoadDocument para analizar el InputSource dado en un objeto de documento DOM. Este objeto Documento representa la estructura del archivo XML.
  2. Al llamar al método RegisterBeanDefinitions, registre las definiciones de Bean en el documento analizado en la fábrica de Bean de Spring. Este método devuelve el número de definiciones de beans registradas.
  3. Si el nivel de registro se establece en DEBUG, se registra el número de definiciones de beans cargadas.

El enfoque aquí es el método RegisterBeanDefinitions, continúe siguiendo el código

cke_140.png

Continúe analizando los puntos clave y finalmente póngase al día con el método doRegisterBeanDefinitions.

cke_141.png

El método doRegisterBeanDefinitions (Element root) es un método en el marco Spring que se utiliza para analizar definiciones de beans en archivos de configuración XML y registrarlas en el contenedor Spring. Este método generalmente se llama después de leer el archivo XML y convertirlo en un árbol DOM (modelo de objetos de documento). En este momento, el elemento raíz del archivo XML se pasa a este método a través del parámetro raíz.

El código se muestra para su análisis:

protected void doRegisterBeanDefinitions(Element root) {

// 保存旧的解析代理(delegate),以便之后可以恢复

BeanDefinitionParserDelegate parent = this.delegate;

// 创建新的解析代理(delegate),用于处理当前XML根节点的解析

this.delegate = this.createDelegate(this.getReaderContext(), root, parent);

// 如果当前节点使用的是Spring默认的XML命名空间

if (this.delegate.isDefaultNamespace(root)) {

// 获取根节点的"profile"属性

String profileSpec = root.getAttribute("profile");

// 检查"profile"属性是否有文本内容

if (StringUtils.hasText(profileSpec)) {

// 按逗号、分号和空格分隔"profile"属性值,得到指定的profiles数组

String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, ",; ");

// 如果当前环境不接受任何指定的profiles,则不加载该Bean定义文件

if (!this.getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {

// 如果日志级别是DEBUG,则记录跳过文件的信息

if (this.logger.isDebugEnabled()) {

this.logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + this.getReaderContext().getResource());

}

// 退出方法,不进行后续处理

return;

}

}

}

// 在解析XML前进行预处理,可被重写的方法

this.preProcessXml(root);

// 解析XML根节点下的Bean定义

this.parseBeanDefinitions(root, this.delegate);

// 在解析XML后进行后处理,可被重写的方法

this.postProcessXml(root);

// 恢复旧的解析代理(delegate)

this.delegate = parent;

}

El fragmento de código anterior es un método interno utilizado por el marco Spring para registrar una definición de bean. Este método se llama al analizar el archivo de configuración XML y registrar la definición del bean en el contenedor Spring. Contiene lógica para procesar atributos de perfil para decidir si se carga una definición de bean específica según el entorno de ejecución, así como enlaces de procesamiento previo y posterior que permiten operaciones personalizadas antes y después del análisis. Finalmente, garantiza que el delegado de resolución se restablezca a su estado anterior para mantener el estado correcto.

A continuación, tenemos que ver cómo analizar xml, centrándonos en el método parseBeanDefinitions.

2.7 parseBeanDefinitions - Analizar elementos BeanDefinition en XML

cke_142.png

El objetivo principal del método parseBeanDefinitions (Element root, BeanDefinitionParserDelegate delegado) es atravesar el nodo raíz del archivo de configuración XML, analizar y registrar todos los beans definidos en él. Este método es responsable de distinguir diferentes tipos de elementos, es decir, elementos estándar en el espacio de nombres predeterminado y elementos personalizados en el espacio de nombres personalizado, y procesarlos en consecuencia.

El código se muestra para su análisis:

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {

// 判断根节点是否使用的是Spring的默认命名空间

if (delegate.isDefaultNamespace(root)) {

// 获取所有子节点

NodeList nl = root.getChildNodes();



// 遍历所有子节点

for (int i = 0; i < nl.getLength(); ++i) {

Node node = nl.item(i);

// 只处理Element类型的节点(过滤掉文本节点等其他类型)

if (node instanceof Element) {

Element ele = (Element)node;

// 如果子元素节点也是默认命名空间,则调用parseDefaultElement方法解析

if (delegate.isDefaultNamespace(ele)) {

this.parseDefaultElement(ele, delegate);

} else {

// 如果子元素节点不是默认命名空间,则调用parseCustomElement方法解析

// 这通常表示节点定义了自定义的行为,可能是用户自定义的标签或者是Spring扩展的标签

delegate.parseCustomElement(ele);

}

}

}

} else {

// 如果根节点不是默认命名空间,那么它可能是一个自定义标签的顶级元素

// 在这种情况下,直接调用parseCustomElement进行解析

delegate.parseCustomElement(root);

}

}

El propósito de este código es analizar los beans definidos en el archivo XML. Examina cada elemento XML (incluidos los elementos raíz y los elementos secundarios) y llama a diferentes métodos de procesamiento en función de si estos elementos pertenecen al espacio de nombres predeterminado de Spring (generalmente " http://www.springframework.org/schema/beans "). Si el elemento pertenece al espacio de nombres predeterminado, llamará a parseDefaultElement para analizar elementos de configuración estándar de Spring como <bean>. Si el elemento no pertenece al espacio de nombres predeterminado, se considerará un elemento personalizado y se llamará a parseCustomElement para analizarlo. Los elementos personalizados suelen ser definidos por los desarrolladores o proporcionados por extensiones Spring para aumentar la funcionalidad del marco.

Aquí puede ver que es un nodo Elemento de procesamiento de bucle. La acción de análisis es principalmente el método parseDefaultElement. Sigamos echándole un vistazo.

cke_143.png

El método parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegado) es el método utilizado por el marco Spring para analizar los elementos del espacio de nombres predeterminado (es decir, el espacio de nombres Spring sin prefijo) en el archivo de configuración XML. Este método maneja específicamente las etiquetas <import>, <alias>, <bean> y <beans>.

 ¿A qué elementos se refiere el "espacio de nombres Spring sin prefijo" ? Pertenecen al espacio de nombres predeterminado de Spring, pero no es necesario especificar un prefijo de espacio de nombres al usarlos. Como <bean>, <property> o <constructor-arg>, estos elementos no tienen prefijo y pertenecen al espacio de nombres del esquema XML definido por Spring. El espacio de nombres predeterminado generalmente se declara en la parte superior del archivo XML a través del atributo xmlns. .

El código se muestra para su análisis:

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {

// 判断当前元素节点名称是否是"import"

if (delegate.nodeNameEquals(ele, "import")) {

// 如果是"import",则导入其他配置文件

this.importBeanDefinitionResource(ele);

} else if (delegate.nodeNameEquals(ele, "alias")) {

// 如果节点是"alias",则处理别名定义,为一个bean定义一个或多个别名

this.processAliasRegistration(ele);

} else if (delegate.nodeNameEquals(ele, "bean")) {

// 如果节点是"bean",则处理bean定义,这是定义Spring bean的核心元素

this.processBeanDefinition(ele, delegate);

} else if (delegate.nodeNameEquals(ele, "beans")) {

// 如果节点是"beans",意味着有嵌套的beans定义,需要递归地注册其中的bean定义

this.doRegisterBeanDefinitions(ele);

}

}

La función de este código es determinar diferentes operaciones de procesamiento para diferentes etiquetas en el archivo de configuración XML según el nombre del elemento. Maneja cuatro etiquetas principales bajo el espacio de nombres predeterminado del marco Spring:

  1. <importar> : importa otros archivos de configuración Spring XML al archivo de configuración actual.
  2. <alias> : proporciona uno o más alias para un bean ya definido.
  3. <bean> : define un bean administrado por Spring. Es el elemento más utilizado y contiene la configuración detallada del bean.
  4. <beans> : define una colección de beans, normalmente el elemento de nivel superior en el archivo de configuración, pero también puede ser una definición anidada, que representa un nuevo ámbito o contexto.

De esta manera, Spring puede construir una fábrica de beans en el contexto de la aplicación basándose en estos elementos.

Al depurar se puede encontrar que el xml se ha analizado del prototipo preliminar.

cke_144.png

Parece que el elemento bean no se ve aquí, ¿cómo analizar esto? Vayamos paso a paso y veamos cómo se llama el método ProcessBeanDefinition en el método parseDefaultElement mencionado anteriormente.

2.8 ProcessBeanDefinition: análisis y procesamiento específicos de etiquetas <bean>

cke_145.png

El método ProcessBeanDefinition es el método utilizado en el marco Spring para procesar el elemento de configuración XML <bean>. Su propósito es convertir la información descrita en el elemento <bean> en un objeto BeanDefinition utilizado internamente por Spring y registrarlo en el contenedor Spring IoC. Este es un paso crítico en el ciclo de vida de Spring Bean porque los beans definidos aquí serán instanciados y administrados cuando se inicie el contenedor.

El código se muestra para su análisis:

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {

// 使用代理解析bean定义元素,这涉及将XML定义的<bean>元素转换成Spring的BeanDefinitionHolder对象,

// 该对象包含了bean定义和名称。

BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);

// 检查解析是否返回了BeanDefinitionHolder对象。

if (bdHolder != null) {

// 如有需要,对bean定义进行装饰。这可能涉及应用任何额外的属性或嵌套元素,

// 这些都是bean定义的一部分,但不是标准<bean> XML配置的一部分。

bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);

try {

// 在注册中心注册bean定义。注册中心通常是持有所有bean定义的Spring IoC容器。

BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, this.getReaderContext().getRegistry());

} catch (BeanDefinitionStoreException var5) {

// 如果在bean注册过程中出现异常,报告错误上下文并抛出异常。

// 错误上下文包括bean的名称和引起问题的XML元素。

this.getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, var5);

}

// 在成功注册后,通知任何监听器一个新的bean定义已被注册。

// 这是Spring事件机制的一部分,允许对容器内的特定动作作出响应。

this.getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));

}

// 注意:如果bdHolder为空,则意味着bean定义元素没有被正确解析

// 或者它不是要被注册的(例如,在抽象定义的情况下)。

// 因此,在这种情况下,该方法不执行任何操作。

}

Este método se utiliza normalmente durante el proceso de análisis de definiciones de beans de Spring Framework y maneja la lógica de creación y registro de definiciones de beans basadas en los elementos XML proporcionados. BeanDefinitionParserDelegate es una clase auxiliar que maneja los detalles del análisis de estructuras Spring XML específicas.

Al depurar esta clase, descubrí que la clase y la identificación de este bean han sido analizadas.

cke_146.png

Algunas personas pueden sentir curiosidad, ¿cómo encapsular elementos xml en BeanDefinitionHolder?

cke_147.png

El método parseBeanDefinitionElement se utiliza para analizar la definición del elemento <bean> en el archivo de configuración de Spring y generar el objeto BeanDefinitionHolder correspondiente. BeanDefinitionHolder es una clase contenedora que encapsula una instancia de BeanDefinition y el nombre de la definición (es decir, la identificación del bean) y el alias (si corresponde).

El código se muestra para su análisis:

@Nullable

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {

// 调用重载方法parseBeanDefinitionElement,并将BeanDefinition设置为null

return this.parseBeanDefinitionElement(ele, (BeanDefinition)null);

}

@Nullable

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {

// 获取元素的id属性

String id = ele.getAttribute("id");

// 获取元素的name属性

String nameAttr = ele.getAttribute("name");

// 创建别名列表

List<String> aliases = new ArrayList();

if (StringUtils.hasLength(nameAttr)) {

// 如果name属性非空,则使用分隔符分割name字符串,并将结果添加到别名列表

String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, ",; ");

aliases.addAll(Arrays.asList(nameArr));

}

// 默认情况下bean的名称使用id属性的值

String beanName = id;

if (!StringUtils.hasText(id) && !aliases.isEmpty()) {

// 如果id为空且别名列表非空,则使用别名列表中的第一个作为bean名称,并从列表中移除它

beanName = aliases.remove(0);

if (this.logger.isTraceEnabled()) {

this.logger.trace("No XML 'id' specified - using '" + beanName + "' as bean name and " + aliases + " as aliases");

}

}

if (containingBean == null) {

// 如果不是嵌套bean定义,则检查bean名称和别名的唯一性

this.checkNameUniqueness(beanName, aliases, ele);

}

// 解析bean定义元素,返回AbstractBeanDefinition对象

AbstractBeanDefinition beanDefinition = this.parseBeanDefinitionElement(ele, beanName, containingBean);

if (beanDefinition != null) {

if (!StringUtils.hasText(beanName)) {

// 如果bean名称为空,则尝试生成bean名称

try {

if (containingBean != null) {

// 如果是内部bean,则使用特定的生成策略

beanName = BeanDefinitionReaderUtils.generateBeanName(beanDefinition, this.readerContext.getRegistry(), true);

} else {

// 否则使用默认策略

beanName = this.readerContext.generateBeanName(beanDefinition);

String beanClassName = beanDefinition.getBeanClassName();

if (beanClassName != null && beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() && !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {

// 如果bean类名不为空,且生成的bean名称以类名开头,且未被使用,则将类名添加到别名列表

aliases.add(beanClassName);

}

}

if (this.logger.isTraceEnabled()) {

this.logger.trace("Neither XML 'id' nor 'name' specified - using generated bean name [" + beanName + "]");

}

} catch (Exception var9) {

// 在名称生成过程中捕获异常,并记录错误

this.error(var9.getMessage(), ele);

return null;

}

}

// 将别名列表转换为数组

String[] aliasesArray = StringUtils.toStringArray(aliases);

// 创建并返回BeanDefinitionHolder对象

return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);

} else {

// 如果bean定义为空,则返回null

return null;

}

}

Este código es responsable de analizar el elemento <bean> en el XML, extraer los atributos de identificación y nombre y manejar posibles alias. Luego crea un AbstractBeanDefinition, que es una representación abstracta de la definición del bean en Spring. Si no se especifica ningún nombre de bean, intenta generar un nombre único y agrega un alias si es necesario. En última instancia, devuelve un BeanDefinitionHolder que contiene toda esta información. Si se encuentra algún problema durante el análisis, se registra un error y se devuelve un valor nulo.

En este código, se llamará a otro método sobrecargado, this.parseBeanDefinitionElement(ele, beanName, containsBean); este código contiene el método parseBeanDefinitionAttributes que encapsula otros atributos de <bean>. Echemos un vistazo.

cke_148.png

cke_149.png

El método parseBeanDefinitionAttributes se utiliza para analizar los atributos del elemento <bean> en el archivo de configuración de Spring y aplicar estos atributos al objeto AbstractBeanDefinition entrante. Este proceso consiste en establecer el alcance del bean, si se debe retrasar la inicialización, el modo de cableado automático, las dependencias, si debe ser un candidato para el cableado automático, si es el bean prioritario (primario), el método de inicialización, el método de destrucción, el método de fábrica y el bean de fábrica. nombre y otras propiedades. Los métodos manejan valores predeterminados para propiedades y manejan formatos heredados para algunas propiedades (como singleton).

Proponer análisis de código directamente:

public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName, @Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) {

// 检查是否使用了已废弃的singleton属性,如果存在,则报错提示应该升级到scope属性

if (ele.hasAttribute("singleton")) {

this.error("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele);

// 如果存在scope属性,则设置bean的作用域

} else if (ele.hasAttribute("scope")) {

bd.setScope(ele.getAttribute("scope"));

// 如果没有设置scope属性但是有包含bean,则设置为包含bean的作用域

} else if (containingBean != null) {

bd.setScope(containingBean.getScope());

}

// 如果设置了abstract属性,根据该属性的值设置bean定义是否为抽象

if (ele.hasAttribute("abstract")) {

bd.setAbstract("true".equals(ele.getAttribute("abstract")));

}

// 解析lazy-init属性,默认使用配置的默认值,如果设置了则覆盖

String lazyInit = ele.getAttribute("lazy-init");

if (this.isDefaultValue(lazyInit)) {

lazyInit = this.defaults.getLazyInit();

}

bd.setLazyInit("true".equals(lazyInit));

// 解析autowire属性,将字符串值转换为相应的自动装配模式

String autowire = ele.getAttribute("autowire");

bd.setAutowireMode(this.getAutowireMode(autowire));

// 解析depends-on属性,将字符串值转换为数组,并设置为bean定义的依赖

if (ele.hasAttribute("depends-on")) {

String dependsOn = ele.getAttribute("depends-on");

bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, ",; "));

}

// 解析autowire-candidate属性,设置bean是否可作为自动装配的候选者

String autowireCandidate = ele.getAttribute("autowire-candidate");

if (this.isDefaultValue(autowireCandidate)) {

String defaultValue = this.defaults.getAutowireCandidates();

if (defaultValue != null) {

String[] patterns = StringUtils.commaDelimitedListToStringArray(defaultValue);

bd.setAutowireCandidate(PatternMatchUtils.simpleMatch(patterns, beanName));

}

} else {

bd.setAutowireCandidate("true".equals(autowireCandidate));

}

// 解析primary属性,设置bean是否为primary

if (ele.hasAttribute("primary")) {

bd.setPrimary("true".equals(ele.getAttribute("primary")));

}

// 解析init-method属性,设置bean的初始化方法

String initMethodName = ele.getAttribute("init-method");

if (ele.hasAttribute("init-method")) {

bd.setInitMethodName(initMethodName);

// 如果没有设置但是有默认值,则使用默认值

} else if (this.defaults.getInitMethod() != null) {

bd.setInitMethodName(this.defaults.getInitMethod());

bd.setEnforceInitMethod(false);

}

// 解析destroy-method属性,设置bean的销毁方法

String destroyMethodName = ele.getAttribute("destroy-method");

if (ele.hasAttribute("destroy-method")) {

bd.setDestroyMethodName(destroyMethodName);

// 如果没有设置但是有默认值,则使用默认值

} else if (this.defaults.getDestroyMethod() != null) {

bd.setDestroyMethodName(this.defaults.getDestroyMethod());

bd.setEnforceDestroyMethod(false);

}

// 解析factory-method属性,设置bean的工厂方法

if (ele.hasAttribute("factory-method")) {

bd.setFactoryMethodName(ele.getAttribute("factory-method"));

}

// 解析factory-bean属性,设置bean的工厂bean名

if (ele.hasAttribute("factory-bean")) {

bd.setFactoryBeanName(ele.getAttribute("factory-bean"));

}

// 返回配置好的bean定义

return bd;

}

La función principal de este código es convertir las propiedades del archivo de configuración XML en propiedades del objeto BeanDefinition. Para cada propiedad, primero verifica si la propiedad existe y, si existe, lee su valor y lo establece en el objeto BeanDefinition. Si existe un valor predeterminado y no se proporciona ningún valor específico en el XML, se utiliza el valor predeterminado. De esta manera, el contenedor Spring puede crear y administrar beans basados ​​en archivos de configuración.

2.9 Resumen

El proceso completo desde la lectura del archivo de configuración XML hasta el registro de BeanDefinition:

cke_150.png

1. Cargue el archivo de configuración :

  • El paso "Crear contexto" en la figura corresponde a crear una instancia de ClassPathXmlApplicationContext, y la ruta del archivo XML se pasará en este momento.
  • ClassPathXmlApplicationContext acepta una o más rutas de archivos XML como parámetros de construcción.

2. Inicialice BeanFactory y actualice :

  • En la figura, el paso "ejecutar actualización" indica que se llama al método de actualización (), que inicia el proceso de inicialización y actualización del contenedor.
  • Inicialice BeanFactory en el método de actualización () y prepárese para analizar el archivo de configuración.

3. Lea el archivo de configuración XML :

  • El paso "Cargar definición de bean" en la figura representa la función de XmlBeanDefinitionReader, que es responsable de leer y cargar archivos de configuración XML.
  • XmlBeanDefinitionReader es responsable de leer el archivo de configuración XML entrante.

4. Analice el archivo XML :

  • El paso "Analizar XML" en la figura representa el proceso DefaultBeanDefinitionDocumentReader del archivo XML, que incluye el análisis de la etiqueta <beans> de nivel superior.
  • DefaultBeanDefinitionDocumentReader comienza a procesar archivos XML y analiza etiquetas de nivel superior como <beans>.
  • Para analizar elementos <bean>, primero verifique si el elemento está en el espacio de nombres predeterminado. Si es así, analice el elemento predeterminado; de lo contrario, los elementos fuera del espacio de nombres predeterminado se consideran elementos personalizados y son manejados por delegado.parseCustomElement(ele).

5.Análisis y registro de la definición de Bean :

  • Los pasos "Registrar definición de bean", "Alias ​​de proceso", "Bean de proceso" e "Importación de proceso" en la figura corresponden a varias actividades de análisis de BeanDefinitionParserDelegate, que implica analizar la identificación, el nombre, el alias, los atributos y los subelementos del bean. etc. Y registre los resultados del análisis en BeanDefinitionRegistry.
  • Utilice BeanDefinitionParserDelegate para analizar los detalles del elemento <bean>, incluido el ID, el nombre, el alias, etc.
  • Analice los atributos del elemento <bean>, como alcance, lazy-init, etc., y establezca estos valores en la instancia de BeanDefinition.
  • Si el elemento <bean> contiene subelementos (como <property> o <constructor-arg>), también se analizarán y agregarán a BeanDefinition en forma de metadatos correspondientes.
  • La BeanDefinition generada se registrará en BeanDefinitionRegistry, utilizando el método BeanDefinitionReaderUtils.registerBeanDefinition.
  • Si se produce algún error durante el proceso de análisis, la información del error se registrará mediante el método de error.

6. Lanzamiento del evento :

  • Después de registrar BeanDefinition, ApplicationContext publicará un evento de registro de componente para notificar a los oyentes relevantes. Este proceso permite que los componentes que implementan la interfaz ApplicationListener o anotan con @EventListener reciban este evento y respondan según sea necesario. Por ejemplo, puede utilizar este evento para activar alguna lógica personalizada, como comprobaciones de configuración adicionales, iniciar algunas operaciones de posprocesamiento, etc.

Este proceso detallado muestra el complejo proceso involucrado desde cargar el archivo de configuración hasta analizar y registrar BeanDefinition y muestra el mecanismo interno del marco Spring para manejar las declaraciones y dependencias de Bean. Esta es la base de la funcionalidad principal de inyección de dependencia de Spring, que garantiza que se puedan crear instancias de beans y administrarlos según lo definido.

3. Ejercicios de lectura del código fuente.

1. Análisis del archivo de configuración XML:

  • ¿Qué componentes utiliza el contenedor Spring al analizar los archivos de configuración de Spring?

  El contenedor Spring utiliza principalmente la clase XmlBeanDefinitionReader al analizar archivos de configuración. Además, BeanDefinitionDocumentReader también se utiliza para la lectura de documentos específicos.

  • ¿Qué papel juega BeanDefinitionReader en el análisis del archivo de configuración?

  BeanDefinitionReader es responsable de leer las definiciones de beans de archivos XML y convertirlos en objetos BeanDefinition internos de Spring.

  • ¿Cuándo se llama al método parseBeanDefinitionElement? ¿Cuál es su producción?

  Se llama a parseBeanDefinitionElement cuando se lee el elemento XML y su salida es un objeto BeanDefinitionHolder, que contiene la definición del bean, así como el nombre y el alias.

2. Análisis de definición de frijol:

  • Describe el proceso de definición de un bean, desde la lectura de elementos XML hasta la generación de un objeto BeanDefinition.

  Los objetos BeanDefinition se crean leyendo el elemento <bean> en XML y extrayendo las propiedades relevantes. Estas propiedades incluyen el nombre de clase del bean, el alcance, las devoluciones de llamadas del ciclo de vida, etc.

  • ¿Cuál es el papel del método parseBeanDefinitionAttributes en todo el proceso de análisis?

  El método parseBeanDefinitionAttributes se utiliza para extraer los atributos del elemento bean y establecerlos en el objeto AbstractBeanDefinition.

  • ¿Qué atributos XML procesa el método parseBeanDefinitionAttributes y cómo afectan al objeto BeanDefinition generado?

  Los atributos procesados ​​por el método parseBeanDefinitionAttributes incluyen alcance, lazy-init, autowire, etc. Estos atributos determinan el comportamiento del bean y cómo interactúa con otros beans.

3. Nombres y alias de beans:

  • ¿Cómo maneja Spring esto si la identificación o el nombre del bean no se proporciona en el elemento XML?

  Si no se proporciona ninguna identificación o nombre, Spring generará automáticamente un nombre de bean único. Puede basarse en el nombre de la clase más un determinado número de serie. Consejo: se mencionó al analizar el método parseBeanDefinitionElement.

  • ¿Para qué sirven los alias en Spring? ¿Cómo se manejan los alias en el método parseBeanDefinitionElement?

  Alias ​​​​puede proporcionar un nombre adicional para un bean, lo cual es útil cuando necesita hacer referencia al mismo bean pero usa diferentes nombres en diferentes contextos. En el método parseBeanDefinitionElement, los alias se manejan analizando el atributo de nombre y usando comas, punto y coma o espacios como delimitadores.

4. Alcance del bean y atributos del ciclo de vida:

  • ¿Cómo definir el alcance de un bean? ¿Cuál es la diferencia entre singleton y prototipo?

  Defina el alcance de un bean estableciendo el atributo de alcance del elemento <bean>. Singleton representa una instancia globalmente única, mientras que el prototipo representa una nueva instancia creada para cada solicitud.

  • ¿Qué impacto tienen estas propiedades lazy-init, init-method y destroy-method en el ciclo de vida del frijol?

  El atributo lazy-init determina si el bean debe inicializarse de forma diferida al inicio, y el método init y el método de destrucción definen los métodos que se llaman cuando el bean se inicializa y destruye.

5. Registro de frijoles:

  • Una vez creado el objeto BeanDefinition, ¿cómo lo registra Spring en el contenedor?

  Una vez analizado el objeto BeanDefinition, se registra en el contenedor Spring mediante el método DefaultListableBeanFactory.registerBeanDefinition.

  • Durante el proceso de registro, si se encuentra un conflicto de nombres de beans, ¿cómo lo manejará Spring?

  Si se encuentra un conflicto de nombres, se lanza una excepción BeanDefinitionStoreException. Si se definen beans con el mismo nombre en diferentes archivos de configuración, estos últimos normalmente anularán los primeros.

6. Manejo de excepciones:

  • ¿Cómo proporciona Spring comentarios al usuario cuando la configuración XML es incorrecta o se utilizan atributos ilegales?

  Spring notificará a los usuarios sobre errores de configuración lanzando BeanDefinitionStoreException. El mensaje de excepción detalla la causa y la ubicación del error.

  • Analice el mecanismo de manejo de errores en Spring ¿Cómo ayuda a los desarrolladores a depurar configuraciones?

  El mecanismo de manejo de errores de Spring incluye información detallada de excepciones y ubicación precisa, lo que es muy útil para que los desarrolladores identifiquen rápidamente errores de configuración.

4. Preguntas frecuentes

4.1 Durante el proceso de actualización, ¿cuál es el ciclo de vida de Bean? ¿Cómo se gestiona el estado de cada Bean?

1. Crear una instancia de BeanFactory :

  • Al comienzo del método de actualización, Spring creará una instancia de un nuevo BeanFactory, generalmente DefaultListableBeanFactory, como contenedor para crear instancias de Bean.

2. Cargue la definición de Bean :

  • Luego, la actualización llama a loadBeanDefinitions para cargar y registrar la definición del Bean. Estas definiciones pueden provenir de archivos de configuración XML, clases de configuración de Java o anotaciones escaneadas.

3.Ejecución de BeanFactoryPostProcessor :

  • Después de que se hayan cargado todas las definiciones de beans, pero antes de crear instancias de los beans, Spring llama a BeanFactoryPostProcessor. Estos controladores pueden modificar las definiciones de beans (metadatos de configuración).

4.Registro de BeanPostProcessor :

  • A continuación, Spring registra la instancia de BeanPostProcessor. Estos controladores pueden modificar instancias de Bean (objetos después de la creación e inicialización).

5. Creación previa de instancias de Singleton Bean :

  • Luego, Spring creará una instancia previa del bean singleton. Para los beans de ámbito único, Spring crea y configura estos beans y luego los coloca en el caché.

6. Inyección de dependencia :

  • Después de crear una instancia del bean, Spring realiza una inyección de dependencia. En este punto, se establecerán las propiedades del Bean y se inyectarán las dependencias relacionadas.

7.Inicialización de frijoles :

  • Posteriormente, se inicializará el Bean. Si el Bean implementa la interfaz InitializingBean, se llamará al método afterPropertiesSet; o si se define el método init, también se llamará al método especificado.

8.Interfaz de llamada de atención :

  • Si el bean implementa alguna interfaz Aware, como ApplicationContextAware o BeanNameAware, se llamará antes de la inicialización.

9.Postprocesamiento BeanPostProcessor :

  • Los métodos de preprocesamiento (postProcessBeforeInitialization) y posprocesamiento (postProcessAfterInitialization) de BeanPostProcessor se llaman antes y después de la inicialización del Bean, y pueden personalizar aún más el Bean.

10. Lanzamiento del evento :

  • Una vez que se han inicializado todos los beans singleton, Spring publica un ContextRefreshedEvent que indica que ApplicationContext se ha actualizado.

11.Utilice frijoles :

  • En este punto, todos los beans están listos y pueden usarse en otras partes de la aplicación.

12. Cerrar el contenedor :

  • Cuando se cierra el contexto de la aplicación, si el Bean implementa la interfaz desechableBean, se llamará al método de destrucción; o si el método del método de destrucción está definido, también se ejecutará para limpiar los recursos.

Durante todo el ciclo de vida, ApplicationContext y BeanFactory rastrean y administran el estado de cada Bean, desde la creación, la inyección de dependencia, la inicialización hasta la destrucción, asegurando que los Beans se creen y limpien en el momento correcto.

4.2 ¿El método de actualización se activa automáticamente? Si no es así, ¿bajo qué condiciones es necesario activarlo manualmente?

El método de actualización en primavera:

1. Cuándo activar:

  • Activación automática : al inicializar ApplicationContext, como usar el nuevo ClassPathXmlApplicationContext ("config.xml") en la aplicación, el método de actualización se llamará automáticamente durante el proceso de inicio del contenedor Spring.
  • Activación manual : si es necesario recargar la configuración mientras la aplicación se está ejecutando (el archivo de configuración puede modificarse), puede llamar manualmente al método de actualización para lograrlo. Pero esto generalmente se usa en escenarios especiales durante la fase de desarrollo o prueba, ya que hace que se reconstruya todo el contexto de la aplicación, incluidos todos los objetos Bean.

2. Por qué es necesaria la activación manual:

  • Normalmente, el contenedor Spring solo necesita cargar la configuración una vez e inicializar cada bean una vez al iniciar. A menos que existan necesidades especiales, como ajustar dinámicamente el nivel de registro o recargar beans específicos en el archivo de configuración, no se requiere activación manual.

El método de actualización en Spring Boot:

Spring Boot simplifica enormemente el proceso de configuración e inicio de las aplicaciones Spring. Configura automáticamente el ApplicationContext de Spring y llama al método de actualización cuando sea apropiado.

1. Disparador automático:

  • Cuando utiliza el método SpringApplication.run() de Spring Boot para iniciar una aplicación, Spring Boot crea automáticamente un ApplicationContext y llama al método de actualización internamente. Este proceso es automático y los desarrolladores normalmente no necesitan preocuparse.

2. Posibles escenarios de activación manual:

  • Spring Boot proporciona el módulo actuador, donde el punto final /refresh se puede usar para recargar la configuración (generalmente usado junto con Spring Cloud Config). Esta no es una llamada al método de actualización de ApplicationContext en el sentido tradicional, sino un mecanismo que activa la recarga de parte de la configuración, especialmente los beans marcados con @RefreshScope, que se pueden actualizar sin reiniciar toda la aplicación.

Consejos generales:

  • Para los desarrolladores, el método de actualización no debe llamarse manualmente a voluntad en un entorno de producción. Porque esto hará que se recargue toda la aplicación, afectando el rendimiento y posiblemente provocando la interrupción del servicio.
  • Si necesita actualizar dinámicamente la configuración, debe usar el punto final /refresh de Spring Cloud Config y Spring Boot Actuator, que es una forma más segura y controlada de actualizar la configuración.

4.3 ¿El método de actualización se comporta de manera diferente en Spring Boot? ¿Spring Boot proporciona una mejor manera de manejar los cambios en el contexto de la aplicación?

En Spring Boot, el comportamiento básico del método de actualización sigue siendo el mismo porque Spring Boot está construido sobre Spring y sigue los mismos principios básicos. Sin embargo, Spring Boot proporciona más automatización y conveniencia para la administración y actualización del contexto de la aplicación:

1. Configuración automática:

  • La característica única de configuración automática de Spring Boot reduce la necesidad de escenarios de actualización manual. Al iniciar, conecta automáticamente el bean y, por lo general, no es necesario llamar explícitamente a la actualización.

2.Colocación de externalización :

  • Spring Boot admite un potente mecanismo de configuración externo, que permite inyectar la configuración a través de archivos de configuración, variables de entorno, etc. Esto hace posible cambiar la configuración sin actualizar el contexto.

3. Actualización condicional :

  • Spring Boot utiliza anotaciones condicionales (como @ConditionalOnClass, @ConditionalOnBean, etc.), que permiten que el contexto ajuste dinámicamente su configuración de acuerdo con el entorno o condiciones específicas, lo que reduce la necesidad de activar la actualización manualmente.

4. Gestión del ciclo de vida :

  • A través de la clase SpringApplication, Spring Boot proporciona capacidades de administración adicionales para el ciclo de vida de la aplicación. Maneja muchas tareas que deben realizarse manualmente en aplicaciones Spring tradicionales, como inicializar y actualizar el contexto de la aplicación.

5.Puntos finales del actuador :

  • Para ejecutar aplicaciones, Spring Boot Actuator proporciona una serie de puntos finales de administración y monitoreo, algunos de los cuales se pueden usar para actualizar la configuración (como / actualizar el punto final) o reiniciar el contexto (como / reiniciar el punto final), que se pueden usar en su lugar. en algunos casos, reinicio completo de la aplicación.

6. Configurar el monitoreo de cambios :

  • Las aplicaciones que utilizan Spring Cloud Config pueden actualizar automáticamente el contexto cuando cambia la configuración. Se pueden escuchar los cambios en el servidor de configuración y activar una actualización automática del contexto del cliente sin intervención manual.

7. Manejo de errores :

  • Spring Boot tiene un mecanismo de manejo de errores predeterminado, especialmente en aplicaciones web, que proporciona una página de error predeterminada y un punto final /error. Además, los desarrolladores pueden personalizar el manejo de errores para satisfacer necesidades específicas.

En resumen, Spring Boot proporciona una forma más automatizada de manejar los cambios en el contexto de la aplicación. En muchos casos, no es necesario llamar manualmente al método de actualización. Sin embargo, si necesita cambiar dinámicamente la configuración del Bean en tiempo de ejecución y desea que estos cambios surtan efecto de inmediato, es posible que también deba utilizar el método de actualización proporcionado por Spring o utilizar los puntos finales relevantes de Spring Boot Actuator para lograr este propósito.

Haga clic para seguir y conocer las nuevas tecnologías de Huawei Cloud lo antes posible ~

Alibaba Cloud sufrió un fallo grave y todos los productos se vieron afectados (restaurados). Tumblr enfrió el sistema operativo ruso Aurora OS 5.0. Se presentó la nueva interfaz de usuario Delphi 12 y C++ Builder 12, RAD Studio 12. Muchas empresas de Internet contratan urgentemente programadores de Hongmeng. Tiempo UNIX está a punto de entrar en la era de los 1.700 millones (ya entró). Meituan recluta tropas y planea desarrollar la aplicación del sistema Hongmeng. Amazon desarrolla un sistema operativo basado en Linux para deshacerse de la dependencia de Android de .NET 8 en Linux. El tamaño independiente es reducido en un 50%. Se lanza FFmpeg 6.1 "Heaviside"
{{o.nombre}}
{{m.nombre}}

Supongo que te gusta

Origin my.oschina.net/u/4526289/blog/10143075
Recomendado
Clasificación