(三)Spring源码解析:自定义标签解析

一、使用示例

步骤1:创建User实体

(三)Spring源码解析:自定义标签解析_第1张图片

步骤2:定义一个XSD文件描述组件内容

(三)Spring源码解析:自定义标签解析_第2张图片

步骤3:创建BeanDefinitionParser接口的实现类,用来解析XSD文件中的定义和组件定义

(三)Spring源码解析:自定义标签解析_第3张图片

步骤4:创建NamespaceHandlerSupport实现类,目的是将组件注册到Spring容器中

(三)Spring源码解析:自定义标签解析_第4张图片

步骤5:编写spring.handlersspring.schemas文件,默认位置是/META-INF目录下

(三)Spring源码解析:自定义标签解析_第5张图片

步骤6:在配置文件oldbean.xml中引入对应的命名空间以及XSD之后,就可以配置

(三)Spring源码解析:自定义标签解析_第6张图片

步骤7:进行测试

(三)Spring源码解析:自定义标签解析_第7张图片

二、源码解析

在第2讲中,我们已经介绍了关于默认标签的解析过程。那么我们还是需要将视角在回到parseBeanDefinitions(...)方法上来,从下图源码截图中我们可以看出来,我们首先是来判断rootroot的子节点是否是默认表空间,即:通过delegate.isDefaultNamespace(...)来进行判断:

如果是默认表空间】执行默认标签解析——delegate.parseDefaultElement(ele, delegate);
如果不是默认表空间】则执行自定义标签解析——delegate.parseCustomElement(ele);

(三)Spring源码解析:自定义标签解析_第8张图片

下面我们来看一下parseCustomElement(...)方法的具体实现,具体来说有如下3个步骤:

首先】获得namespaceUri,此处是通过org.w3c.dom.Node中的getNamespaceURI()方法进行获取的;
其次】获得解析该自定义标签的NamespaceHandler实现类。
最后】调用该实现类的parse(...)方法进行解析操作。

(三)Spring源码解析:自定义标签解析_第9张图片

2.1> getNamespaceURI(ele)方法解析

此方法是用于获得namespaceUri,此处是通过org.w3c.dom.Node中的getNamespaceURI()方法进行获取的,以使用示例中为例,将会返回的namespaceUri=“http://www.muse.com/schema/user”

(三)Spring源码解析:自定义标签解析_第10张图片

2.2> resolve(namespaceUri)方法解析

此方法是用来获得解析该自定义标签的NamespaceHandler实现类,为下图中红框处代码:

(三)Spring源码解析:自定义标签解析_第11张图片

在此处的this.readerContext.getNamespaceHandlerResolver()方法中,实际会返回DefaultNamespaceHandlerResolver实例对象。

(三)Spring源码解析:自定义标签解析_第12张图片

(三)Spring源码解析:自定义标签解析_第13张图片

上面我们了解了DefaultNamespaceHandlerResolver实例对象的创建过程之后,那么下面我们就来分析一下它的resolve(namespaceUri)这个方法的内部实现,下面是该方法的源码部分:

(三)Spring源码解析:自定义标签解析_第14张图片

getHandlerMappings()方法中,我们获得了系统加载的所有NamespaceHandler实例对象的映射,映射关系为:key=urivalue=NamespaceHandler实现类。但是,如果我们发现加载的handlerMappings等于null,那么我们就需要去加载META-INF/spring.handlers文件中的配置信息,将其生成NamespaceHandler实例对象的映射。所以,综上所示,getHandlerMappings()方法的主要功能就是读取spring.handlers的配置文件并将配置文件缓存在map中。

(三)Spring源码解析:自定义标签解析_第15张图片

那么以我们的演示例子来说,handlerMappings中是包含了11xxxNamespaceHander实例对象的映射关系的,在下图中,红框部分就是我们自定义的UserNamespaceHandler

(三)Spring源码解析:自定义标签解析_第16张图片

那么在调用 namespaceHandler.init() 方法的时候,其实调用的是UserNamespaceHandler实例的init()方法,该方法是我们自己实现的。如下图所示:

当我们调用 handlerMappings.put(namespaceUri, namespaceHandler) 方法时,那么就将原本String类型的value值“com.muse.springbootdemo.UserNamespaceHandler”,替换为UserNamespaceHandler实例对象了。如下图所示:

(三)Spring源码解析:自定义标签解析_第17张图片

2.3> parse(...)方法解析

下面我们再来看一下的parse(...)方法,该方法是用来进行自定义标签的解析操作

(三)Spring源码解析:自定义标签解析_第18张图片

parse(...)方法中我们可以看到,首先是通过findParserForElement(element, parserContext)方法来找到localName对应的解析器。以我们上面的示例为例,我们在oldbean.xml中配置的是,那么获得了localName就等于“user”。由于我们在UserNamespaceHandler类中已经配置了userUserBeanDefinitionParser实例对象的对应关系,所以parser就拿它作为方法的返回值。具体详情,请见下图所示:

(三)Spring源码解析:自定义标签解析_第19张图片

在上面的代码逻辑中,我们已经获得到了parser示例对象(UserBeanDefinitionParser实例对象),那么我们通过调用parser对象的 parser(element, parserContext) 方法对自定义标签执行解析操作。下面是该方法涉及的源码部分:

(三)Spring源码解析:自定义标签解析_第20张图片

我们在上面可以看到,对自定义标签进行解析是在parseInternal(element, parserContext)方法中执行的,

(三)Spring源码解析:自定义标签解析_第21张图片

doParse(element, parserContext, builder)方法中,执行了真正的自定义标签解析逻辑,那么既然是自定义标签,是无法通过Spring进行解析的,而是需要我们自己提供自定义解析类XxxBeanDefinitionParser来实现doParse(...)方法的,具体如下所示:

(三)Spring源码解析:自定义标签解析_第22张图片

今天的文章内容就这些了:

写作不易,笔者几个小时甚至数天完成的一篇文章,只愿换来您几秒钟的 点赞 & 分享 。

你可能感兴趣的:(spring源码解析,源码,spring,java,后端,spring源码)