spring自定义标签解析

记录一下spring自定义标签理解过程。
关键的三个点

  1. spring.handlers和spring.schemas配置文件解析。
  2. user.xsd对象解析id和name作为spring的基础数据会被解析
  3. 了解一下NamespaceHandlerSupport的parser方法调用逻辑,xml对象如何被解析。

文件夹目录

在这里插入图片描述

示例执行结果
在这里插入图片描述
在这里插入图片描述

主题目录

***point 1/3> MATE-INF目录写死
***point 2/3> 调用DefaultNamespaceHandlerResolver.init();
***point 3/3> AbstractSingleBeanDefinitionParser.doParse();

扩展解析
***point 0/0>解析xml配置文件id和其父类属性
NamespaceHandlerSupport
public BeanDefinition parse(Element element, ParserContext parserContext) {
// 获取元素的解析器
BeanDefinitionParser parser = findParserForElement(element, parserContext);
return (parser != null ? parser.parse(element, parserContext) : null);
}
此处代码解析xml的id和name属性
在这里插入图片描述
最后都是存储到spring容器的beanDefinitionMap和beanDefinition内部中解析对象可能是xml,注解比如commonpent注解,测试时根据实际情况构造。
在这里插入图片描述

***point 1/3> MATE-INF目录写死
DefaultNamespaceHandlerResolver字段DEFAULT_HANDLER_MAPPINGS_LOCATION = “META-INF/spring.handlers”;
对应写死的配置文件路径。此处代码解析spring.handlers配置文件

在这里插入图片描述

PluggableSchemaResolver字段DEFAULT_SCHEMA_MAPPINGS_LOCATION = “META-INF/spring.schemas”;
对应写死的配置文件路径。此处代码解析spring.schemas配置文件

在这里插入图片描述

spring自定义标签的代码

启动类

package com.mashibing.selftag2;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestSelfTag2 {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("selftag2.xml");

        User2 user2=(User2)context.getBean("testbean2");
        System.out.println("username:"+user2.getUsername()+"  "+"email:"+user2.getEmail());
    }
}

标签类,转换对象

package com.mashibing.selftag2;

public class User2 {

    private String username;
    private String email;
    private String password;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

标签解析器

package com.mashibing.selftag2;

import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;

public class User2BeanDefinitionParser  extends AbstractSingleBeanDefinitionParser {

    // 返回属性值所对应的对象
    @Override
    protected Class<?> getBeanClass(Element element) {
        return User2.class;
    }

    @Override
    protected void doParse(Element element, BeanDefinitionBuilder builder) {
        // 获取标签具体的属性值
        String userName = element.getAttribute("userName");
        String email = element.getAttribute("email");
        String password = element.getAttribute("password");

        if(StringUtils.hasText(userName)){
            builder.addPropertyValue("username",userName);
        }
        if (StringUtils.hasText(email)){
            builder.addPropertyValue("email",email);
        }
        if (StringUtils.hasText(password)){
            builder.addPropertyValue("password",password);
        }
    }
}

标签命名句柄

package com.mashibing.selftag2;

import org.springframework.beans.factory.xml.NamespaceHandlerSupport;

public class User2NamespaceHandler extends NamespaceHandlerSupport {

    @Override
    public void init() {
        registerBeanDefinitionParser("user",new User2BeanDefinitionParser());
    }
}

配置文件

spring.handlers

http\://www.nanami.com/schema/user=com.mashibing.selftag2.User2NamespaceHandler

spring.schemas

http\://www.nanami.com/schema/user.xsd=META-INF/user.xsd

user.xsd

<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema"
        targetNamespace="http://www.nanami.com/schema/user"
        xmlns:tns="http://www.nanami.com/schema/user"
        elementFormDefault="qualified">
    <element name="user">
        <complexType>
            <attribute name ="id" type = "string"/>
            <attribute name ="userName" type = "string"/>
            <attribute name ="email" type = "string"/>
            <attribute name ="password" type="string"/>
        </complexType>
    </element>
</schema>

selftag2.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:a2="http://www.nanami.com/schema/user"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.nanami.com/schema/user http://www.nanami.com/schema/user.xsd
       ">


    <a2:user id = "testbean2" userName = "test_lee_testbean2" email = "test_email@qq.com"/>
</beans>

关键源码

1.xml对象解析代码。

2.parse方法解析对象
此逻辑分为两块,一块是调用init方法User2NamespaceHandler继承自NamespaceHandlerSupport
另外一块是调用doParse方法,映射User2BeanDefinitionParser继承自AbstractSingleBeanDefinitionParser

备注:路径描述
AbstractApplicationContext的refersh作用基础方法
public void refresh() throws BeansException, IllegalStateException {
	//Tell the subclass to refresh the internal bean factory.
	// 创建容器对象:DefaultListableBeanFactory
	// 加载xml配置文件的属性值到当前工厂中,最重要的就是BeanDefinition
	ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
}
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
	// 初始化BeanFactory,并进行XML文件读取,并将得到的BeanFactory记录在当前实体的属性中
	refreshBeanFactory();
}

AbstractRefreshableApplicationContext
protected final void refreshBeanFactory() throws BeansException {
	// 初始化documentReader,并进行XML文件读取及解析,默认命名空间的解析,自定义标签的解析
	loadBeanDefinitions(beanFactory);
}

AbstractXmlApplicationContext
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
	// 开始完成beanDefinition的加载
	loadBeanDefinitions(beanDefinitionReader);
}
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
	reader.loadBeanDefinitions(configLocations);
}


AbstractBeanDefinitionReader
public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
	count += loadBeanDefinitions(location);
}
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
	return loadBeanDefinitions(location, null);
}
public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
	int count = loadBeanDefinitions(resources);
}
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
	count += loadBeanDefinitions(resource);
}


XmlBeanDefinitionReader
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
	return loadBeanDefinitions(new EncodedResource(resource));
}
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
	// 逻辑处理的核心步骤
	return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
	int count = registerBeanDefinitions(doc, resource);
}
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
	// 完成具体的解析过程
	documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
}
public XmlReaderContext createReaderContext(Resource resource) {
	return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
			this.sourceExtractor, this, getNamespaceHandlerResolver());
}

DefaultBeanDefinitionDocumentReader
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
	this.readerContext = readerContext;
	doRegisterBeanDefinitions(doc.getDocumentElement());
}

DefaultBeanDefinitionDocumentReader
protected void doRegisterBeanDefinitions(Element root) {
	parseBeanDefinitions(root, this.delegate);
}
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
	delegate.parseCustomElement(ele);
}

BeanDefinitionParserDelegate***point***
对应自定义AbstractSingleBeanDefinitionParserNamespaceHandlerSupport的实现类调用。
public BeanDefinition parseCustomElement(Element ele) {
		***point 2/3> 调用DefaultNamespaceHandlerResolver.init();
		// 根据命名空间找到对应的NamespaceHandlerspring
		NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
		***point 3/3> AbstractSingleBeanDefinitionParser.doParse();
		// 调用自定义的NamespaceHandler进行解析
		return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}

public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
	return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}

NamespaceHandlerSupport
public BeanDefinition parse(Element element, ParserContext parserContext) {
		// 获取元素的解析器,
		BeanDefinitionParser parser = findParserForElement(element, parserContext);
		return (parser != null ? parser.parse(element, parserContext) : null);
}

AbstractBeanDefinitionParser
public final BeanDefinition parse(Element element, ParserContext parserContext) {
	AbstractBeanDefinition definition = parseInternal(element, parserContext);
}


AbstractSingleBeanDefinitionParser
protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
	doParse(element, parserContext, builder);
}

关联两个实现类的调用
使用ClassUtils.forName方法实例化类路径,实例化以后继续深入,会调用init()和doParser()方法。
在这里插入图片描述

DefaultNamespaceHandlerResolver
namespaceHandler.init();
// 调用自定义的namespaceHandler的初始化方法
在这里插入图片描述
***point 2/3> 调用DefaultNamespaceHandlerResolver.init();

AbstractSingleBeanDefinitionParser
// 调用子类重写的doParse方法进行解析
doParse(element, parserContext, builder);
备注:BeanDefinitionBuilder对象动态映射值,通过key和value方式
在这里插入图片描述
***point 3/3> AbstractSingleBeanDefinitionParser.doParse();

gradle运行时的一些逻辑
在这里插入图片描述

资源文件:https://download.youkuaiyun.com/download/weixin_50750933/74792262
断点环境:https://download.youkuaiyun.com/download/weixin_50750933/74903063

感受
想起和最开始学习ssh框架,xml配置文件写entity对象。当时也看了一下spring的对接dom对象解析。也是写死的内容。
从refresh方法到具体实现方法,仅仅只是一个调用。就走了几十个方法,N个类。只能先用大量时间先熟悉了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值