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.mzph.cn/news/818318.shtml

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

相关文章

若依vue中关于字典的使用

文章目录 字典管理页面列表点击某个字典类型展示具体字典数据修改某一条字典数据 字典的应用一般用于select多选框中代码实现根据字典Dict的value获取Label&#xff0c;类似于通过key获得value 源码解析 字典管理页面 列表 点击某个字典类型展示具体字典数据 修改某一条字典数…

Zookeeper集群+消息队列Kafka

一. Zookeeper 集群的相关知识 1. zookeeper的概念 ZooKeeper 是一个分布式的&#xff0c;开放源码的分布式应用程序协调服务&#xff0c;是Google的Chubby一个开源的实现&#xff0c;是Hadoop和Hbase的重要组件。它是一个为分布式应用提供一致性服务的软件&#xff0c;提供的…

C++修炼之路之list模拟实现--C++中的双向循环链表

目录 引言 一&#xff1a;STL源代码中关于list的成员变量的介绍 二&#xff1a;模拟实现list 1.基本结构 2.普通迭代器 const迭代器的结合 3.构造拷贝构造析构赋值重载 清空 4.inserterase头尾插入删除 5.打印不同数据类型的数据《使用模板加容器来完成》 三&#xf…

python之gmsh划分网格

Gmsh&#xff08;Geometry Modeling and Meshing Suite&#xff09;是一个开源的三维有限元网格生成器&#xff0c;它集成了内置的CAD引擎和后处理器。Gmsh的设计目标是提供一个快速、轻量级且用户友好的网格工具&#xff0c;同时具备参数化输入和高级可视化能力。Gmsh围绕几何…

数据治理项目——深铁集团数据治理规划

目录 一、前言 二、数据治理内容与主要措施 2.1 实施背景 2.2 主要举措 2.2.1 制定数据战略目标 2.2.2 绘制数据治理蓝图 2.2.3 绘制数据治理制度 2.2.4 梳理数据资产目录 三、 应用效果 3.1 数据资产可视化管理 3.2 数据标准治理 3.3 集团大数据平台优化建设 一、…

Windows10为Git Bash添加文件传输命令rsync(详细图文配置)

文章目录 1. 安装git bash2. 下载所需要的4个包3. 下载解压包的软件4. 复制每个包下面的usr到git安装目录下4.1 所遇问题4.2 解决 5. 安装完成6. 需要注意 Windows上要使用 rsync命令上传或下载文件&#xff0c;需要使用git bash&#xff0c;git bash没有rsync&#xff0c;需要…

zabbix监控服务

一、监控软件的作用 作为一个运维&#xff0c;需要会使用监控系统查看服务器状态以及网站流量指标&#xff0c;利用监控系统的数据去了解上线发布的结果和网站的健康状态 利用一个优秀的监控软件&#xff0c;我们可以&#xff1a; 对系统不间断实时监控实时反馈系统当前状态保…

游戏实践:扫雷

一.游戏介绍 虽然很多人玩过这个游戏&#xff0c;但还是介绍一下。在下面的格子里&#xff0c;埋的有10颗雷&#xff0c;我们通过鼠标点击的方式&#xff0c;点出你认为不是雷的地方&#xff0c;等到把所有没有雷的格子点完之后&#xff0c;及视为游戏胜利。 上面的数字的意思…

华院计算参编《金融业人工智能平台技术要求》标准

随着人工智能技术的迅猛发展&#xff0c;金融机构正在从业务场景化向企业智能化演进&#xff0c;金融业对智能化的需求愈加迫切。为引导产业有序发展、规范行业自律、加快金融行业智能化转型&#xff0c;中国信通院依托中国人工智能产业发展联盟&#xff08;AIIA&#xff09;及…

CommunityToolkit.Mvvm笔记---Ioc

使用MVVM模式提高应用程序代码库中的模块化程度的最常用模式是使用某种形式的反转控制&#xff08;Ioc&#xff09;。其中最常见的解决方案是使用依赖关系注入&#xff0c;该解决方案存在于创建多个注入后端类的服务&#xff08;即以参数的形式传递给 viewmodel 构造函数&#…

ELK+Filebeat日志分析系统

一、ELK基本介绍&#xff1a; 1.ELK 简介: ELK平台是一套完整的日志集中处理解决方案(日志系统)。 将 ElasticSearch、Logstash 和 Kiabana 三个开源工具配合使用&#xff0c; 完成更强大的用户对日志的查询、排序、统计需求。 ELK --> ELFK --> ELFKMQ2.ELK组件介绍…

IDEA创建jsp文件具体步骤

1. 创建普通项目 2.文件->项目结构->模块&#xff0c;点击号&#xff0c;选择web 点击确认 3.配置工件 4.配置tomcat 点击确定 5.创建jsp文件&#xff0c;注意创建位置 6.然后换调试位置 注意此时不能有其他的web项目运行,否则就选不了路径 完成后运行即可

Python代码打包成exe程序

国内镜像源 清华大学 https://pypi.tuna.tsinghua.edu.cn/simple阿里云 https://mirrors.aliyun.com/pypi/simple/豆瓣 https://pypi.douban.com/simple/ 百度云 https://mirror.baidu.com/pypi/simple/中科大 https://pypi.mirrors.ustc.edu.cn/simple/华为云 https://mirror…

MySQL 使用C语言

一般使用MySQL很少用命令行&#xff0c;一般都是通过程序内部使用&#xff0c;MySQL也为不同的语言定制了不同的头文件和库函数&#xff0c;可以在自己的程序中通过包含头文件和编译时候链接库函数来使用MySQL。 现在一般安装MySQL的时候就会自动给你安装库函数和头文件。 可…

数据资产管理制度探索——浙江篇

在行政事业单位数据资产管理领域&#xff0c;浙江省以创新性思维与高质量发展的战略眼光&#xff0c;积极探索并构建了具有前瞻性和实效性的数据资产管理制度。作为财政部数据资产管理试点省份&#xff0c;浙江省财政厅与省标准化研究院强强联合&#xff0c;充分运用数据溯源、…

【报错】TypeError: Cannot read property ‘meta‘ of undefined

&#x1f608;解决思路 首先这里很明显我们能看到是缺少该参数&#xff1a;meta。 但是经过查找后发现和该参数无关。 &#x1f608;解决方法 后来我上网搜了下&#xff0c;网上的回答大部分偏向于是package.json这个文件中的tabBar.list数组对象只有一条的问题。 网上的大…

基于SpringBoot + Vue实现的租房管理系统设计与实现+毕业论文+开题报告​(包运行成功)

介绍 管理员的主要功能设计为&#xff1a;密码信息管理、注册用户管理、区域管理、出租管理等模块。 房主的主要功能设计为&#xff1a;密码信息管理、个人信息管理、订单管理等模块。 用户的主要功能设计为&#xff1a;用户登录、密码管理、出租管理、收藏管理等模块。 源码论…

数据结构---线性表

1&#xff0c;顺序表实现---动态分配 #include<stdlib.h> #define InitSize 10 typedef struct {int *data;//静态分配int length;int MaxSize; }SqList; void InitList(SqList& L) {L.data (int*)malloc(InitSize * sizeof(int));//分配空间L.length 0;L.MaxSize…

关于SpringCloud,你了解多少?

Why SpringCloud&#xff1f; Spring cloud 是一系列框架的有序集合。它利用 spring boot 的开发便利性巧妙地简化了分布式系统基础设施的开发&#xff0c;如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等&#xff0c;都可以用 spring boot 的开发风格做到一…

论坛直击|发展新质生产力,高校怎么做?

新质生产力浪潮涌动 三大议题聚焦高校人才培养 今年全国两会的政府工作报告将“大力推进现代化产业体系建设&#xff0c;加快发展新质生产力”列在2024年政府工作任务首位&#xff0c;发展新质生产力的先导是培养拔尖创新人才&#xff0c;高等教育改革必须以立德树人为根本任…