Análise do código-fonte do SpringBoot (7) - prepareContext/prepare o contexto do aplicativo

I. Introdução

Este artigo é baseado na análise do código-fonte spring-boot-2.2.14.BUILD-SNAPSHOT do prepareContext para preparar o contexto do aplicativo.

2. prepareContexto

Seguindo o acima, este artigo continua a analisar o método run do SpringApplication e analisa a linha prepareContext dos
Insira a descrição da imagem aqui
parâmetros de solicitação de código:

Tipo de parâmetro Breve descrição dos parâmetros
Contexto ConfigurávelApplicationContext O valor de retorno do método createApplicationContext() representa o contexto do aplicativo
Ambiente configurável Classe de interface para informações de variáveis ​​de ambiente do sistema
Ouvintes SpringApplicationRunListeners Classe de coleção de SpringApplicationRunListener
AplicaçãoArgumentos aplicaçãoArgumentos Parâmetros de aplicação
Banner impressoBanner Informações do banner impresso

Insira a implementação do 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 o contexto da aplicação AnnotationConfigServletWebServerApplicationContext, foram executadas as 8 etapas a seguir

  1. Unifique ApplicationContext e ambiente usado pelo aplicativo
  2. Pós-processamento ApplicationContext
  3. Executar inicializadores
  4. Evento pós-contexto preparado
  5. Imprimir registros de inicialização e perfil
  6. Registrar um bean singleton
  7. Carregar classe de inicialização
  8. Pós evento contextLoaded

2.1、context.setEnvironment

Unifique ApplicationContext e ambiente usado pelo aplicativo

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);
    }
    
}

Substitua todos os ambientes relevantes no contexto pelo ambiente criado em SpringApplication. Você ainda se lembra das perguntas em " SpringBoot Source Code Analysis (5)-createApplicationContext para criar um contexto de aplicação "? A extensão é: havia dois ambientes em nossa aplicação antes, um no contexto e outro no SpringApplication. Após este método, apenas o ambiente em SpringApplication existirá, e o ambiente original no contexto será reciclado.

Nesse ponto, mencionamos uma falha no artigo anterior, pois embora o ambiente nativo do container seja substituído aqui, quando o SpringBootExceptionReporter foi inicializado antes, o ambiente nativo foi configurado para o analisador de exceções, e o ambiente mantido por esses analisadores não not Obter atualizações sincronizadas não é o objeto de ambiente que realmente usamos.

2.2、postProcessApplicationContext(contexto);

As três etapas a seguir foram executadas

  1. Definir beanNameGenerator de ApplicationContext
  2. Defina o resourceLoader e o classLoader do ApplicationContext
  3. Defina o serviço de conversão de tipo 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());
    }
}

Primeiro, verifique se existe um BeanNameGenerator personalizado no objeto SpringApplication. Em caso afirmativo, registre-o no pool singleton do contêiner. Este objeto é usado para gerar nomes para os beans no contêiner. Quando o novo contêiner Spring é lançado, ele irá gerar um por padrão. A estratégia de nomenclatura é colocar o nome da classe em letras minúsculas, mas o objeto em SpringApplication é nulo por padrão.

Em seguida, verifique se o objeto SpringApplication possui um ResourceLoader personalizado. Em caso afirmativo, atribua-o ao contêiner. Já analisamos isso antes e o padrão é nulo.

O último if branch, addConversionService é definido como true por padrão no construtor do objeto SpringApplication, portanto, se for usado, ele define um ConvertonService para o contêiner. Esta classe é usada para conversão de tipo, como String para Integer, etc. , na verdade já vi isso várias vezes em artigos anteriores.
Insira a descrição da imagem aqui

2.3、applyInitializers(contexto)

O que é carregado é a lista ApplicationContextInitializer em META-INF/spring.factories, e seu método de inicialização é chamado em sequência.

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);
	}
}

Os inicializadores são obtidos durante a inicialização do SpringApplication. Para obter o código-fonte, consulte " SpringBoot Source Code Analysis (2)-SpringBoot Startup Source Code (Ten Thousand Words Graphic Source Code Debug explica o princípio de inicialização do Springboot)" . de 7 inicializadores são obtidos:

  • DelegandoApplicationContextInitializer
  • SharedMetadataReaderFactoryContextInitializer
  • ContextIdApplicationContextInitializer
  • ConfigurationWarningsApplicationContextInitializer
  • ServerPortInfoApplicationContextInitializer
  • ConditionEvaluationReportLoggingListener
  • RSocketPortInfoApplicationContextInitializer

Este artigo primeiro classifica o contexto do método prepareContext. Quanto à inicialização feita por esses ApplicationContextInitializers integrados, iremos analisá-la separadamente em nosso próximo artigo " SpringBoot Source Code Analysis (8)-Built-in ApplicationContextInitializer "

Todas essas classes de inicialização não executam a operação substantiva de iniciar o serviço. Todas elas registram objetos e enterram o ponto. O método de inicialização é na verdade chamado mais tarde por InvokeBeanFactoryPostProcessors e antes do início do projeto.

2.4. Publique o evento ApplicationContextInitializedEvent

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

Evento de conclusão de inicialização do contêiner de aplicativo, os ouvintes interessados ​​neste evento são

  • Pré-inicializador de fundo
  • DelegandoApplicationListener

Ponto de extensão BackgroundPreinitializer
, inicializador de processo em segundo plano, usado para execução multithread de tarefas demoradas em segundo plano, o evento ApplicationContextInitializedEvent não é processado aqui

O ponto de extensão DelegatingApplicationListener
, ouvinte de proxy, continua a distribuir eventos, não manipula o evento ApplicationContextInitializedEvent

2.5. Imprimir registros de inicialização e 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 se o contêiner atual possui um contêiner pai. Caso contrário, ele será considerado o contêiner raiz para inicialização do projeto e uma linha de log será impressa, incluindo classe de inicialização, nome do servidor atual, caminho do projeto, 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 Bean Singleton

Registrou dois beans singleton

  • Bean de parâmetro de linha de comando, o nome é springApplicationArguments, o valor é applicationArgument
  • banner bean, o nome é springBootBanner, o valor é printBanner
//注册命令行参数bean
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
    
    
    //banner bean
    beanFactory.registerSingleton("springBootBanner", printedBanner);
}

Finalmente, o método registerSingleton irá registrá-los no contêiner singletonObjects. Como podemos ver pelo nome, este é um contêiner para armazenar objetos singleton.
Insira a descrição da imagem aqui

2.6.1. Registro manual do processo Bean singleton

Chame o método DefaultListableBeanFactory#registerSingleton e chame explicitamente o método DefaultSingletonBeanRegistry#registerSingleton da classe pai

DefaultListableBeanFactory Registrando manualmente um bean singleton
Registrar manualmente um bean singleton é diferente de verificar a definição do bean e depois registrar o bean singleton. O bean singleton registrado manualmente não é mantido no beanDefinitionMap, mas o beanName é mantido em 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 o singleton Bean
, adiciona beanName a RegisteredSingletons, salva beanName e o objeto correspondente em singletonObjects e exclui beanFactory e earlySingleton correspondentes 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);
        }
    }
}

Em seguida, registre o bean singleton e continue a análise.

Defina se será permitida a substituição com o mesmo nome (setAllowBeanDefinitionOverriding), que é falso por padrão (valor padrão da propriedade allowBeanDefinitionOverriding). Se for verdade, os dados BeanDefinition subsequentes substituirão os anteriores.
Insira a descrição da imagem aqui

Adicionar pós-processador de carregamento lento beanFactory (addBeanFactoryPostProcessor).Como o carregamento lento não está habilitado por padrão, o pós-processador de carregamento lento não será adicionado por padrão.
Insira a descrição da imagem aqui

2.7. Inicialize BeanDefinitionLoader e carregue o aplicativo

A seguir, vamos examinar um método de carregamento mais importante

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

getAllSources obtém o atributo PrimarySources do objeto SpringApplication, e esse atributo recebe um valor no construtor SpringApplication, que é nossa classe de inicialização Demo3Application.class
Insira a descrição da imagem aqui

Em seguida, insira o método de carregamento

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();
}

Primeiro, um BeanDefinitionLoader é gerado para carregar as fontes de variáveis ​​​​de membro de SpringApplication.Há apenas um objeto de Demo3Application.class na lista de fontes atual.

Primeiro, crie um BeanDefinitionLoader por meio do método createBeanDefinitionLoader, que pode carregar uma classe em um BeanDefinition. O primeiro parâmetro é o contêiner Spring e o segundo parâmetro é nossa classe de inicialização.

Construtor 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));
  }

No método de construção de BeanDefinitionLoader, um objeto AnnotatedBeanDefinitionReader será criado. Esta classe foi criada uma vez no construtor do contêiner spring. O contêiner spring não é usado diretamente aqui, mas um novo é criado. A construção do Reader será ser repetido. processo, mas o método de registro de beans no contêiner Spring é verificado quanto à nulidade antes da execução, portanto, não haverá registro repetido, semelhante ao código a seguir

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"));
}

De volta ao método de carregamento, as próximas ramificações if não entrarão. Por padrão, o beanNameGenerator, o resourceLoader e o ambiente em SpringApplication são todos nulos. Observe que o ambiente que realmente usamos é criado no método run do objeto SpringApplication e não não atribuído à sua própria variável de ambiente, portanto ainda é nulo aqui

Insira a última linha do método load

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

Em circunstâncias normais, há apenas uma classe de inicialização, continue seguindo o método de carregamento

//加载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());
}

Nossa classe de inicialização é do tipo classe, pegue a primeira ramificação

//加载类资源
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;
}

O método isComponent determina se existe uma anotação @Component na classe de inicialização. A classe de inicialização é anotada com @SpringBootApplication. É uma anotação composta e contém a anotação @Component internamente. Portanto, esta ramificação é estabelecida e entra no método de registro.

public class AnnotatedBeanDefinitionReader {
    
    

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

RegisterBean chama doRegisterBean

public class AnnotatedBeanDefinitionReader {
    
    

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

Por fim, nossa classe de inicialização será convertida em BeanDefinition e registrada no BeanDefinitionMap do contêiner Spring. Posteriormente, usaremos isso como ponto de partida para escanear o Controlador, Serviço, etc.

 //注册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);
  }

Após a conclusão da execução, você pode ver que nossa classe de inicialização foi adicionada ao BeanDefinitionMap do contêiner Spring, e as classes anteriores foram todas registradas no contêiner durante o processo de inicialização do AnnotatedBeanDefinitionReader interno quando o novo contêiner foi criado.
Insira a descrição da imagem aqui

2.8. Liberar evento contextLoaded

Chame listeners.contextLoaded(context), publicando um evento ApplicationPreparedEvent.

Assim como o mecanismo de publicação de eventos anterior, o método contextLoaded de EventPublishingRunListener é finalmente chamado.

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 loop for percorre todos os ouvintes do objeto SpringApplication, que é o ApplicationListener carregado de META-INF/spring.factories quando SpringApplication foi criado pela primeira vez. No loop, é julgado se o Listener implementa a interface ApplicationContextAware. Em caso afirmativo, If então, atribua o contêiner Spring a ele

Este retorno de chamada do Aware é executado originalmente durante o processo de atualização do contêiner Spring, mas como o ouvinte aqui só pode ser armazenado em um atributo de lista do contêiner, ele não será registrado no contêiner e não será gerenciado como um Bean. , não há como acionar o retorno de chamada normalmente durante o processo de atualização do contêiner Spring, portanto, atribuímos o valor manualmente aqui.

Em seguida, na última condição do loop for, adicione-o à lista de ouvintes do contêiner Spring. Mencionamos antes que após o contêiner ser iniciado, a função de publicação de eventos será transferida para o contêiner, e esta é uma etapa importante. A lista de ouvintes integrada é fornecida ao contêiner. Com a lista de ouvintes, os eventos podem ser transmitidos naturalmente para eles.

Por fim, o evento ApplicationPreparedEvent é publicado. O processo de publicação é o mesmo de antes. Existem quatro listeners de interesse final:

  • ConfigFileApplicationListener
  • LoggingApplicationListener
  • Pré-inicializador de fundo
  • DelegandoApplicationListener

2.8.1、ConfigFileApplicationListener

Ouvinte de arquivo de configuração

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

Ouvinte 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、Pré-inicializador de fundo

Pré-inicializador em segundo plano, atualmente não executa processamento de tarefas, para facilitar expansão futura

2.8.4、DelegandoApplicationListener

Ouvinte proxy, não faz nenhum processamento, conveniente para expansão futura

3. Resumo

A principal função desta etapa é preparar a atualização do applicationContext abaixo.

  • ApplicationContext unificado e ambiente de aplicativo
  • Defina beanNameGenerator, resouceLoader e classLoader de ApplicationContext, - e defina o serviço de conversão de tipo de beanFactory
  • Executar inicializador
  • Postar ApplicationContextInitializedEvent
  • Imprimir log de inicialização e log de perfil
  • Registre manualmente dois beans singleton, linha de comando e banner
  • Inicialize BeanDefinitionLoader e carregue fontes de classe de inicialização
  • Pós evento contextLoaded

Acho que você gosta

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