一、前言
- 最近开始重温Spring,觉得只知道会用是不行的,所以想尝试着结合资料观摩一下源码
- 由于Spring源码非常的多,一步步都看完明显是不现实的,所以只记录自己觉得重要的步骤,其它细枝末节不去纠结
- 本人才疏学浅,以下的总结完全主观,在逐渐学习的过程中也会不断更新表述不合适的地方,如果有错误希望能包涵并指出!
二、源码阅读
- 接着02博客,到doLoadBeanDefinitions(inputSource, encodedResource.getResource())
- doLoadBeanDefinitions
- 进来首先执行doLoadDocument来获得一个Document对象,点进去看看
- 进来后先读注释:使用配置的DocumentLoader实际加载指定的文档。documentLoader对象是在初始化该类XmlBeanDefinitionReader的时候就创建好了的,实例为DefaultDocumentLoader,调用该对象的loadDocument去解析文件,传入了5个参数,一个个看
- inputSource:包含解析文件信息的IO流对象
- getEntityResolver():获得XmlBeanDefinitionReader中的entityResolver属性,该属性是我们在初始化xml解析器时设置的,里面主要包含xml中相关的xsd
- errorHandler:出现错误时的处理类
- getValidationModeForResource(resource):进入该方法,由上面的注释我们知道这是决定给定资源验证模式的,VALIDATION_AUTO和VALIDATION_XSD都是int类型的final常量,决定相应验证方法的,同时由注释知道我们也可以自己指定验证模式通过覆写该方法
- isNamespaceAware():返回XML解析器是否应支持XML命名空间(false)
- 进入loadDocument方法,里面主要就是通过工厂构建DocumentBuilder对象,用该对象去解析配置文件,具体解析过程就不点进去看了,看不懂…
- 回到doLoadBeanDefinitions,执行完doLoadDocument后返回了一个Document对象,在控制台查看该对象,发现此时已经把配置文件相关信息都放进去了
- 有了Document对象后,开始注册BeanDefinitions,将创建好的doc对象和资源对象传入,返回一个int值,由下面的代码我们指定该count代表的就是一共有多少个注册的bean对象,进入该方法
- 进入后先是构造了一个BeanDefinitionDocumentReader对象,然后调用getRegistry().getBeanDefinitionCount()获得之前注册的bean对象个数,点进该方法看看
- getRegistry方法,返回AbstractBeanDefinitionReader类中的registry属性,该属性是我们在实例化XmlBeanDefinitionReader时,由它调用父类AbstractBeanDefinitionReader的构造方法时设置的,实例为DefaultListableBeanFactory
- getBeanDefinitionCount就是获得DefaultListableBeanFactory类中beanDefinitionMap的个数,但显然我们之前没有注册过bean,所以此时返回0
- 由创建的BeanDefinitionDocumentReader调用方法开始注册bean,此时第二个参数为创建一个XmlReaderContext实例,不看创建步骤了,进入该方法
- 进入到了DefaultBeanDefinitionDocumentReader类中,先把创建好的XmlReaderContext实例赋给该类的readerContext属性,之后调用doRegisterBeanDefinitions,继续进入
- 进来先尝试获得DefaultBeanDefinitionDocumentReader类中的delegate属性,这个属性我们之前没有设置过,返回null
- 返回了null对象后,下一步就是创建delegate属性了,点进去看看
- 通过BeanDefinitionParserDelegate的构造方法去实例化该对象并返回,此时我们并不是很确定delegate属性代表的是什么,点BeanDefinitionParserDelegate类中看看
- 发现该类中定义了很多常量,都是我们在配置文件时常用的配置属性,所以此时我们可以推断delegate属性中肯定有包含解析时用于匹配相应属性的容器
- 回到doRegisterBeanDefinitions方法中,用创建的delegate属性去验证传入的是否是一个根对象,document对象显然是,进入if,里面做了一些判断,其实什么都没干,跳过到下面的preProcessXml
- 点进preProcessXml(root)方法和postProcessXml(root)方法,发现里面也是什么都没干,这2个方法也是留给子类做扩展的,用于在注册bean前和注册bean后干点事
- 进入parseBeanDefinitions方法,先是通过getChildNodes获取到传入的根节点中的所有子元素,之后开始遍历
- 遍历的时候有2种情况,第一种是DefaultElement,即默认标签(不引入命名空间也可正常使用的标签),其它的都是CustomElement,我们在配置文件中先配置的是注解扫描,也就是<context:component-scan base-package=“com.hcc”/>所以先进入else中,进入该方法
- 去到了BeanDefinitionParserDelegate类中,里面由调用了重载方法,多了一个参数null,继续往下走
- 由上面定义的形参我们知道传入的null是一个BeanDefinition对象,该方法第一步获取到namespaceUri,即该配置对应的是哪一个命名空间
- 解析namespaceUri,去获得相应的处理器handler,注意:不同的命名空间会有不同的处理器,具体可以在命名空间对应的源码目录下的MTEA-INF/spring.handlers文件中查看,之后调用处理器的parse方法继续解析
- 里面通过findParserForElement方法获得具体的解析类,在同一个命名空间中,不同的标签也会对应不同的解析类,之后调用解析类的parse方法解析对应标签,所有解析类在解析完后都会将标签信息封装成BeanDefinitionHolder对象,并将其注册到工厂中
- 将beanDefinition对象注册到工厂中,进入该方法
- 一路往下走最终回到了我们熟悉的DefaultListableBeanFactory类中,将刚创建好的beanDefinition添加到工厂容器中,注意:此时所有注册的对象实例还没有创建,只是往容器中添加了bean信息
- 因为我们只有一个对象需要注册,所以在工厂添加完后便一路返回,最终回到parseBeanDefinitions中,解析下一个document对象
- 当所有的document对象解析完成后,就会一路返回最终回到refresh方法中,工厂对象就创建完了,现在我们知道obtainFreshBeanFactory语句不仅仅时创建工厂,其中还包括解析xml文件,将bean信息注册到工厂中
- 边看边写太累了/(ㄒoㄒ)/~~,后续的只画图算了,有需要的下面网址自取
- 进来首先执行doLoadDocument来获得一个Document对象,点进去看看
三、图像总结
链接:https://pan.baidu.com/s/1_3dNVCsFp7o1_KzS9FYdPA
提取码:7wt8