Diretório de artigos
- I. Introdução
- 2. prepareContexto
-
- 2.1、context.setEnvironment
- 2.2、postProcessApplicationContext(contexto);
- 2.3、applyInitializers(contexto)
- 2.4. Publique o evento ApplicationContextInitializedEvent
- 2.5. Imprimir registros de inicialização e perfil
- 2.6. Registrar Bean Singleton
- 2.7. Inicialize BeanDefinitionLoader e carregue o aplicativo
- 2.8. Liberar evento contextLoaded
- 3. Resumo
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
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
- Unifique ApplicationContext e ambiente usado pelo aplicativo
- Pós-processamento ApplicationContext
- Executar inicializadores
- Evento pós-contexto preparado
- Imprimir registros de inicialização e perfil
- Registrar um bean singleton
- Carregar classe de inicialização
- 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
- Definir beanNameGenerator de ApplicationContext
- Defina o resourceLoader e o classLoader do ApplicationContext
- 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.
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.
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.
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.
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
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.
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