Análisis completo del principio de carga de la configuración de SpringBoot

Uno, cargando lógica

1.1) Cargar postprocesador Genesis

Tomemos un ejemplo:

final AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext ([su clase de configuración] .class);

Preste atención al objeto AnnotationConfigApplicationContext aquí, siga el código fuente:

// En el método de construcción sin parámetros, verá el siguiente código  
this.reader = new AnnotatedBeanDefinitionReader (this);

AnnotatedBeanDefinitionReader:

public AnnotatedBeanDefinitionReader (registro BeanDefinitionRegistry, entorno Environment) { 
   Assert.notNull (registro, "BeanDefinitionRegistry no debe ser nulo"); 
   Assert.notNull (entorno, "El entorno no debe ser nulo"); 
   // Asignar el objeto ApplicationContext a AnnotatedBeanDefinitionReader 
   this. registro = registro; 
   // Cálculo de expresión de condición de procesamiento de usuario @Conditionl host, er, 
   this.conditionEvaluator = new ConditionEvaluator (registro, entorno, nulo); 
   // Aquí hay algo trascendental, registrar un montón de procesadores configurados se denominan "procesadores Genesis" porque estos procesadores juegan un papel decisivo en el procesamiento posterior 
   AnnotationConfigUtils.registerAnnotationConfigProcessors (this.registry); 
}

Dije antes que un montón de procesadores configurados-objetos Genesis están registrados en AnnotationConfigUtils.registerAnnotationConfigProcessors. Nota: Todos los registrados aquí son definiciones de Bean, todos los registrados aquí son definiciones de Bean y todos los registrados aquí son definiciones de Bean. Se dicen cosas importantes tres veces; entre ellos, se registra un "ConfigurationClassPostProcessor". Nuestra clase de configuración (hay otros procesadores génesis para analizar anotaciones, este no es nuestro enfoque hoy, déjelos ir primero), es un postprocesador de una fábrica de Bean;

clipboard.png

En el diagrama de clases, puede ver que esta clase implementa las interfaces BeanFactoryPostProcessor y BeanDefinitionRegistryPostProcessor. ¿Por qué es un BeanDefinitionRegistryPostProcessor en lugar de un BeanFactoryPostProcessor? El contenido anterior mencionó que BeanFactoryPostProcessor puede procesar y modificar definiciones de Bean. BeanDefinitionRegistryPostProcessor es una mejora de BeanFactoryPostProcessor, que no solo puede procesar y modificar sino también registrar;

1.2) Registro de clases de configuración

Registre la clase de configuración entrante a través del método register a través de la segunda oración del código (método register (annotatedClasses)) en el método de construcción AnnotationConfigApplicationContext Aquí, la definición de Bean todavía está registrada;

// El lector aquí es el objeto AnnotatedBeanDefinitionReader mencionado en 1.1  
this.reader.register (annotatedClasses);

En este punto, el procesador y la clase de configuración para analizar la configuración están disponibles, por lo que el siguiente paso es cómo manejarlo; preste especial atención: aquí finalmente la clase de configuración se registra solo a través de org.springframework.context.annotation.AnnotatedBeanDefinitionReader Método #doRegisterBean Las definiciones de Bean no tienen nada que ver con el almacenamiento en caché, el procesamiento y el análisis sintáctico de Spring. Estas son las rutinas del ecosistema de Spring que primero se convierten en definiciones de Bean y luego en el análisis y procesamiento; estas definiciones de Bean se almacenan en el contenedor BeanDefinitionMap;

1.3) Iniciar procesamiento

Aquí está el método de actualización del núcleo de Spring

/ * 
* Interfaz de actualización del contenedor IOC 
* Este método es el método central; 
* Es la encarnación de toda la ecología de Spring; 
* Refleja el ciclo de vida completo del IOC, incluido el ciclo de vida de inicialización de Bean, carga de Bean, uso, y destrucción 
* Este método contiene Los 13 métodos básicos son la esencia del Sutra del Jiuyin de Spring. Después de comprender estos 13 métodos, realmente comprenderá Spring 
* * / 
refresh ();

Entonces, en qué método se lleva a cabo específicamente el análisis de la clase de configuración, no se preocupe, tómese su tiempo;

/ * 
* 5: Llamar al postprocesador de la fábrica de frijoles 
* Aquí, el Bean que cumple las condiciones se convierte en una Definición de Bean (BeanDefinition) 
* Aquí está el análisis real de la clase y las anotaciones de elementos en la clase 
* Por ejemplo : @Configuration, @ ComponentScan, @Bean, etc. finalmente ponga estas definiciones de Bean en el BeanDefinitionMap 
* Debe haber una causa y efecto, aquí está la creación de la Definición de Bean del Procesador Genesis (BeanDefinition) creada antes: ConfigurationClassPostProcessor; es decir, una llamada separada a getBean 
* Debido a Genesis de ConfigurationClassPostProcessor El objeto procesador tiene prioridad absoluta.Los procesadores Genesis no se han creado, por lo que hay un pedo detrás 
* * / 
invokeBeanFactoryPostProcessors (beanFactory);

Es aquí donde se llama al postprocesador de la fábrica de Bean para procesar. Póngase en contacto con el contenido anterior y piense en ello. ¿El postprocesador llamado aquí tiene y solo la definición de Genesis Bean y la clase de configuración que pasé? Definición de Bean, y ConfigurationClassPostProcessor es una implementación de la interfaz de postprocesador de una fábrica de Bean, por lo que la clase de configuración se procesa aquí;

PD: Aquí hay una pregunta que se hace a menudo en las entrevistas. Al llamar al postprocesador ConfigurationClassPostProcessor Genesis, primero debe llamar al método postProcessBeanDefinitionRegistry de la interfaz BeanDefinitionRegistryPostProcessor con función de registro para procesar la definición de Bean de la clase de configuración, y luego llamar al BeanFactoryPostProcessor interfaz El método postProcessBeanFactory está registrado;

2. Lógica de procesamiento

/ * 
* Pasar a la fábrica de frijoles y obtener el postprocesador de la fábrica de frijoles en el applicationContext (pero debido a que no hay un proceso de instanciación, el pasado está vacío) 
* getBeanFactoryPostProcessors (): el conjunto actual de postprocesadores de la fábrica de frijoles, aquí y es no registrado en el incorporado 
* Esta colección está vacía cuando se llama por primera vez; 
* Este contenedor se usa para llamar manualmente al método ApplicationContext.addBeanFactoryPostProcessor () después de inicializar el AnnotationConfigApplicationContext externamente; 
* Es el Bean agregado manualmente por usted mismo. procesador; 
* * / 
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors (beanFactory, getBeanFactoryPostProcessors ());

En PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors, se divide aproximadamente en dos pasos; el primer paso: llamar a todos los Bean Definition PostProcessor que implementa la función de registro (BeanDefinitionRegistryPostProcessor); el segundo paso: llamar a todos los postprocesadores que implementan Bean Factory (BeanFacessor);

Hoy, me enfocaré en el contenido del primer paso para ingresar el método invokeBeanFactoryPostProcessors en la clase PostProcessorRegistrationDelegate; mucha energía por delante, preparar los tejidos y comenzar a rodar:

2.1) La parte principal del código fuente

El primer paso puede ser completar el análisis y la carga de la configuración por el procesador Genesis ConfigurationClassPostProcessor; en todos los procesadores Genesis, solo ConfigurationClassPostProcessor implementa la interfaz BeanDefinitionRegistryPostProcessor;

/ * 
* El primer paso: primero llamar al postprocesador de BeanDefinitionRegistryPostProcessor 
* La primera vez entra, solo la definición de Bean del procesador Genesis y la definición de Bean de la clase de configuración pasada por el constructor 
* Procesador Genesis: ConfigurationClassPostProcessor es el postprocesador de la definición de Bean con la implementación de la función de registro Processor (BeanDefinitionRegistryPostProcessor); 
* Entonces aquí debe ser ConfigurationClassPostProcessor que cumpla con las condiciones; 
* * / 
// Juzgar que nuestro beanFacotry implementa BeanDefinitionRegistry 
if (beanFactory instanceof BeanDefinitionRegistry) { 
   // Convertir a la fuerza nuestra fábrica de frijoles en 
   BeanDefinitionRegistry ( BeanDefinitionRegistry) beanFactory; 
   // Guarde 
   el postprocesador del tipo BeanFactoryPostProcessor 
   List <BeanFactoryPostProcessor> regularPostProcessors = new ArrayList <> (); // Guarde el postprocesador del tipo BeanDefinitionRegistryPostProcessor
   List <BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList <> (); 
 
   / *
   * pasó cíclicamente beanFactoryPostProcessors 
   * Si esta colección no se llama manualmente al método addBeanFactoryPostProcessor después de inicializar AnnotationConfigApplicationContext en el frente 
   * entonces esta colección está vacía 
   * * / 
   para (BeanProcessor postProcessor) 
      // Determinar si nuestro postprocesador es un BeanDefinitionRegistryPostProcessor 
      if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) { 
         // Conversión forzada 
         BeanDefinitionRegistryPostProcessor registrationProcessor = 
               (BeanDefinitionRegistryPostProcessor) postProcessor; 
         // Llámalo como el método de postprocesador BeanDefinition del procesador. 
         registryProcessor.postProcessBeanDefinitionPostProcessor.postProcessBeanDefinitionPostProcessor 
         usado para guardar la colección
         RegistryProcessors.add (registryProcessor); 
      } 
      else {// Si la interfaz BeanDefinitionRegistryPostProcessor no está implementada, entonces es BeanFactoryPostProcessor 
         // Agregar el postprocesador actual a regularPostProcessors 
         regularPostProcessors.add (postProcessor); 
      } 
   } 

   // Definir un usuario colectivo Guardar el Actualmente preparado BeanDefinitionRegistryPostProcessor 
   List <BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList <> (); 

   / * 
   * Paso 1.1: Ir al recipiente para obtener el nombre del procesador de la haba de la BeanDefinitionRegistryPostProcessor 
   * Este es en realidad llamando el procesador Génesis ConfigurationClassPostProcessor 
   * * /  
   GenesisClassPostProcessor String [] postProcessorNames =
         BeanFactory .getBeanNamesForType (BeanDefinitionRegistryPostProcessor.class, verdadero, falso);
   // Bucle el nombre de tipo del BeanDefinitionRegistryPostProcessor obtenido en el paso anterior 
   para (String ppName: postProcessorNames) { 
      / * 
      * Determinar si la interfaz PriorityOrdered está implementada 
      * Si la interfaz PriorityOrdered está implementada, se llamará primero 
      * Esto refleja que hay serán zapatos para niños que cumplan con la especificación. Prioridad; procesador Genesis ConfigurationClassPostProcessor es un zapato para 
      niños que cumple con la especificación, por lo que aquí se llamará primero 
      * * / if (beanFactory.isTypeMatch (ppName, PriorityOrdered.class)) { 
         // Obtener el objeto llamando a getBean () mostrado Luego agregarlo a la colección 
         currentRegistryProcessors a currentRegistryProcessors.add (beanFactory.getBean (ppName, BeanDefinitionRegistryPostProcessor.class));  
         // En el Al mismo tiempo, también se agrega a la colección ProcessingBeans.
         procesadosBeans.add (ppName); 
      } 
   }
   // Ordene el BeanDefinitionRegistryPostProcessor en la colección currentRegistryProcessors por el valor devuelto por el método getOrder en PriorityOrdered 
   sortPostProcessors (currentRegistryProcessors, beanFactory); 
   // Agréguelo a registryProcessors para guardarlo en 
   registryProcessors.addAll (currentRegistryProcessorsDeProcessor típico); 
   / ** 
    * Incessor aquí está ConfigurationClassPostProcessor 
    * que se usa para cargar definiciones de beans, como nuestro escaneo de paquetes, @import, etc. . . . . . . . . 
    * Aquí está el método postProcessBeanDefinitionRegistry en el Bean Definition 
   PostProcessor con función de registro (BeanDefinitionRegistryPostProcessor) 
    * / invokeBeanDefinitionRegistryPostProcessors (currentRegistryProcessors, registro); 
   // Después de que se completa la llamada, 
   limpia inmediatamente las gotas currentRegistryProcessors.clear ();

   // Vaya al contenedor para obtener el nombre del procesador del bean de BeanDefinitionRegistryPostProcessor 
   postProcessorNames = beanFactory.getBeanNamesForType (BeanDefinitionRegistryPostProcessor.class, true, false); 
   // Haga un bucle con el nombre de tipo del BeanDefinitionRegistryPostProcessor obtenido en el paso anterior 
   pp postProcessorNames) { 
      / * 
      * No se han procesado, e implementan la interfaz Ordered, lo anterior es llamar al postprocesador de definición Bean que implementa la interfaz PriorityOrdered, aquí está el procesamiento del postprocesador de definición Bean que implementa la interfaz Ordered 
      * Entonces puede sacar una conclusión: la interfaz PriorityOrdered tiene una prioridad más alta que Ordered; 
      * * / 
      if (! procesadosBeans.contains (ppName) && beanFactory.isTypeMatch (ppName, Ordered.class)) {  
         // El método mostrado para llamar a getBean () obtiene el objeto y luego lo agrega a la colección currentRegistryProcessors
         currentRegistryProcessors.add (beanFactory.getBean (ppName, BeanDefinitionRegistryPostProcessor.class)); 
         // También se agregó a la colección ProcessingBeans 
         processedBeans.add (ppName); 
      } 
   } 
   // Ordenar BeanDefinitionRegistryPostProcessor de la colección currentRegistryProcessors 
   sortPostProcessors (currentRegistryProcessors, BeanFactory); 
   // él Añadir para guardar Para registryProcessors en 
   registryProcessors .addAll (currentRegistryProcessors); 
   / * 
   * Invocar su método de posprocesamiento 
   * Aquí está el método postProcessBeanDefinitionRegistry en el BeanDefinitionRegistryPostProcessor con función de registro 
   * * / 
   invokeBeanDefinitionRegistryPostProcessors (currentRegistryProcessors, registro); 
   // Después de la llamada 
   currentReg , limpiar inmediatamente );

   // Llama al BeanDefinitionRegistryPostProcessor que no implementa ninguna interfaz de prioridad  
   // Defina una variable de conmutación para procesamiento repetido El valor predeterminado es verdadero
   boolean reiterate = true; 
   // Puedes entrar por primera vez 
   while (reiterate) { 
      // Ingresa al bucle y cambia inmediatamente la variable de cambio a fasle 
      reiterate = false; 
      / / Ir al contenedor Obtenga el nombre del procesador del bean de BeanDefinitionRegistryPostProcessor 
      postProcessorNames = beanFactory.getBeanNamesForType (BeanDefinitionRegistryPostProcessor.class, true, false); 
      // Repita el nombre del tipo de BeanDefinitionRegistryPostProcessor (obtenido en el paso anterior 
      ppProcessor: postProcessor 
         // Sin procesar , es decir, el postprocesador de definición de Bean que no implementa ninguna interfaz de prioridad (PriorityOrdered, Ordered) 
         if (! ProcessingBeans.contains (ppName)) { 
            // El método mostrado para llamar a getBean () obtiene el objeto y luego lo agrega a la colección currentRegistryProcessors ir con
            currentRegistryProcessors.add (beanFactory.getBean (ppName, BeanDefinitionRegistryPostProcessor.class));  
            // También se agregó a la colección de procesados
            procesadosBeans.add (ppName); 
            // Establecer en verdadero nuevamente 
            reiterate = true; 
         } 
      } 
      // Ordenar el BeanDefinitionRegistryPostProcessor en la colección 
      currentRegistryProcessors sort, beanProcessors ; 
      // 
      Agréguelo al registroProcessors.addAll (currentRegistryProcessors); 
      / * 
       * llame a su método de posprocesamiento 
       * aquí está el posprocesador de definición de Bean con función de registro (BeanDefinitionRegistryPostProcessor) método postProcessBeanDefinitionRegistry 
       * * /
      invokeBeanDefinitionRegistryPostProcessors (currentRegistryProcessors, registro); 
      // Limpiar 
      currentRegistryProcessors.clear ();  
   }

   / * 
   * Llamar a la interfaz que implementa BeanDefinitionRegistryPostProcessor; esta interfaz implementa BeanFactoryPostProcessor 
   * La llamada real aquí es el método postProcessBeanFactory 
   * beanPostProcessor * / 
   (invokeFactoryPostProcessFactory) de BeankeFactory. ; 
   // Llamar BeanFactoryPostProcessor 
   invokeBeanFactoryPostProcessors (regularPostProcessors, beanFactory); 
} else {// Si el beanFacotory actual no implementa BeanDefinitionRegistry directamente electroforesis 
    // Método de electroforesis directo de beanFacotoryPostProcessor interfaz para post-procesamiento 
   beanFactory invokeBeanBeanFactory 
} 
/
* Fin del primer paso 
* ------------------------------------------- - ------------------ 
* * /

En este paso, preste especial atención al método invokeBeanFactoryPostProcessors en las últimas líneas; ingrese este método:

private static void invokeBeanFactoryPostProcessors ( 
      Collection <? extiende BeanFactoryPostProcessor> postProcessors, ConfigurableListableBeanFactory beanFactory) { 
   / * 
   * Este es el postprocesador que llama a Bean Factory. Tenga en cuenta que esta no es una función registrada 
   * * / 
   para (BeanFactoryPostProcessor postProcessor) {postProcessor: 
      / * 
      * La siguiente es otra llamada del viento, que en realidad es un misterio oculto; 
      * Debe haber un procesador Genesis cargado inicialmente-ConfigurationClassPostProcessor: Este método realmente hace algo 
      trascendental ; 
      * * / postProcessor.postProcessBeanFactory (beanFactory) ; 
   } 
}
/ ** 
 * Preparar las clases de configuración para atender solicitudes de 
 beans en tiempo de ejecución * reemplazándolas con subclases mejoradas con CGLIB. 
 * / 
@Override 
public void postProcessBeanFactory (ConfigurableListableBeanFactory beanFactory) { 
   int factoryId = System.identityHashCode (beanFactory); 
   if (this.factoriesPostProcessed.contains (factoryId)) { 
      lanzar una nueva IllegalStateException ( 
            "postProcessBeanFactory ya se ha llamado en este postprocesador contra" + beanFactory); 
   } 
   this.factoriesPostProcessed.add (factoryId); 
   if (! this.registriesPostProcessed.contains (factoryId)) { 
      // El gancho BeanDefinitionRegistryPostProcessor aparentemente no es compatible ...
      // Simplemente llame a processConfigurationClasses perezosamente en este punto. 
      ProcessConfigBeanDefinitions ((BeanDefinitionRegistry) beanFactory); 
   } 
   / * 
   * Atención especial aquí es usar Cglib para proxy la clase de configuración en la siguiente llamada; recuerde nuestra clase de configuración Habrá métodos 
   como @Configuration, @Bean, etc.que están anotados con anotaciones; * Aquí hay otra pregunta común de la entrevista; si llamamos al método anotado con la anotación @Bean en la clase de configuración, habrá 10 métodos más En este momento, el procesamiento en este método en realidad solo se llama 
   * una vez, porque es proxy, todas las llamadas, excepto la primera llamada, se obtienen del grupo de búfer; esto también se cumple para la idea de programación orientada a Bean, ahora que ya existe, por qué tiene que repetir el proceso; 
   * Preste especial atención al tipo de Full al generar Bean Definition (BeanDefinition) solo cuando se genera la clase anotada con la anotación @Configuration; en el código fuente en profundidad, puede ver que solo Bean definición (Beandefinition) 
   * Se utilizará como proxy cuando el tipo sea Full 
   * * / 
   hanceConfigurationClasses (beanFactory); 
   beanFactory.addBeanPostProcessor (new ImportAwareBeanPostProcessor (beanFactory)); 
}

2.2) Proceso de ejecución del análisis de configuración

En realidad, el primer paso es llamar continuamente al postprocesador de la fábrica de Bean. El proceso de llamada es simplemente la siguiente secuencia;

db41e76ac0a63711a031b2755ab4e098.png

Recordando el primer capítulo, dijimos que al inicio se creará un procesador Genesis ConfigurationClassPostProcessor. Este procesador implementa tanto BeanDefinitionRegistryPostProcessor como BeanFactoryPostProcessor, por lo que en este proceso se llamará en el primer y cuarto paso; el primer paso es llamar al método en el postprocesador con la función de registro, por lo que en este paso se procesarán el @Bean y otros elementos de la clase; el elemento se convierte en una definición de Bean; el cuarto paso es llamar al método sin la función de registro Métodos en el postprocesador;

Mirando un código de muestra, lo siguiente es una implementación de nuestro post-procesador de fábrica de Bean personalizado con función de registro:

@Component 
clase pública TestConfig implementa BeanDefinitionRegistryPostProcessor { 

    @Override 
    public void postProcessBeanDefinitionRegistry (registro de BeanDefinitionRegistry) throws BeansException { 
        
    } 
    
    @Override 
    public void postProcessBeanFactory (ConfigurableListableBeanFactory lanza BeansException 

    ) 
{

Suponiendo que he implementado un BeanDefinitionRegistryPostProcessor en mi código en este momento, se llamará en los pasos tercero y cuarto. Esto ilustra completamente que Spring es poderoso debido a su ecología y se puede llamar nuevamente en lugares específicos para el desarrollo. Espacio completo para el personal jugar;

(PD: Aquí hay una pregunta de entrevista más común. ¿Cuándo se cargó el BeanDefinitionRegistryPostProcessor definido por nosotros mismos? Combinado con lo anterior, en el primer paso, solo hay un procesador Genesis ConfigurationClassPostProcessor. Este El procesador se usa específicamente para analizar la clase de configuración. Todas nuestras configuraciones personalizadas se cargan a través de este postprocesador Genesis, por lo que nuestro postprocesador personalizado solo existirá después del primer paso; PD: para zapatos de niños que quieran abusar de sí mismos, puede seguir el código del primer paso en profundidad);

clipboard.png

Este paso tiene solo un postprocesador Genesis ConfigurationClassPostProcessor, así que eche un vistazo al método de implementación de este postprocesador:

postProcessBeanDefinitionRegistry pública vacío (registro BeanDefinitionRegistry) { 
   int registryId = System.identityHashCode (registro); 
   si (this.registriesPostProcessed.contains (registryId)) { 
      throw new IllegalStateException ( 
            "postProcessBeanDefinitionRegistry ya se llama en este post-procesador en contra de" + registro); 
   } 
   if (this.factoriesPostProcessed.contains (registryId)) { 
      throw new IllegalStateException ( 
            "postProcessBeanFactory ya llamado en este post-procesador contra" + registro); 
   } 
   this.registriesPostProcessed.add (registryId); 
   // Análisis real de nuestra definición de bean, Este método es demasiado profundo, al usar este método tuve que explotar la vejiga y el intestino grueso antes de encontrarlo, los niños que estén interesados ​​en desafiar pueden probarlo;
   processConfigBeanDefinitions (registro); 
}

El método processConfigBeanDefinitions es el procesamiento de análisis e inyección, y la cadena de llamadas es muy profunda. Aquí hay una mención: debe prestar especial atención a los métodos que comienzan con do en Spring. De acuerdo con la especificación de Spring, estos métodos que comienzan con do son en realidad métodos para hacer las cosas;


Supongo que te gusta

Origin blog.51cto.com/15138908/2668826
Recomendado
Clasificación