SpringBoot启动流程分析之准备应用上下文refreshContext()

文章目录

        • 源码入口
        • 1、准备刷新
          • 1.1、子类prepareRefresh()方法
          • 1.2 父类prepareRefresh()方法
        • 2、通知子类刷新内部bean工厂
        • 3、准备bean工厂
        • 4、允许上下文子类对bean工厂进行后置处理

源码入口

org.springframework.boot.SpringApplication#run(java.lang.String…)

public ConfigurableApplicationContext run(String... args) {.... try {//本篇内容从本行开始记录  refreshContext(context);//本篇内容记录到这,后续更新....}catch (Throwable ex) {handleRunFailure(context, ex, exceptionReporters, listeners);throw new IllegalStateException(ex);}
}

流程分析

private void refreshContext(ConfigurableApplicationContext context) {refresh(context);if (this.registerShutdownHook) {try {context.registerShutdownHook();}catch (AccessControlException ex) {// Not allowed in some environments.}}}

最终调用父类AbstractApplicationContext的refresh()方法。

protected void refresh(ApplicationContext applicationContext) {Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);((AbstractApplicationContext) applicationContext).refresh();}@Override
public final void refresh() throws BeansException, IllegalStateException {		try {super.refresh();}catch (RuntimeException ex) {stopAndReleaseWebServer();throw ex;}
}

可以看到refresh中的步骤都是单个单个的方法

如果看过Spring源码的同学现在应该很熟悉了…
org.springframework.context.support.AbstractApplicationContext#refresh

@Override
public void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {// 准备刷新prepareRefresh();// 通知子类刷新内部bean工厂ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();// 准备bean工厂以便在此上下文中使用prepareBeanFactory(beanFactory);try {// 允许上下文子类中对bean工厂进行后处理postProcessBeanFactory(beanFactory);// 在bean创建之前调用BeanFactoryPostProcessors后置处理方法invokeBeanFactoryPostProcessors(beanFactory);// 注册BeanPostProcessorregisterBeanPostProcessors(beanFactory);// 注册DelegatingMessageSourceinitMessageSource();// 注册multicasterinitApplicationEventMulticaster();// 创建内置的Servlet容器onRefresh();// 注册ListenerregisterListeners();// 完成BeanFactory初始化,初始化剩余单例beanfinishBeanFactoryInitialization(beanFactory);// 发布对应事件finishRefresh();}catch (BeansException ex) {if (logger.isWarnEnabled()) {logger.warn("Exception encountered during context initialization - " +"cancelling refresh attempt: " + ex);}// Destroy already created singletons to avoid dangling resources.destroyBeans();// Reset 'active' flag.cancelRefresh(ex);// Propagate exception to caller.throw ex;}finally {// Reset common introspection caches in Spring's core, since we// might not ever need metadata for singleton beans anymore...resetCommonCaches();}}
}

开始之前先把AnnotationConfigServletWebServerApplicationContext类图放这:
在这里插入图片描述

1、准备刷新

先调用子类重写的方法,再调用父类方法。还记得前面讲过在创建AnnotationConfigServletWebServerApplicationContext的时候构造方法中实例化了一个ClassPathBeanDefinitionScanner。

@Override
protected void prepareRefresh() {this.scanner.clearCache();super.prepareRefresh();
}
1.1、子类prepareRefresh()方法

在其父类ClassPathScanningCandidateComponentProvider中有一个MetadataReaderFactory(接口)工厂对象,判断该对象是否是CachingMetadataReaderFactory这个特定类或者是它的子类的一个实例,返回Boolean值,是就是true,则清除缓存。该类中有个Map,用来缓存每个Spring资源句柄(即每个“.class”文件)的MetadataReader实例。

@Nullable
private Map<Resource, MetadataReader> metadataReaderCache;/*** Clear the local MetadataReader cache, if any, removing all cached class metadata.*/
public void clearCache() {if (this.metadataReaderCache instanceof LocalResourceCache) {synchronized (this.metadataReaderCache) {this.metadataReaderCache.clear();}}else if (this.metadataReaderCache != null) {// Shared resource cache -> reset to local cache.setCacheLimit(DEFAULT_CACHE_LIMIT);}
}

缓存Map如果是LocalResourceCache(可以看到该类继承了LinkedHashMap),执行的就是LinkedHashMap的clear()方法了

else如果缓存不为空,就是重新new一个LocalResourceCache

 * Specify the maximum number of entries for the MetadataReader cache.* <p>Default is 256 for a local cache, whereas a shared cache is* typically unbounded. This method enforces a local resource cache,* even if the {@link ResourceLoader} supports a shared resource cache.*/
public void setCacheLimit(int cacheLimit) {if (cacheLimit <= 0) {this.metadataReaderCache = null;}else if (this.metadataReaderCache instanceof LocalResourceCache) {((LocalResourceCache) this.metadataReaderCache).setCacheLimit(cacheLimit);}else {this.metadataReaderCache = new LocalResourceCache(cacheLimit);}
}/*** Clear the local MetadataReader cache, if any, removing all cached class metadata.*/
public void clearCache() {if (this.metadataReaderCache instanceof LocalResourceCache) {synchronized (this.metadataReaderCache) {this.metadataReaderCache.clear();}}else if (this.metadataReaderCache != null) {// Shared resource cache -> reset to local cache.setCacheLimit(DEFAULT_CACHE_LIMIT);}
}@SuppressWarnings("serial")
private static class LocalResourceCache extends LinkedHashMap<Resource, MetadataReader> {private volatile int cacheLimit;public LocalResourceCache(int cacheLimit) {super(cacheLimit, 0.75f, true);this.cacheLimit = cacheLimit;}public void setCacheLimit(int cacheLimit) {this.cacheLimit = cacheLimit;}public int getCacheLimit() {return this.cacheLimit;}@Overrideprotected boolean removeEldestEntry(Map.Entry<Resource, MetadataReader> eldest) {return size() > this.cacheLimit;}
}
1.2 父类prepareRefresh()方法
protected void prepareRefresh() {//系统启动时间this.startupDate = System.currentTimeMillis();//是否关闭标识,falsethis.closed.set(false);//是否活跃标识,truethis.active.set(true);if (logger.isDebugEnabled()) {if (logger.isTraceEnabled()) {logger.trace("Refreshing " + this);}else {logger.debug("Refreshing " + getDisplayName());}}// 调用子类GenericWebApplicationContext重写后的方法替换servlet相关属性源initPropertySources();// 这里是验证由ConfigurablePropertyResolver#setRequiredProperties()方法指定的属性,解析为非空值,如果没有设置的话这个方法就不会执行什么操作。getEnvironment().validateRequiredProperties();// Allow for the collection of early ApplicationEvents,// to be published once the multicaster is available...this.earlyApplicationEvents = new LinkedHashSet<>();
}

在这里插入图片描述在这里插入图片描述

看下最后这个方法,initServlet属性源,方法注释是这么说的将基于Servlet的StubPropertySource替换为使用给定servletContext和servletConfig对象填充的实例。此方法可以被调用任意次数,但将用相应的实际属性源替换为StubPropertySource一次且仅一次。

看下if判断里的条件,servletContext不为空,source中存在指定name的的属性源,且该属性源要是StubPropertySource的类型或者是其子类。也就是当第一次调用以后,该属性源就被替换成了ServletContextPropertySource和ServletConfigPropertySource,所以之后的调用最后一个判断条件就不会成立了。

public static void initServletPropertySources(MutablePropertySources sources,@Nullable ServletContext servletContext, @Nullable ServletConfig servletConfig) {Assert.notNull(sources, "'propertySources' must not be null");//servletContextInitParamsString name = StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME;if (servletContext != null && sources.contains(name) && sources.get(name) instanceof StubPropertySource) {sources.replace(name, new ServletContextPropertySource(name, servletContext));}//servletConfigInitParamsname = StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME;if (servletConfig != null && sources.contains(name) && sources.get(name) instanceof StubPropertySource) {sources.replace(name, new ServletConfigPropertySource(name, servletConfig));}
}

看validateRequiredProperties()方法前,先看下Environment的类图。这里是验证由ConfigurablePropertyResolver#setRequiredProperties()方法指定的属性,解析为非空值,如果没有设置的话这个方法就不会执行什么操作,所以这里就略过了。

方法最后new了一个LinkedHashSet,收集早期事件,如果multicaster 有用就会广播事件。prepareRefresh()就执行完了。

2、通知子类刷新内部bean工厂

可以看到refreshBeanFactory()方法上的注释说的,什么都不做:我们拥有一个内部beanfactory,并依靠调用方通过我们的公共方法(或beanfactory)注册bean。

前面介绍GenericApplicationContext说了与每次刷新创建新的内部beanfactory实例的其他applicationContext实现不同,此上下文的内部beanfactory从一开始就可用,以便能够在其上注册bean定义。只能调用一次refresh()。

所以在该方法内只设置了SerializationId,该id是在准备应用上下文时调用ContextIdApplicationContextInitializer时设置的id,在setSerializationId方法中,使用id做key,new了一个弱引用对象为value,添加到了serializableFactories中,DefaultListableBeanFactory为被弱引用对象;如果需要,可以通过id得到引用对象,在通过get()方法得到DefaultListableBeanFactory对象。

@Override
protected final void refreshBeanFactory() throws IllegalStateException {if (!this.refreshed.compareAndSet(false, true)) {throw new IllegalStateException("GenericApplicationContext does not support multiple refresh attempts: just call 'refresh' once");}this.beanFactory.setSerializationId(getId());
}/** Map from serialized id to factory instance. */
private static final Map<String, Reference<DefaultListableBeanFactory>> serializableFactories = new ConcurrentHashMap<>(8);
public void setSerializationId(@Nullable String serializationId) {if (serializationId != null) {serializableFactories.put(serializationId, new WeakReference<>(this));}else if (this.serializationId != null) {serializableFactories.remove(this.serializationId);}this.serializationId = serializationId;
}
3、准备bean工厂
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {//通知内部bean工厂使用上下文的类加载器beanFactory.setBeanClassLoader(getClassLoader());//为bean定义值中的表达式指定解析策略,即解析el表达式,默认"#{"开头,"}"结尾beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));//在这里new了一个资源编辑注册器ResourceEditorRegistrar,该类实现了PropertyEditorRegistrar接口//作用是使用以下资源编辑器去填充给的的注册表:ResourceEditor、InputStreamEditor、InputSourceEditor、FileEditor、Urleditor、UriEditor、ClassEditor、ClassArrayEditor。//如果给的注册表是PropertyEditorRegistrySupport类型,编辑器交由该类管理beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));// 向BeanPostProcessor的List中添加一个ApplicationContextAwareProcessor,参数是上下文//该类作用是将上下文传递给实现environmentaware、embeddedValueResolveraware、resourceLoaderware、applicationEventPublisheraware、messageSourceAware或applicationContextaware接口的bean。beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));//自动装配忽略给定的类型,添加到忽略的集合中beanFactory.ignoreDependencyInterface(EnvironmentAware.class);beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);beanFactory.ignoreDependencyInterface(MessageSourceAware.class);beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);// BeanFactory interface not registered as resolvable type in a plain factory.// MessageSource registered (and found for autowiring) as a bean.//根据第二个参数注册一个特殊的依赖类型beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);beanFactory.registerResolvableDependency(ResourceLoader.class, this);beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);beanFactory.registerResolvableDependency(ApplicationContext.class, this);// 该BeanPostProcessor检测那些实现了ApplicationListener接口的bean,在它们创建时初始化之后,将它们添加到应用上下文的事件多播器上//并在这些ApplicationListener bean销毁之前,将它们从应用上下文的事件多播器上移除。beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));// Detect a LoadTimeWeaver and prepare for weaving, if found.if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));// Set a temporary ClassLoader for type matching.beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));}// Register default environment beans.//注册默认environment bean,如果beanfactory不存在environment if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());}//systemProperties 同上if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());}//systemEnvironment 同上if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());}
}
4、允许上下文子类对bean工厂进行后置处理

basePackages和annotatedClasses都为空。跳过
org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext#postProcessBeanFactory

@Override
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {super.postProcessBeanFactory(beanFactory);if (this.basePackages != null && this.basePackages.length > 0) {this.scanner.scan(this.basePackages);}if (!this.annotatedClasses.isEmpty()) {this.reader.register(ClassUtils.toClassArray(this.annotatedClasses));}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/619640.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

网络篇04 | 应用层 mqtt(物联网)

网络篇04 | 应用层 mqtt&#xff08;物联网&#xff09; 1. MQTT协议介绍1.1 MQTT简介1.2 MQTT协议设计规范1.3 MQTT协议主要特性 2 MQTT协议原理2.1 MQTT协议实现方式2.2 发布/订阅、主题、会话2.3 MQTT协议中的方法 3. MQTT协议数据包结构3.1 固定头&#xff08;Fixed header…

Go Slice/Map 复制陷阱

信的内容是我 22 年问 Google Golang Nuts 的问题以及得到的回复&#xff0c;原信链接&#xff1a;https://groups.google.com/g/golang-nuts/c/Q7_Dj_ogVJE/m/_5TSXCIaBgAJ?pli1 信件内容 为什么Go的slice有“复制陷阱”&#xff0c;而map却没有&#xff1f; 假设我们有一个…

微软拼音输入法删除备选词

不是所有词都可以从微软拼音输入法的备选词中删除的。 当你需要对自己的自定义词删除的话&#xff0c;在备选词中选择鼠标右键&#xff0c;然后单击删除。 如上图就可以把备选词从微软拼音输入法中删除了。

【SpringBoot】返回参数

返回参数 返回页面返回数据返回 html 代码返回 json 数据两数相加用户登录 返回页面 首先在 static 文件夹中创建 index.html 文件&#xff1a; 代码&#xff1a; <html> <body><h1>hello word!!!</h1><p>this is a html page</p> <…

从 SQLite 3.5.9 迁移到 3.6.0(二十一)

返回&#xff1a;SQLite—系列文章目录 上一篇&#xff1a;从 SQLite 3.4.2 迁移到 3.5.0&#xff08;二十&#xff09; 下一篇&#xff1a;SQLite从出生到现在&#xff08;发布历史记录&#xff09;&#xff08;二十二&#xff09; ​SQLite 版本 3.6.0 &#xff08;2008…

【MATLAB源码-第16期】基于matlab的MSK定是同步仿真,采用gardner算法和锁相环

1、算法描述 **锁相环&#xff08;PLL&#xff09;** 是一种控制系统&#xff0c;用于将一个参考信号的相位与一个输入信号的相位同步。它在许多领域中都有应用&#xff0c;如通信、无线电、音频、视频和计算机系统。锁相环通常由以下几个关键组件组成&#xff1a; 1. **相位…

PostgreSQL入门到实战-第二十七弹

PostgreSQL入门到实战 PostgreSQL中数据分组操作(二)官网地址PostgreSQL概述PostgreSQL中HAVING命令理论PostgreSQL中HAVING命令实战更新计划 PostgreSQL中数据分组操作(二) 使用PostgreSQL HAVING子句来指定组或聚合的搜索条件 官网地址 声明: 由于操作系统, 版本更新等原因…

华为OD机试 - 内存冷热标记(Java 2024 C卷 100分)

华为OD机试 2024C卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;A卷B卷C卷&#xff09;》。 刷的越多&#xff0c;抽中的概率越大&#xff0c;每一题都有详细的答题思路、详细的代码注释、样例测试…

经久耐用特氟龙材质塑料烧杯PFA坩埚耐受强酸强碱耐高温

PFA烧杯在实验过程中可作为储酸容器或涉及强酸强碱类实验的反应容器&#xff0c;用于盛放样品、试剂&#xff0c;可搭配电热板加热、蒸煮、赶酸用。 PFA烧杯规格参考&#xff1a;10ml、30ml、50ml、100ml、250ml、500ml、1000ml、2000ml。 外壁均有凸起刻度&#xff0c;直筒设…

《由浅入深学习SAP财务》:第2章 总账模块 - 2.6 定期处理 - 2.6.6 年初操作:科目余额结转

2.6.6 年初操作&#xff1a;科目余额结转 在使用事务代码 FAGLB03 查询科目余额时&#xff0c;可以看到按期间的发生额清单。其中&#xff0c;第一行称为“余额结转”&#xff0c;该行的累计余额代表上年度遗留下来的余额&#xff0c;也就是年初余额。对于资产负债表科目而言&a…

高清4路HDMI编码器JR-3214HD

产品简介&#xff1a; JR-3214HD四路高清HDMI编码器是专业的高清音视频编码产品&#xff0c;该产品具有支持4路高清HDMI音视频采集功能&#xff0c;4路3.5MM独立外接音频输入&#xff0c;编码输出双码流H.264格式&#xff0c;音频MP3/AAC格式。编码码率可调&#xff0c;画面质…

2024最新 PyCharm 2024.1 更新亮点看这篇就够了

2024最新 PyCharm 2024.1 更新亮点看这篇就够了 文章目录 2024最新 PyCharm 2024.1 更新亮点看这篇就够了&#x1f680; PyCharm 2024.1 发布&#xff1a;全面升级&#xff0c;助力高效编程&#xff01;摘要引言 &#x1f680; 快速掌握 Hugging Face&#xff1a;模型与数据集文…