Análisis del código fuente de SpringBoot (7): prepareContext / prepare el contexto de la aplicación

I. Introducción

Este artículo se basa en el análisis del código fuente spring-boot-2.2.14.BUILD-SNAPSHOT de prepareContext para preparar el contexto de la aplicación.

2. prepararContexto

Siguiendo lo anterior, este artículo continúa analizando el método de ejecución de SpringApplication y analiza la línea prepareContext de
Insertar descripción de la imagen aquí
parámetros de solicitud de código:

Tipo de parámetro Breve descripción de los parámetros.
Contexto ConfigurableApplicationContext El valor de retorno del método createApplicationContext() representa el contexto de la aplicación.
Entorno de entorno configurable Clase de interfaz para información de variables de entorno del sistema
Oyentes SpringApplicationRunListeners Clase de colección de SpringApplicationRunListener
Argumentos de aplicaciónArgumentos de aplicación Parámetros de la aplicación
Banner impresoBanner Información del banner impreso

Ingrese la implementación del método:

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
			SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
    
    
	// 1、设置环境对象
	//统一ApplicationContext和Application,使用Application的environment
	context.setEnvironment(environment);
	// 2、注册组件 设置ApplicationContext的beanNameGenerator、resourceLoader、
	postProcessApplicationContext(context);
	// 3、应用初始化器对ApplicationContext进行初始化处理(Initializers在构造SpringApplication时就从spring.factories中加载到了)
	applyInitializers(context);
	// 4、发布ApplicationContext准备妥当事件
	listeners.contextPrepared(context);
	// 5、打印startup日志信息
	if (this.logStartupInfo) {
    
    
		logStartupInfo(context.getParent() == null);
		logStartupProfileInfo(context);
	}
	// 6 、添加特定的单例beans到 beanFactory中
	// Add boot specific singleton beans
	ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
	beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
	if (printedBanner != null) {
    
    
		beanFactory.registerSingleton("springBootBanner", printedBanner);
	}
	if (beanFactory instanceof DefaultListableBeanFactory) {
    
    
		((DefaultListableBeanFactory) beanFactory)
				.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
	}
	if (this.lazyInitialization) {
		context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
	}
	// Load the sources加载资源
	Set<Object> sources = getAllSources();
	Assert.notEmpty(sources, "Sources must not be empty");
	// 加载启动类,见启动类注入容器中
	load(context, sources.toArray(new Object[0]));
	// 触发contextLoaded事件
	listeners.contextLoaded(context);
}

Para preparar el contexto de la aplicación AnnotationConfigServletWebServerApplicationContext, se realizaron los siguientes 8 pasos

  1. Unificar ApplicationContext y el entorno utilizado por la aplicación
  2. Contexto de aplicación de posprocesamiento
  3. Ejecutar inicializadores
  4. Publicar contextoEvento preparado
  5. Imprimir registros de inicio y perfil
  6. Registrar un frijol singleton
  7. Cargar clase de inicio
  8. Publicar evento contextLoaded

2.1、context.setEntorno

Unificar ApplicationContext y el entorno utilizado por la aplicación

public class AnnotationConfigServletWebServerApplicationContext
        extends ServletWebServerApplicationContext implements AnnotationConfigRegistry {
    
    
        
    @Override
    public void setEnvironment(ConfigurableEnvironment environment) {
    
    
        //显式调用父类AbstractApplicationContext的setEnvironment方法
        super.setEnvironment(environment);
        //调用AnnotatedBeanDefinitionReader#setEnvironment()方法
        this.reader.setEnvironment(environment);        
        //ClassPathBeanDefinitionScanner继承了ClassPathScanningCandidateComponentProvider,所以调用了父类setEnvironment方法
        this.scanner.setEnvironment(environment);
    }
    
}

Reemplace todos los entornos relevantes en el contexto con el entorno creado en SpringApplication. ¿Todavía recuerdas las preguntas en " Análisis del código fuente de SpringBoot (5) -createApplicationContext para crear un contexto de aplicación "? La extensión es: antes había dos entornos en nuestra aplicación, uno en el contexto y otro en SpringApplication. Después de este método, solo existirá el entorno en SpringApplication y se reciclará el entorno original en contexto.

Con respecto a este punto, mencionamos una falla en el artículo anterior, porque aunque aquí se reemplaza el entorno nativo del contenedor, cuando se inicializó SpringBootExceptionReporter antes, el entorno nativo se configuró en el analizador de excepciones y el entorno mantenido por estos analizadores no. not Obtener actualizaciones sincronizadas no es el objeto de entorno que realmente utilizamos.

2.2、postProcessApplicationContext(contexto);

Se realizaron los siguientes tres pasos.

  1. Establecer beanNameGenerator de ApplicationContext
  2. Establecer el cargador de recursos y el cargador de clases de ApplicationContext
  3. Establecer el servicio de conversión de tipos de ApplicationContext
protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
    
    
    //beanNameGenerator默认为null,所以此处没有设置
    if (this.beanNameGenerator != null) {
    
    
        //如果beanNameGenerator不为空
        //那么注册一个名为internalConfigurationBeanNameGenerator
        //值为beanNameGenerator的单例bean
        context.getBeanFactory().registerSingleton(
                AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
                this.beanNameGenerator);
    }
    //resourceLoader默认为null,所以此处没有设置
    if (this.resourceLoader != null) {
    
    
        //如果resourceLoader不为空
        if (context instanceof GenericApplicationContext) {
    
    
            //context是GenericApplicationContext子类
            //那么设置上下文context的resourceLoader
            ((GenericApplicationContext) context)
                    .setResourceLoader(this.resourceLoader);
        }
        if (context instanceof DefaultResourceLoader) {
    
    
            //如果当前上下文是DefaultResourceLoader的子类
            //那么设置上下文context的classLoader
            ((DefaultResourceLoader) context)
                    .setClassLoader(this.resourceLoader.getClassLoader());
        }
    }
    //this.addConversionService默认为true
    if (this.addConversionService) {
    
    
        //设置类型转换Service
        context.getBeanFactory().setConversionService(
                ApplicationConversionService.getSharedInstance());
    }
}

Primero, verifique si hay un BeanNameGenerator personalizado en el objeto SpringApplication. Si es así, regístrelo en el grupo singleton del contenedor. Este objeto se usa para generar nombres para los beans en el contenedor. Cuando sale el nuevo contenedor Spring, generará uno de forma predeterminada. La estrategia de nomenclatura es poner el nombre de la clase en minúsculas, pero el objeto en SpringApplication es nulo de forma predeterminada.

Luego verifique si el objeto SpringApplication tiene un ResourceLoader personalizado, si es así, asígnelo al contenedor, lo hemos analizado antes y el valor predeterminado es nulo.

La última rama if, addConversionService, se establece en verdadero de forma predeterminada en el constructor del objeto SpringApplication, por lo que si se usará, establece un ConversonService para el contenedor. Esta clase se usa para la conversión de tipos, como String a Integer, etc. , de hecho lo he visto varias veces en artículos anteriores.
Insertar descripción de la imagen aquí

2.3, aplicar Inicializadores (contexto)

Lo que se carga es la lista ApplicationContextInitializer en META-INF/spring.factories, y su método de inicialización se llama en secuencia.

protected void applyInitializers(ConfigurableApplicationContext context) {
    
    
	for (ApplicationContextInitializer initializer : getInitializers()) {
    
    
	    //断言判断initializer的类型是否符合条件
		Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
				ApplicationContextInitializer.class);
		Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
		//执行各个initializer的初始化initialize方法
		initializer.initialize(context);
	}
}

Los inicializadores se obtienen durante la inicialización de SpringApplication. Para obtener el código fuente, consulte " Análisis del código fuente de SpringBoot (2) -Código fuente de inicio de SpringBoot (la depuración del código fuente gráfico de diez mil palabras explica el principio de inicio de Springboot)" . de 7 inicializadores se obtienen:

  • DelegarApplicationContextInitializer
  • SharedMetadataReaderFactoryContextInitializer
  • ContextIdApplicationContextInitializer
  • ConfiguraciónAdvertenciasApplicationContextInitializer
  • ServerPortInfoApplicationContextInitializer
  • CondiciónEvaluaciónReporteLoggingListener
  • RSocketPortInfoApplicationContextInitializer

Este artículo primero clasifica el contexto del método prepareContext. En cuanto a la inicialización realizada por estos ApplicationContextInitializers integrados, la analizaremos por separado en nuestro próximo artículo " Análisis del código fuente de SpringBoot (8) -ApplicationContextInitializer integrado ".

Todas estas clases de inicialización no realizan la operación sustancial de iniciar el servicio. Todas registran objetos y entierran el punto. En realidad, invokeBeanFactoryPostProcessors llama al método de inicialización más tarde y antes de que se inicie el proyecto.

2.4 Publicar el evento ApplicationContextInitializedEvent

// 4、发布ApplicationContext准备妥当事件
listeners.contextPrepared(context);

Evento de finalización de inicialización del contenedor de aplicaciones, los oyentes interesados ​​en este evento son

  • FondoPreinicializador
  • DelegandoApplicationListener

Punto de extensión BackgroundPreinitializer
, inicializador de proceso en segundo plano, utilizado para la ejecución multiproceso de tareas que consumen mucho tiempo en segundo plano, el evento ApplicationContextInitializedEvent no se procesa aquí

Punto de extensión DelegatingApplicationListener
, escucha de proxy, continúa distribuyendo eventos, no maneja el evento ApplicationContextInitializedEvent

2.5 Imprimir registros de inicio y perfil

//logStartupInfo默认为true
if (this.logStartupInfo) {
    
    
    //判断是否有父容器,打印项目启动信息
    // Starting Demo3Application on pcname with PID 12372 (E:\workspace\demo3\target\classes started by username in E:\workspace\demo3)
    logStartupInfo(context.getParent() == null);

    //打印profile
    //No active profile set, falling back to default profiles: default
    logStartupProfileInfo(context);
}

Este código determina si el contenedor actual tiene un contenedor principal. De lo contrario, se considera el contenedor raíz para el inicio del proyecto y se imprimirá una línea de registro que incluye la clase de inicio, el nombre del servidor actual, la ruta del proyecto, el PID, etc. .

2023-07-18 10:35:07.105  INFO 3136 --- [           main] com.example.demo.Demo3Application        : Starting Demo3Application on hualsd with PID 3136 (D:\WorkSpace\demo3\target\classes started by 188 in D:\WorkSpace\demo3)
2023-07-18 10:35:32.693  INFO 3136 --- [           main] com.example.demo.Demo3Application        : The following profiles are active: sit

2.6 Registrar frijol singleton

Dos beans singleton registrados

  • Bean de parámetro de línea de comando, el nombre es springApplicationArguments y el valor es applicationArgument
  • frijol de banner, el nombre es springBootBanner, el valor es printBanner
//注册命令行参数bean
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
    
    
    //banner bean
    beanFactory.registerSingleton("springBootBanner", printedBanner);
}

Finalmente, el método RegisterSingleton los registrará en el contenedor singletonObjects. Como podemos ver por el nombre, este es un contenedor para almacenar objetos singleton.
Insertar descripción de la imagen aquí

2.6.1 Registro manual del proceso Singleton Bean

Llame al método DefaultListableBeanFactory#registerSingleton y llame explícitamente al método de la clase principal DefaultSingletonBeanRegistry#registerSingleton

DefaultListableBeanFactory Registrar manualmente un bean singleton
Registrar manualmente un bean singleton es diferente de escanear la definición del bean y luego registrar el bean singleton.El bean singleton registrado manualmente no se mantiene en beanDefinitionMap, pero el beanName se mantiene en manualSingletonNames.

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
        implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
    
    

    //注册单例bean
    public void registerSingleton(String beanName, Object singletonObject) throws IllegalStateException {
    
    
        super.registerSingleton(beanName, singletonObject);

        //判断bean的创建过程是否已经开始了
        //调用抽象父类AbstractBeanFactory#hasBeanCreationStarted()方法
        //判断AbstractBeanFactory成员变量alreadyCreated Set不为空
        if (hasBeanCreationStarted()) {
    
    
            //bean创建过程已经开始了
            //锁住成员变量beanDefinitionMap
            synchronized (this.beanDefinitionMap) {
    
    
                if (!this.beanDefinitionMap.containsKey(beanName)) {
    
    
                    //如果bean定义Map,  beanDefinitionMap已经包含了bean
                    //维护到手工单例bean名称manualSingletonNames中
                    Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames.size() + 1);
                    updatedSingletons.addAll(this.manualSingletonNames);
                    updatedSingletons.add(beanName);
                    this.manualSingletonNames = updatedSingletons;
                }
            }
        }
        else {
    
    
            // bean还没有注册过, 仍处于启动注册阶段
            if (!this.beanDefinitionMap.containsKey(beanName)) {
    
    
                //如果beanDefinitionMap不包含beanName
                //那么添加到manualSingletonNames
                this.manualSingletonNames.add(beanName);
            }
        }

        //清空allBeanNamesByType和singletonBeanNamesByType
        clearByTypeCache();
    }
}

DefaultSingletonBeanRegistry registra manualmente el Bean singleton
, agrega beanName a los Singletons registrados, guarda beanName y el objeto correspondiente en singletonObjects y elimina beanFactory y earlySingleton correspondientes a beanName.

//默认单例bean注册器
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
    
    
    
    //缓存单例bean, key为bean名称,value为bean实例
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

    //缓存beanFactory, key为bean名称, value为beanFactory
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

    //早期单例缓存, key为bean名称, value为bean实例
    //为了解决循环依赖而引入的
    private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

    //单例bean名称set
    private final Set<String> registeredSingletons = new LinkedHashSet<>(256);

    //正在创建的单例bean名称set
    private final Set<String> singletonsCurrentlyInCreation =
            Collections.newSetFromMap(new ConcurrentHashMap<>(16));


    //手工注册单例bean
    @Override
    public void registerSingleton(String beanName, Object singletonObject) throws IllegalStateException {
    
    
        //判断名称和值不可以为空
        Assert.notNull(beanName, "Bean name must not be null");
        Assert.notNull(singletonObject, "Singleton object must not be null");
        synchronized (this.singletonObjects) {
    
    
            //判断bean是否为空
            Object oldObject = this.singletonObjects.get(beanName);
            if (oldObject != null) {
    
    
                //不为空抛异常
                throw new IllegalStateException("Could not register object [" + singletonObject +
                        "] under bean name '" + beanName + "': there is already object [" + oldObject + "] bound");
            }
            //添加一个单例bean
            addSingleton(beanName, singletonObject);
        }
    }
    
    //添加一个单例bean
    protected void addSingleton(String beanName, Object singletonObject) {
    
    
        synchronized (this.singletonObjects) {
    
    
            //保存到singletonObjects的map中
            this.singletonObjects.put(beanName, singletonObject);
            this.singletonFactories.remove(beanName);
            this.earlySingletonObjects.remove(beanName);
            //添加beanName
            this.registeredSingletons.add(beanName);
        }
    }
}

Luego registre el frijol singleton y continúe con el análisis.

Establezca si se permitirá la anulación con el mismo nombre (setAllowBeanDefinitionOverriding), que es falso de forma predeterminada (valor predeterminado de la propiedad enableBeanDefinitionOverriding). Si es verdadero, los datos posteriores de BeanDefinition sobrescribirán los anteriores.
Insertar descripción de la imagen aquí

Agregue el posprocesador de carga diferida de beanFactory (addBeanFactoryPostProcessor). Dado que la carga diferida no está habilitada de forma predeterminada, el posprocesador de carga diferida no se agregará de forma predeterminada.
Insertar descripción de la imagen aquí

2.7 Inicializar BeanDefinitionLoader y cargar la aplicación

A continuación veamos un método de carga más importante.

Set<Object> sources = this.getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
this.load(context, sources.toArray(new Object[0]));

getAllSources obtiene el atributo primarioSources del objeto SpringApplication y a este atributo se le asigna un valor en el constructor SpringApplication, que es nuestra clase de inicio Demo3Application.class
Insertar descripción de la imagen aquí

Luego ingrese el método de carga.

protected void load(ApplicationContext context, Object[] sources) {
    
    
    if (logger.isDebugEnabled()) {
    
    
        logger.debug(
                "Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
    }
    //实例化BeanDefinitionLoader
    BeanDefinitionLoader loader = createBeanDefinitionLoader(
            getBeanDefinitionRegistry(context), sources);
    //this.beanNameGenerator为null
    if (this.beanNameGenerator != null) {
    
    
        loader.setBeanNameGenerator(this.beanNameGenerator);
    }
    //this.resourceLoader为null
    if (this.resourceLoader != null) {
    
    
        loader.setResourceLoader(this.resourceLoader);
    }
    //this.environment为null
    if (this.environment != null) {
    
    
        loader.setEnvironment(this.environment);
    }
    //调用load()方法,加载各个sources
    loader.load();
}

Primero, se genera un BeanDefinitionLoader para cargar las fuentes de variables miembro de SpringApplication. Solo hay un objeto Demo3Application.class en la lista de fuentes actual.

Primero cree un BeanDefinitionLoader a través del método createBeanDefinitionLoader, que puede cargar una clase en un BeanDefinition. El primer parámetro es el contenedor Spring y el segundo parámetro es nuestra clase de inicio.

Constructor BeanDefinitionLoader

  /**
 * 构造函数
   */
  BeanDefinitionLoader(BeanDefinitionRegistry registry, Object... sources) {
    
    
      Assert.notNull(registry, "Registry must not be null");
      Assert.notEmpty(sources, "Sources must not be empty");
      //传入的sources, 目前只有Demo3Application.class
      this.sources = sources;
      this.annotatedReader = new AnnotatedBeanDefinitionReader(registry);
      this.xmlReader = new XmlBeanDefinitionReader(registry);
      if (isGroovyPresent()) {
    
    
          //使用了groovy
          this.groovyReader = new GroovyBeanDefinitionReader(registry);
      }
      this.scanner = new ClassPathBeanDefinitionScanner(registry);
      //排除sources扫描
      this.scanner.addExcludeFilter(new ClassExcludeFilter(sources));
  }

En el método de construcción de BeanDefinitionLoader, se creará un objeto AnnotatedBeanDefinitionReader. Esta clase se creó una vez en el constructor del contenedor de resorte. El contenedor de resorte no se usa directamente aquí, pero se crea uno nuevo. La construcción del lector se repetirá el proceso, pero el método de registrar beans en el contenedor Spring se verifica para detectar nulidad antes de la ejecución, por lo que no habrá registro repetido, similar al siguiente código

if (!registry.containsBeanDefinition("org.springframework.context.annotation.internalAutowiredAnnotationProcessor")) {
    
    
    def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
    def.setSource(source);
    beanDefs.add(registerPostProcessor(registry, def, "org.springframework.context.annotation.internalAutowiredAnnotationProcessor"));
}

Volviendo al método de carga, las siguientes ramas if no entrarán. De forma predeterminada, beanNameGenerator, ResourceLoader y el entorno en SpringApplication son todos nulos. Tenga en cuenta que el entorno que realmente utilizamos se crea en el método de ejecución del objeto SpringApplication y no no asignado a su propia variable de entorno, por lo que aquí sigue siendo nulo

Ingrese la última línea del método de carga.

 /**
  * 加载sources
  */
 public int load() {
    
    
     int count = 0;
     for (Object source : this.sources) {
    
    
         count += load(source);
     }
     return count;
 }

En circunstancias normales, solo hay una clase de inicio, continúe siguiendo el método de carga

//加载Object资源
private int load(Object source) {
    
    
    Assert.notNull(source, "Source must not be null");
    if (source instanceof Class<?>) {
    
    
        //加载类资源
        return load((Class<?>) source);
    }
    if (source instanceof Resource) {
        //加载Resource资源
        return load((Resource) source);
    }
    if (source instanceof Package) {
        //加载Package资源
        return load((Package) source);
    }
    if (source instanceof CharSequence) {
        //加载字符串资源
        return load((CharSequence) source);
    }
    throw new IllegalArgumentException("Invalid source type " + source.getClass());
}

Nuestra clase de inicio es de tipo clase, toma la primera rama

//加载类资源
private int load(Class<?> source) {
    
    
    if (isGroovyPresent()
            && GroovyBeanDefinitionSource.class.isAssignableFrom(source)) {
    
    
        // 使用了groovy,加载groovy资源
        GroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source,
                GroovyBeanDefinitionSource.class);
        load(loader);
    }
    //如果有@Component注解
    if (isComponent(source)) {
    
    
        this.annotatedReader.register(source);
        return 1;
    }
    return 0;
}

El método isComponent determina si hay una anotación @Component en la clase de inicio. La clase de inicio está anotada con @SpringBootApplication. Es una anotación compuesta y contiene la anotación @Component internamente. Por lo tanto, esta rama se establece e ingresa al método de registro.

public class AnnotatedBeanDefinitionReader {
    
    

    //Class列表注册Bean定义
    public void register(Class<?>... annotatedClasses) {
    
    
        for (Class<?> annotatedClass : annotatedClasses) {
    
    
            //单个Bean注册
            registerBean(annotatedClass);
        }
    }
}

RegisterBean llama a doRegisterBean

public class AnnotatedBeanDefinitionReader {
    
    

     //单个Class注册bean
    public void registerBean(Class<?> annotatedClass) {
    
    
        doRegisterBean(annotatedClass, null, null, null);
    }
    
}

Finalmente, nuestra clase de inicio se convertirá en BeanDefinition y se registrará en el BeanDefinitionMap del contenedor Spring. Posteriormente, usaremos esto como punto de partida para escanear el Controlador, Servicio, etc. en el proyecto y registrarlos en el contenedor.

 //注册Bean定义
  <T> void doRegisterBean(Class<T> annotatedClass, @Nullable Supplier<T> instanceSupplier, @Nullable String name,
          @Nullable Class<? extends Annotation>[] qualifiers, BeanDefinitionCustomizer... definitionCustomizers) {
    
    
      //生成注解BeanDefinition
      AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
      
      //判断是否符合@Conditional注解的条件
      //不满足的话, 就不注册Bean
      if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
    
    
          return;
      }
      //设置instanceSupplier, //AbstractAutowireCapableBeanFactory#createBeanInstance中调用了instanceSupplier.get()生成bean实例
      abd.setInstanceSupplier(instanceSupplier);
      //Scope元空间
      ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
      abd.setScope(scopeMetadata.getScopeName());
      //生成Bean名称
      String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));

      //处理Lazy, Primary, DependsOn, Role, Description注解
      AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
      if (qualifiers != null) {
    
    
          for (Class<? extends Annotation> qualifier : qualifiers) {
    
    
              if (Primary.class == qualifier) {
    
    
                  abd.setPrimary(true);
              }
              else if (Lazy.class == qualifier) {
    
    
                  abd.setLazyInit(true);
              }
              else {
    
    
                  abd.addQualifier(new AutowireCandidateQualifier(qualifier));
              }
          }
      }
      for (BeanDefinitionCustomizer customizer : definitionCustomizers) {
    
    
          //beanDefinition定制器
          customizer.customize(abd);
      }

      //bean定义容器
      BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
      
      //Scope代理模式处理
      //ScopedProxyMode.DEFAULT和NO不需要代理处理
      //INTERFACES使用JDK动态代理
      //TARGET_CLASS使用CGLIB动态代理
      definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
      
      //注册Bean定义
      BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
  }

Una vez completada la ejecución, puede ver que nuestra clase de inicio se agregó al BeanDefinitionMap del contenedor Spring, y todas las clases anteriores se registraron en el contenedor durante el proceso de inicialización del AnnotatedBeanDefinitionReader interno cuando se creó el nuevo contenedor.
Insertar descripción de la imagen aquí

2.8 Lanzamiento del evento contextLoaded

Llame a listeners.contextLoaded (contexto) y publique un evento ApplicationPreparedEvent.

Al igual que el mecanismo de publicación de eventos anterior, finalmente se llama al método contextLoaded de EventPublishingRunListener.

public void contextLoaded(ConfigurableApplicationContext context) {
    
    
	for (ApplicationListener<?> listener : this.application.getListeners()) {
    
    
		if (listener instanceof ApplicationContextAware) {
    
    
			((ApplicationContextAware) listener).setApplicationContext(context);
		}
		context.addApplicationListener(listener);
	}
	this.initialMulticaster.multicastEvent(new ApplicationPreparedEvent(this.application, this.args, context));
}

Este bucle for atraviesa todos los oyentes del objeto SpringApplication, que es el ApplicationListener cargado desde META-INF/spring.factories cuando se creó SpringApplication por primera vez. En el bucle, se juzga si el oyente implementa la interfaz ApplicationContextAware. Si es así, si Entonces, asígnale el contenedor Spring.

Esta devolución de llamada de Aware se ejecuta originalmente durante el proceso de actualización del contenedor Spring, pero dado que el oyente aquí solo puede almacenarse en un atributo de lista del contenedor, no se registrará en el contenedor y no se administrará como un Bean. , no hay forma de activar la devolución de llamada de forma normal durante el proceso de actualización del contenedor Spring, por lo que asignamos el valor manualmente aquí.

Luego, en la última condición del bucle for, agréguelo a la lista de oyentes del contenedor de primavera. Hemos mencionado antes que después de que se inicia el contenedor, la función de publicación de eventos se transferirá al contenedor, y este es un paso importante. La lista de oyentes incorporada se entrega al contenedor y, con la lista de oyentes, los eventos se les pueden transmitir de forma natural.

Finalmente se publica el evento ApplicationPreparedEvent. El proceso de publicación es el mismo que antes. Hay cuatro oyentes de interés final:

  • ConfigFileApplicationListener
  • LoggingApplicationListener
  • FondoPreinicializador
  • DelegandoApplicationListener

2.8.1、ConfigFileApplicationListener

Escucha de archivos de configuración

public class ConfigFileApplicationListener
        implements EnvironmentPostProcessor, SmartApplicationListener, Ordered {
    
    
    //事件处理
    @Override
    public void onApplicationEvent(ApplicationEvent event) {
    
    
        if (event instanceof ApplicationEnvironmentPreparedEvent) {
    
    
            onApplicationEnvironmentPreparedEvent(
                    (ApplicationEnvironmentPreparedEvent) event);
        }
        if (event instanceof ApplicationPreparedEvent) {
    
    
            //处理ApplicationPreparedEvent
            onApplicationPreparedEvent(event);
        }
    }
    
    //处理ApplicationPreparedEvent
    private void onApplicationPreparedEvent(ApplicationEvent event) {
    
    
        this.logger.switchTo(ConfigFileApplicationListener.class);
        addPostProcessors(((ApplicationPreparedEvent) event).getApplicationContext());
    }
    
    //applicationContext中添加一个PropertySourceOrderingPostProcessor
    protected void addPostProcessors(ConfigurableApplicationContext context) {
    
    
        //用于重排序PropertySourceOrderingPostProcessor
        context.addBeanFactoryPostProcessor(
                new PropertySourceOrderingPostProcessor(context));
    }
}

2.8.2、LoggingApplicationListener

Oyente de registro

public class LoggingApplicationListener implements GenericApplicationListener {
    
    

    private void onApplicationPreparedEvent(ApplicationPreparedEvent event) {
    
    
        ConfigurableListableBeanFactory beanFactory = event.getApplicationContext()
                .getBeanFactory();
        //注册日志单例bean
        if (!beanFactory.containsBean(LOGGING_SYSTEM_BEAN_NAME)) {
    
    
            beanFactory.registerSingleton(LOGGING_SYSTEM_BEAN_NAME, this.loggingSystem);
        }
    }
    
}

2.8.3、Preinicializador de fondo

Preinicializador en segundo plano; actualmente no realiza procesamiento de tareas para facilitar la expansión futura.

2.8.4、Delegación de escucha de aplicaciones

Oyente proxy, no realiza ningún procesamiento, conveniente para futuras expansiones

3. Resumen

La función principal de este paso es prepararse para actualizar el contexto de la aplicación a continuación.

  • Contexto de aplicación unificado y entorno de aplicación
  • Configure beanNameGenerator, resouceLoader y classLoader de ApplicationContext y configure el servicio de conversión de tipos de beanFactory.
  • Ejecutar inicializador
  • Publicar ApplicationContextInitializedEvent
  • Imprimir registro de inicio y registro de perfil
  • Registre manualmente dos beans singleton, línea de comando y banner
  • Inicialice BeanDefinitionLoader y cargue fuentes de clases de inicio
  • Publicar evento contextLoaded

Supongo que te gusta

Origin blog.csdn.net/weixin_49114503/article/details/131762297
Recomendado
Clasificación