| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> Java知识库 -> spring 源码解析系列01--xml解析 -> 正文阅读 |
|
[Java知识库]spring 源码解析系列01--xml解析 |
前提说明: 相关文章,都是由本人的学习笔记转化,可能某个地方或者某一句话感到奇怪的地方,大家可以留言,我会逐一回答。 另外,spring源码阅读有一定的承上启下,如果没有前面的知识铺垫,很难从中间某一段源码解读去读懂相关关系的,其实 spring 这个框架,里面复杂的是一些类和接口的多继承,多实现的处理,还有一些比较绕人的递归逻辑,但是核心不离一些 processor, 如 BeanPostProcessor, BeanFactoryProcessor,? BeanDefinitionRedistry 等,这些都是 spring 的一些核心接口,实现他们就会可以自定义自己的扩展等。 源码解析中,为了能快速定位到源码方法位置,我使用 idea 的 copy reference 来复制完整的类名称和方法名,那样就可以通过快速查找(快速按两下 shift? 找到方法),以核心方法 refresh() 为例,我会在文中以全限定类名#方法名(参数)的形式记录:org.springframework.context.support.AbstractApplicationContext#refresh 快速按两下 shift 键,粘贴类名#方法名,可以快速定位到具体方法,方便快速查看 ?另外,分析的源码版本为 5.2.8 我们以最原始的方式,学习spring源码,最开始肯定是以 xml 形式的源码 其实一个最简单的spring框架,我们仅仅需要 context包即可
但是想打印日志,还是得依赖以下的包: 下面以简单的 xml 形式的配置跟一下源码: student 类:
spring.xml 文件
xml 构建一个spring 如下:
我们进入 ClassPathXmlApplicationContext 这个类: 进入核心方法: org.springframework.context.support.AbstractApplicationContext#refresh ClassPathXmlApplicationContext 是继承了这个类的,这个是抽象类,refresh() 是这个抽象类的方法,这是一种模板方法设计模式的体现,该方法是spring容器初始化的核心方法。是spring容器初始化的核心流程,是一个典型的父类模板设计模式的运用,根据不同的上下文对象,会调到不同的上下文对象子类方法中 核心上下文子类有: * ClassPathXmlApplicationContext * FileSystemXmlApplicationContext * AnnotationConfigApplicationContext * EmbeddedWebApplicationContext(springboot) refresh 方法第一个重要方法: obtainFreshBeanFactory(); obtain 为获得的意思 目的如下: 重要程度:5 * 1、创建BeanFactory对象 * 2、xml解析 * 传统标签解析:bean、import等 * 自定义标签解析 如:<context:component-scan base-package="com.xiangxue.jack"/> * 自定义标签解析流程: * a、根据当前解析标签的头信息找到对应的namespaceUri * b、加载spring所有jar中的spring.handlers文件。并建立映射关系 * c、根据namespaceUri从映射关系中找到对应的实现了NamespaceHandler接口的类 * d、调用类的init方法,init方法是注册了各种自定义标签的解析类 * e、根据namespaceUri找到对应的解析类,然后调用paser方法完成标签解析 * * 3、把解析出来的xml标签封装成BeanDefinition对象 核心我们关心的,还是最终把 xml 标签封装成 BeanDefinition 对象,后面初始化的相关条件参数都是从这个对象里面去取的 现在我们重要讲解 obtainFreshBeanFactory 这个方法: org.springframework.context.support.AbstractApplicationContext#obtainFreshBeanFactory obtainFreshBeanFactory 点进去: 首先调用的第一个方法是 refreshBeanFactory();, 这个在 AbstractApplicationContext 这个抽象类中的接口,故而然之,就是的让子类实现这个接口 但是我们使用 ctrl + alt 查看的时候,发现有两个实现,那么要怎么区分呢? ?我们首先看 clssPathXmlapplication 的继承关系: ?可以看到,clssPathXmlapplication 的 上几层父类,继承了 AbstractApplicationContext ,这个AbstractRefreshableApplicationContext 类就实现了 AbstractApplicationContext 的refreshBeanFactory方法 所以我们断点进去AbstractApplicationContext 的 refreshBeanFactory 方法: spring 中,模板设计模式可谓是用得最多设计模式,用习惯了模板设计模式,感觉写什么都想用模板设计模式的感觉,比如设计 redis 的缓存,使用模板设计模式就能很好应用对公司的代码约束规范,这也是考验架构师的能力之一 模板设计模式大概如下: 1、父类定义抽象方法,同时父类定义核心方法,核心方法中去调用抽象方法 2、子类实现抽象方法 3、调用子类的父类核心方法,抽象方法层面执行的是子类的实现 这一段不重要,略过 ?我们核心进入 customizeBeanFactory(beanFactory); 这个方法先,主要是根据参数设置是否允许循环依赖和 BeanDefinition 重载,默认没有配置的情况下,都不会进入这两个条件,但是这两个值默认都为true, 主要是为了禁用才存在这个设置: ?这两个参数要怎么配置进去呢?在讲解 BeanFactoryPostPossor 的时候再说,其实就是实现 BeanDefinitionRegistryPostProcessor 接口,实现 postProcessBeanFactory 接口中,拿到 beanFactory 进行设置进去就行,我们核心的,还是要看 loadBeanDefinitions(beanFactory); 这个方法 进入他的实现:org.springframework.context.support.AbstractXmlApplicationContext#loadBeanDefinitions(org.springframework.beans.factory.support.DefaultListableBeanFactory) ?这里又产生了一个委托设计模式的存在,但是没什么作用,想看讲解可以看第二节课的13:38开始看。还是挺简单的,大概的意思就是,我实现了某接口,但是我的实现,是用其他人的接口实现来干的,就是说,A接口定义B方法,B类和C类都实现了B方法,但是C类虽然实现,但是他的实现是调用B类实现的B方法,相当于外包。我们主要看 loadBeanDefinitions(beanDefinitionReader) 这个方法 ?继续 跟进这个 loadBeanDefinitions, 由于可能存在多个资源文件,所以这里使用了 for 循环 ?核心还是再跟进 for 循环里面的 loadBeanDefinitions ?一路跟踪,到达 org.springframework.beans.factory.support.AbstractBeanDefinitionReader#loadBeanDefinitions(java.lang.String, java.util.Set<org.springframework.core.io.Resource>) ?这里第一行的 ResourceLoader 拿到的是 ClassPathXmlApplicationContext 对象,从继承关系图中,ClassPathXmlApplicationContext 是有继承 ResourceLoader 的 ?什么时候设置进去的呢?是在 org.springframework.context.support.AbstractXmlApplicationContext#loadBeanDefinitions(org.springframework.beans.factory.support.DefaultListableBeanFactory) 的时候设置进去的 我们核心看 int count = loadBeanDefinitions(resources); 这个方法: org.springframework.beans.factory.support.AbstractBeanDefinitionReader#loadBeanDefinitions(org.springframework.core.io.Resource...) 进入 xml 的实现: org.springframework.beans.factory.xml.XmlBeanDefinitionReader#loadBeanDefinitions(org.springframework.core.io.Resource) org.springframework.beans.factory.xml.XmlBeanDefinitionReader#loadBeanDefinitions(org.springframework.core.io.support.EncodedResource) 核心看这个方法: org.springframework.beans.factory.xml.XmlBeanDefinitionReader#doLoadBeanDefinitions 核心看 registerBeanDefinitions 这一行,根据解析出来的document对象,拿到里面的标签元素封装成BeanDefinition 进入 org.springframework.beans.factory.xml.XmlBeanDefinitionReader#registerBeanDefinitions? 继续走进核心方法 documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); 核心代码到了: org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions 这里 ?doRegisterBeanDefinitions 的作用,就是拿到标签,挨个解析各个节点,根据节点信息,再封装对应的 beanDefinition 主要看这个方法的 parseBeanDefinitions(root, this.delegate); 这一行 进入: org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#parseBeanDefinitions ?这里的解析,又分为默认标签解析,和自定义标签解析 解析默认标签: org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#parseDefaultElement 重点看解析 bean 标签的 org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#processBeanDefinition ?跟进这个方法的第一行,来到 org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parseBeanDefinitionElement(org.w3c.dom.Element, org.springframework.beans.factory.config.BeanDefinition) ?这个方法中,checkNameUniqueness(beanName, aliases, ele); 会检查名字是否唯一,我们进入解析的核心方法 AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean); org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parseBeanDefinitionElement(org.w3c.dom.Element, java.lang.String, org.springframework.beans.factory.config.BeanDefinition) 在这个方法中,会生成 BeanDefinition ?AbstractBeanDefinition bd = createBeanDefinition(className, parent); 这个代码中,会到 org.springframework.beans.factory.support.BeanDefinitionReaderUtils#createBeanDefinition 的时候 new 出来一个 GenericBeanDefinition 对象 ?这里的,如果 className 不为空,要么就通过反射,设置到 beanClass 这个属性,要么就把 className 设置到 BeanClassName 这个属性里面去 spring 中,有N多个 BeanDefinition, 根据不同的功能产生不同的 BeanDefinition, 像 @service, @Compoment的注解,则为另外一个 AnnotationBeanDefinition, 每一个BeanDefinition都有多少差异,但是核心的都是 AbstractBeanDefinition 的对象,核心的内容都在这个 AbstractBeanDefinition 抽象对象里面 如果同时有注解,又有xml的话,注解的 bean会覆盖xml 的,不过这个条件是得在允许覆盖 BeanDefinition 的前提下 BeanDefinition 会贯穿整个 spring 的生命周期 重要属性如下: ?我们继续回到 org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parseBeanDefinitionElement 的其它方法
|
|
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/23 13:04:57- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |