以代码流程方式探索Spring源码(三)--自定义BeanDefiniton的解析

在Spring中,BeanDefinition的解析在将任务交由BeanDefinitonParserDelegate的时候,出现了分支。一支是spring默认标签的解析,另一支是自定义标签的解析,下面就探索下在spring初始化的过程中是怎么解析自定义标签信息的。

类:DefaultBeanDefinitonDocumentReader
bean标签解析分支

自定义标签是什么样儿的

举例:<context:component-scan base-package=“com.csdn.wgd”/>

类:BeanDefinitionParserDelegate
从下图展示的parseCustomElement()方法中,可以看出解析自定义标签的三大步:1、获取namespaceUri;2、获取对应的NamespaceHandler;3、调用对应的parse()方法。
在这里插入图片描述

根据当前解析标签element找到该标签对应的namespaceUri

在这里插入图片描述
调用的是Node的方法,那这个得到的namespaceURI是什么呢,请看下图。
在这里插入图片描述
如上图所示,就是根据自定义标签的前缀在xmlns中找到对应的namespaceUri。

加载spring中所有的jar包中的spring.handlers文件,并建立映射关系。

以context这个自定义标签为例,它的spring.handler文件在如图所示的位置。
在这里插入图片描述
spring.handler文件中存储的是Element的namespaceUri和对应的handler类的映射关系。如下图所示:
在这里插入图片描述

根据namespaceUri从映射关系中找到对应的实现了NamespaceHandler接口的类在这里插入图片描述

那么映射关系是在哪里建立的的,跟进resolve()方法。
在这里插入图片描述
在这个方法中,首先会对spring所有jar包(包括自定义目录下面的)里面的 META-INFO/spring.handlers 文件中的内容建立map映射关系,然后获取到对应的handler类,执行 init() 方法。(下一个步骤介绍)
继续跟进getHandlerMappings()方法:
在这里插入图片描述
在这里插入图片描述
首先可以看到,这个方法进来会首先从DefaultNamespaceHandlerResolver的类变量中取这个map,取到的值为null时,才会去扫描文件进行解析,解析后会赋值给此变量,下一个方法再进来的时候就直接取值而不用解析了。这个也是spring中惯用的一种方法,利用类变量来实现缓存的功能,在我们自己的项目中有些需求也可以这样来实现。

至于对spring.handler文件的加载,跟进去就会发现底层也是用流的方法加载的。在spring中,对文件的读取本质底层都是用流的方式来加载。

调用对应类的init()方法,该方法会注册对应的解析类

下图中展示了context这个自定义标签对应的NamespaceHandler类。它的init方法注册了该前缀下可能存在的所有的具体标签的解析类。其中我们最常见的就是component-scan这个标签。我们可以发现在spring中,涉及到register的地方,基本就是建立一个映射关系,可以看出spring对map这种数据结构的使用很频繁,在我们自己的项目中,涉及到缓存之类的也可以使用这种数据结构。
在这里插入图片描述

根据namespaceUri找到对应的解析类,调用其中的parse()方法完成自定义标签的解析

在这里插入图片描述
类:NamespaceHandlerSupport
在这个地方获取到该element的具体解析器,就是在init()方法中注册的那些解析器。
在这里插入图片描述
在findParserForElement()方法中是通过该Element的localName来获取对应的解析Parser。存在一个map中,就是init的时候保存的map。

类:ComponentScanBeanDefinitionParser
在这里,先获取<context:component-scan base-package=“com.csdn.wgd”/>中的base-package属性。具体的扫描工作交给ClassPathBeanDefinitionScanner类的对象去做,就是doScan()方法,我们知道,前面带do的方法就是真正干活的方法。
在这里插入图片描述
在获取到BeanDefinitionHolder对象后,看上图最后一句代码,会进行注册,表面上看是注册到了XmlReaderContext中,实际上在XmlReaderContext中包含了BeanDefinitionRegistry。从类关系上可以看出来DefaultListableBeanFactory实现了BeanDefinitionRegistry。从前面的代码来看,这个地方的BeanDefinitionRegistry的实际类型是DefaultListableBeanFactory。此外,在这个地方还完成了三个重要的BeanDefinition的注册,ConfigurationClassPostProcessor、AutowiredAnnotationBeanPostProcessor、CommonAnnotatinBeanPostProcessor。
在这里插入图片描述
我们先看下在configureScanner()方法中spring干了啥?
在这里插入图片描述
在这里插入图片描述
进来了先判断该component-scan标签有没有配置“use-default_filters”属性信息,如果没有配置的话,就使用spring默认的过滤器,如下图所示。默认操作就是在includeFilters这个List<TypeFilter>中加入@Component这个注解。这个就是我们的Spring默认扫描的时候认识的就是加有@Component注解的类的原因。
在这里插入图片描述
在这里插入图片描述

接下来我们看看doScan这个劳工到底是怎么干活的。
在循环中处理可能传过来的多个base-package的值;
我们可以看到在findCandidateComponents()方法中就返回了BeanDefinition类型的对象;
然后进行属性封装,最后注册进Beandefinition容器。
在这里插入图片描述
看一看是怎么根据base-package路径找到这些候选的BeanDefinition的。
在这里插入图片描述
从下图可以看出:
===>先对路径进行正则化;
===>递归路径找到该路径下的所有类文件并封装成Resource对象;【递归查找】
===>循环处理各个Resource;
===>抽取类的基本信息封装为MetadataReader对象;
===>判断师父需要创建BeanDefiontion,如果需要,则进行新建并放入set进行返回。
【递归查找方式是根据给的路径生成一个File,然后判断该File是否是一个.class文件,是的话加入候选,反之如果是一个文件夹,递归查找。】
【我们可以看到判断的逻辑是isCandidateComponent()方法,从名字上看就能看出来是判断类上有没有@Component注解,以及有没有对应的include和exclude属性并进行对应判断。】
【可以看出新建的BeanDefinition的实际对象类型是ScannedGenericBeanDefinition】
在这里插入图片描述
回到doScan()方法生成bd的后续操作
在这里插入图片描述
在第二个红箭头出对beanDefiniton进行了很多的属性的装配。
在这里插入图片描述

至此,我们的通过ClassPathXmlApplicationContext进来的spring初始化工作就完成了默认和自定义标签解析的BeanDefinition的注册工作。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值