SpringBoot源码分析(7)--prepareContext/准备应用上下文

news/2024/11/29 18:27:11/

文章目录

  • 一、前言
  • 二、prepareContext
    • 2.1、context.setEnvironment
    • 2.2、postProcessApplicationContext(context);
    • 2.3、applyInitializers(context)
    • 2.4、发布ApplicationContextInitializedEvent事件
    • 2.5、打印启动和profile日志
    • 2.6、注册单例Bean
      • 2.6.1、手工注册单例Bean流程
    • 2.7、初始化BeanDefinitionLoader, 加载Application
    • 2.8、发布contextLoaded事件
      • 2.8.1、ConfigFileApplicationListener
      • 2.8.2、LoggingApplicationListener
      • 2.8.3、BackgroundPreinitializer
      • 2.8.4、DelegatingApplicationListener
  • 三、总结

一、前言

本文基于spring-boot-2.2.14.BUILD-SNAPSHOT源码分析prepareContext准备应用上下文这一步骤。

二、prepareContext

承接上文,本文继续SpringApplication的run方法往下分析,看prepareContext这行代码
在这里插入图片描述
请求参数:

参数类型参数简要说明
ConfigurableApplicationContext contextcreateApplicationContext()方法的返回值,代表应用上下文
ConfigurableEnvironment environment系统的环境变量信息的接口类
SpringApplicationRunListeners listenersSpringApplicationRunListener的集合类
ApplicationArguments applicationArguments应用参数
Banner printedBanner打印的Banner信息

进入方法实现:

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {// 1、设置环境对象//统一ApplicationContext和Application,使用Application的environmentcontext.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 beansConfigurableListableBeanFactory 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);
}

准备应用上下文环境AnnotationConfigServletWebServerApplicationContext, 执行了以下8个步骤

  1. 统一ApplicationContext和Application使用的environment
  2. 后置处理ApplicationContext
  3. 执行Initializers
  4. 发布contextPrepared事件
  5. 打印启动和profile日志
  6. 注册单例bean
  7. 加载启动类
  8. 发布contextLoaded事件

2.1、context.setEnvironment

统一ApplicationContext和Application使用的environment

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

将context中相关的environment全部替换成SpringApplication中创建的environment。还记得《SpringBoot源码分析(5)–createApplicationContext创建应用上下文》中的疑问吗,引申下就是:之前我们的应用中有两个environment,一个在context中,一个在SpringApplication中。经过此方法后,就只会存在SpringApplication中的environment了,而context中的原environment会被回收。

关于这点我们上篇有提到过一个瑕疵,因为这里虽然替换了容器原生的environement,但之前初始化SpringBootExceptionReporter的时候,已经把原生的environment设置到了异常分析器中,这些分析器持有的environment没有得到同步的更新,并不是我们真正使用的环境对象。

2.2、postProcessApplicationContext(context);

执行了以下三步

  1. 设置ApplicationContext的beanNameGenerator
  2. 设置ApplicationContext的resourceLoader和classLoader
  3. 设置ApplicationContext的类型转换Service
protected void postProcessApplicationContext(ConfigurableApplicationContext context) {//beanNameGenerator默认为null,所以此处没有设置if (this.beanNameGenerator != null) {//如果beanNameGenerator不为空//那么注册一个名为internalConfigurationBeanNameGenerator//值为beanNameGenerator的单例beancontext.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默认为trueif (this.addConversionService) {//设置类型转换Servicecontext.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance());}
}

首先看SpringApplication对象中有没有自定义的BeanNameGenerator,有的话就注册到容器的单例池,这个对象是用来给容器中的Bean生成名字的,Spring容器new出来的时候会默认生成一个,默认的命名策略就是类名小写,不过SpringApplication中的该对象默认是null的

然后看SpringApplication对象有没有自定义ResourceLoader,有的话就赋值给容器,这个我们之前也分析过,默认也是null的

最后一个if分支,addConversionService在SpringApplication对象的构造函数里就默认设置为true,所以会走if,它为容器设置了一个ConversonService,这个类是用来做类型转换的,比如String转Integer等等,其实在之前的文章中已经见过几次了
在这里插入图片描述

2.3、applyInitializers(context)

加载的是META-INF/spring.factories中的ApplicationContextInitializer列表 ,并依次调用其initialize方法

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

initializers在SpringApplication初始化期间获取到,获取源码可参考《SpringBoot源码分析(2)–SpringBoot启动源码(万字图文源码debug讲解springboot启动原理)》, 一共获取到7个初始化器:

  • DelegatingApplicationContextInitializer
  • SharedMetadataReaderFactoryContextInitializer
  • ContextIdApplicationContextInitializer
  • ConfigurationWarningsApplicationContextInitializer
  • ServerPortInfoApplicationContextInitializer
  • ConditionEvaluationReportLoggingListener
  • RSocketPortInfoApplicationContextInitializer

本文先梳理prepareContext方法的脉络,至于这些内置的ApplicationContextInitializer做了哪些初始化,我们下篇文章《SpringBoot源码分析(8)–内置ApplicationContextInitializer》单独分析

所有的这些初始化类都没有进行启动服务的实质性操作,都是通过注册对象,埋点,后面invokeBeanFactoryPostProcessors才真正调用初始化方法,而且在项目启动之前

2.4、发布ApplicationContextInitializedEvent事件

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

Application容器初始化完成事件, 对该事件感兴趣的监听器有

  • BackgroundPreinitializer
  • DelegatingApplicationListener

BackgroundPreinitializer
扩展点, 后台进程初始化器, 用于多线程执行后台耗时任务, 在这里不处理ApplicationContextInitializedEvent事件

DelegatingApplicationListener
扩展点, 代理监听器, 继续分发事件, 不处理ApplicationContextInitializedEvent事件

2.5、打印启动和profile日志

//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: defaultlogStartupProfileInfo(context);
}

这段代码判断当前容器是否有父容器,如果没有的话就认为是项目启动的根容器,会打印一行日志,包括启动类、当前的服务器名、项目路径、PID等

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、注册单例Bean

注册了两个单例Bean

  • 命令行参数bean, 名称为springApplicationArguments, 值为applicationArgument
  • banner bean, 名称为springBootBanner, 值为printedBanner
//注册命令行参数bean
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {//banner beanbeanFactory.registerSingleton("springBootBanner", printedBanner);
}

最终registerSingleton方法会把他们注册到singletonObjects容器中,从名字我们就可以看出来,这是个存放单例对象的容器。
在这里插入图片描述

2.6.1、手工注册单例Bean流程

调用DefaultListableBeanFactory#registerSingleton方法, 显示调用父类DefaultSingletonBeanRegistry#registerSingleton方法

DefaultListableBeanFactory 手工注册单例Bean
手工注册单例Bean, 不同于扫描bean定义, 然后注册单例bean, 手工注册的单例Bean, 没有维护到beanDefinitionMap中, 而是将beanName维护到manualSingletonNames中

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactoryimplements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {//注册单例beanpublic void registerSingleton(String beanName, Object singletonObject) throws IllegalStateException {super.registerSingleton(beanName, singletonObject);//判断bean的创建过程是否已经开始了//调用抽象父类AbstractBeanFactory#hasBeanCreationStarted()方法//判断AbstractBeanFactory成员变量alreadyCreated Set不为空if (hasBeanCreationStarted()) {//bean创建过程已经开始了//锁住成员变量beanDefinitionMapsynchronized (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//那么添加到manualSingletonNamesthis.manualSingletonNames.add(beanName);}}//清空allBeanNamesByType和singletonBeanNamesByTypeclearByTypeCache();}
}

DefaultSingletonBeanRegistry手工注册单例Bean
将beanName添加到registeredSingletons中, beanName和对应的对象保存singletonObjects中, 并删除beanName对应的beanFactory, earlySingleton

//默认单例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为beanFactoryprivate final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);//早期单例缓存, key为bean名称, value为bean实例//为了解决循环依赖而引入的private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);//单例bean名称setprivate final Set<String> registeredSingletons = new LinkedHashSet<>(256);//正在创建的单例bean名称setprivate final Set<String> singletonsCurrentlyInCreation =Collections.newSetFromMap(new ConcurrentHashMap<>(16));//手工注册单例bean@Overridepublic 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");}//添加一个单例beanaddSingleton(beanName, singletonObject);}}//添加一个单例beanprotected void addSingleton(String beanName, Object singletonObject) {synchronized (this.singletonObjects) {//保存到singletonObjects的map中this.singletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName);this.earlySingletonObjects.remove(beanName);//添加beanNamethis.registeredSingletons.add(beanName);}}
}

接着注册单例bean继续往下分析。

设置是否允许同名覆盖(setAllowBeanDefinitionOverriding),默认情况下为false(allowBeanDefinitionOverriding属性默认值)。如果为true,后面的BeanDefinition数据会将前面的覆盖掉。
在这里插入图片描述

添加beanFactory懒加载后置处理器(addBeanFactoryPostProcessor),由于默认情况下并未启动懒加载,所以默认情况下懒加载后置处理器也不会被添加
在这里插入图片描述

2.7、初始化BeanDefinitionLoader, 加载Application

接下来看一个比较重要的load方法

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

getAllSources获取的是SpringApplication对象的primarySources属性,而该属性在SpringApplication构造函数中赋了值,也就是我们的启动类Demo3Application.class
在这里插入图片描述

接下来进入load方法

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

首先生成一个BeanDefinitionLoader, 用于加载SpringApplication的成员变量sources, 当前sources列表中只有Demo3Application.class一个对象。

先通过createBeanDefinitionLoader方法创建一个BeanDefinitionLoader,它可以将一个类加载成BeanDefinition,第一个参数就是spring容器,第二个参数是我们的启动类。

BeanDefinitionLoader构造方法

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

在BeanDefinitionLoader的构造方法中,会创建一个AnnotatedBeanDefinitionReader对象,这个类在spring容器的构造函数中已经创建过一次了,这里没有直接使用spring容器的,而是又新建了一个,会重复走一遍Reader的构造流程,但是其中往spring容器注册bean的方法执行前都做了判空的校验,所以不会重复注册,类似如下代码

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

回到load方法,接下来几个if分支都不会进,默认情况下SpringApplication中的beanNameGenerator、resourceLoader、environment都是null,注意我们真正使用的environment是在SpringApplication对象的run方法中创建的,并没有赋值给它自己的environment变量,所以这里依然是null

进入最后一行load方法

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

正常情况下启动类只有一个,继续跟进load方法

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

我们启动类是class类型,走第一个分支

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

isComponent方法判断启动类上是否有@Component注解,启动类加了@SpringBootApplication注解,它是一个复合注解,内部包含了@Component注解,所以这个分支成立,进入register方法

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

registerBean调用doRegisterBean

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

最终将我们的启动类转化为BeanDefinition注册到spring容器的BeanDefinitionMap中,后续会以此为起点,开始扫描项目中的Controller、Service等等注册到容器中

 //注册Bean定义<T> void doRegisterBean(Class<T> annotatedClass, @Nullable Supplier<T> instanceSupplier, @Nullable String name,@Nullable Class<? extends Annotation>[] qualifiers, BeanDefinitionCustomizer... definitionCustomizers) {//生成注解BeanDefinitionAnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);//判断是否符合@Conditional注解的条件//不满足的话, 就不注册Beanif (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);}

执行完毕后,可以看到Spring容器的BeanDefinitionMap中,已经添加了我们的启动类,而前面几个类都是在new容器的时候,内部AnnotatedBeanDefinitionReader初始化的过程中注册到容器里的
在这里插入图片描述

2.8、发布contextLoaded事件

调用listeners.contextLoaded(context), 发布了一个ApplicationPreparedEvent事件。

跟之前的事件发布机制一样,最终调用了EventPublishingRunListener的contextLoaded方法

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

这个for循环,遍历了SpringApplication对象所有的监听器,也就是最开始创建SpringApplication的时候,从META-INF/spring.factories中加载到的ApplicationListener,在循环中,判断Listener是否实现了ApplicationContextAware接口,如果是的话就把Spring容器赋给他

这个Aware的回调本来也是在Spring容器refresh的过程执行的,但是由于这里的监听器可能仅仅是存储在容器的一个列表属性里,而并不会注册到容器中,也就不会作为Bean管理起来,后续就没办法真正在spring容器的refresh过程以正常的方式触发回调,所以就在这里手动赋值了

然后在for循环的最后一个条件里,将其添加到spring容器的监听器列表,我们之前有提到过,容器启动后,事件发布的职能会转交给容器进行,而这里正是重要的一步,将内置的监听器列表交给了容器,有了监听器列表, 自然可以向它们广播事件了

最后发布事件ApplicationPreparedEvent,发布流程跟之前一样,最终感兴趣的监听器有四个:

  • ConfigFileApplicationListener
  • LoggingApplicationListener
  • BackgroundPreinitializer
  • DelegatingApplicationListener

2.8.1、ConfigFileApplicationListener

配置文件监听器

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

2.8.2、LoggingApplicationListener

日志监听器

public class LoggingApplicationListener implements GenericApplicationListener {private void onApplicationPreparedEvent(ApplicationPreparedEvent event) {ConfigurableListableBeanFactory beanFactory = event.getApplicationContext().getBeanFactory();//注册日志单例beanif (!beanFactory.containsBean(LOGGING_SYSTEM_BEAN_NAME)) {beanFactory.registerSingleton(LOGGING_SYSTEM_BEAN_NAME, this.loggingSystem);}}}

2.8.3、BackgroundPreinitializer

后台预初始化器, 当前不做任务处理, 方便以后扩展

2.8.4、DelegatingApplicationListener

代理监听器, 不做任何处理, 方便以后扩展

三、总结

这一步的主要作用是为下面刷新applicationContext做准备

  • 统一了ApplicationContext和Application的environment
  • 设置ApplicationContext的beanNameGenerator,resouceLoader和classLoader, - 并设置beanFactory的类型转换Service
  • 执行Initializer
  • 发布ApplicationContextInitializedEvent
  • 打印启动日志和profile日志
  • 手工注册命令行和banner两个单例Bean
  • 初始化BeanDefinitionLoader, 加载启动类sources
  • 发布contextLoaded事件

http://www.ppmy.cn/news/1011159.html

相关文章

SQL注入实操三(SQLilabs Less41-65)

文章目录 一、sqli-labs靶场1.轮子模式总结2.Less-41 stacked Query Intiger type blinda.注入点判断b.轮子测试c.获取数据库名称d.堆叠注入e.堆叠注入外带注入获取表名f.堆叠注入外带注入获取列名g.堆叠注入外带注入获取表内数据 3.Less-42 Stacked Query error baseda.注入点…

kubernetes网络之网络策略-----Network Policies - Example

创建一个Deployment并配置Service 创建一个 nginx Deployment 用于演示 Kubernetes 的 NetworkPolicy&#xff1a; kubectl create deployment nginx --imagenginx 输出结果 deployment.apps/nginx created通过Service暴露该Deployment kubectl expose deployment nginx --po…

【stream的使用】使用stream.filter过滤List对象

Stream初相识 概括讲&#xff0c;可以将Stream流操作分为3种类型&#xff1a; 创建Stream Stream中间处理 终止Steam 每个Stream管道操作类型都包含若干API方法&#xff0c;先列举下各个API方法的功能介绍。 开始管道 主要负责新建一个Stream流&#xff0c;或者基于现有的数组…

ReactNative 学习笔记

学习使用的开发工具 编译器 VSCode 开发语言工具 TypeScript 重要程度分类 一般 这个程度的知识点主要是达到熟练掌握即可&#xff0c;不用太深入研究和学习。 重要 这个程度的知识点主要是达到熟练掌握&#xff0c;并且内部的原理切要熟记&#xff0c;因为会关联到其他的知…

【二分查找】74. 搜索二维矩阵

74. 搜索二维矩阵 解题思路 方法1 将二维数组转换为一维数组使用二分查找 class Solution {public boolean searchMatrix(int[][] matrix, int target) {// 使用二分查找// 将矩阵写入一个数组中 然后使用二分查找算法int[] a new int[matrix.length * matrix[0].length];i…

STM32--综述

文章目录 前言STM32简介STM32F103C8T6系统结构Keil软件安装注意事项新建工程操作流程 前言 本专栏将学习B站江协科技的STM32入门教程&#xff0c;通过自身理解和对老师的总结所写的博客专栏。 STM32简介 STM32是意法半导体&#xff08;STMicroelectronics&#xff09;公司推…

当进行一个npm包开发时,依赖管理的重要性

npm install 的时候会进行什么&#xff1f; 当一个项目被拉下来并执行npm install的时候&#xff0c;其实dependencies 和 devDependencies都会被安装。 如果项目有严格区分生产、开发环境的话&#xff0c;是可以通过--production来以只安装 dependencies 字段的模块。 作为…

用栈实现队列(JS)

用栈实现队列 题目 请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作&#xff08;push、pop、peek、empty&#xff09;&#xff1a; 实现 MyQueue 类&#xff1a; void push(int x) 将元素 x 推到队列的末尾int pop() 从队列的开头移除并返回元素int…