Spring自定义标签

Spring自定义标签

一、Spring配置文件中xml中标签的解析过程

在聊Spring的xml中配置文件,我们首先需要关注三个文件:

spring.handlersspring.schmas以及在schmas文件中对应位置的xsd文件

xsd文件定义了在对应的命名空间下有什么标签,标签对应的属性及属性对于的数据类型

spring.handlersspring.schmas会在 AbstactXmlApplicationContext中的loadBeanDefinitions中被加载,接下来我们重点聊一下这个方法。

1. loadBeanDefinitions(DefaultListableBeanFactory beanFactory)
  • 创建一个xml的BeanDefinitionReader,通过回调设置到BeanFactory中

  • 给reader对象设置环境对象,需要重点关注beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));这个方法将ResourceEntityResolver对象设置进入BeanDefinitionReader中,而在ResourceEntityResolver的父类构造中,将dtd的解析器和schema的解析器PluggableSchemaResolver类型的属性赋值。而在PluggableSchemaResolver类中的getSchemaMappings()会将网络上dtd地地址和对应的本地dtd文件生成map映射关系。而该方法的实际调用位置是DefaultNameSpaceHandlerResolver.resolve(String namespaceUri)中显示调用;

  • 而在IDEA调试时,我们往往会发现这个方法过早的被调用。这是由于IDEA中的调试会隐式调用该类的toString()方法,而在PluggableSchemaResolver.toString()会调用getSchemaMappings(),导致了这个结果

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dk82qg65-1623206811865)(D:\md文档\images\image-20210527114951894.png)]

@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
    // 创建一个xml的beanDefinitionReader,并通过回调设置到beanFactory中
    XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

    // 给reader对象设置环境对象
    beanDefinitionReader.setEnvironment(this.getEnvironment());
    beanDefinitionReader.setResourceLoader(this);
    beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

    //  初始化beanDefinitionReader对象,此处设置配置文件是否要进行验证
    initBeanDefinitionReader(beanDefinitionReader);
    // 开始完成beanDefinition的加载
    loadBeanDefinitions(beanDefinitionReader);
}
2. resolve(String namespaceUri)

方法位于DefaultNamespaceHandlerResolver类中,根据将xml配置文件读取成的Document中获取的namespace从而获取对应的NamespaceHandler,而该命名空间下往往有多个BeanDefinitionParser对应着不同的标签,根据这些解析类可将对应的标签解析成相应的BeanDefinition

@Override
@Nullable
public NamespaceHandler resolve(String namespaceUri) {
    // 获取所有已经配置好的handler映射
    Map<String, Object> handlerMappings = getHandlerMappings();
    // 根据命名空间找到对应的信息
    Object handlerOrClassName = handlerMappings.get(namespaceUri);
    if (handlerOrClassName == null) {
        return null;
    }
    else if (handlerOrClassName instanceof NamespaceHandler) {
        // 如果已经做过解析,直接从缓存中读取
        return (NamespaceHandler) handlerOrClassName;
    }
    else {
        // 没有做过解析,则返回的是类路径
        String className = (String) handlerOrClassName;
        try {
            // 通过反射将类路径转化为类
            Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
            if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
                throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
                                             "] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
            }
            // 实例化类
            NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
            // 调用自定义的namespaceHandler的初始化方法
            namespaceHandler.init();
            // 讲结果记录在缓存中
            handlerMappings.put(namespaceUri, namespaceHandler);
            return namespaceHandler;
        }
        catch (ClassNotFoundException ex) {
            throw new FatalBeanException("Could not find NamespaceHandler class [" + className +
                                         "] for namespace [" + namespaceUri + "]", ex);
        }
        catch (LinkageError err) {
            throw new FatalBeanException("Unresolvable class definition for NamespaceHandler class [" +
                                         className + "] for namespace [" + namespaceUri + "]", err);
        }
    }
}

具体调用链路如下所示

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PTpD159Y-1623206811866)(D:\md文档\images\image-20210528151613782.png)]

获取NamespaceHandler后调用handler.parse()方法解析获取对应的BeanDefinition

二、自定义标签的方式

1. 在resources文件夹下建对应的文件

META-INF\spring.handlersMETA-INF\spring.schemasMETA-INF\user.xsd

spring.handlers

http\://www.tianya.com/schema/user=com.tianya.spring.selftag.UserNamespaceHandler

spring.schemas

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

user.sxd

<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema"
        targetNamespace="http://www.tianya.com/schema/user"
        xmlns:tns="http://www.tianya.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>
2. model对象
package com.tianya.spring.selftag.model;

/**
 * ClassName: User <br/>
 * Description: <br/>
 * date: 2021/5/27 9:59<br/>
 *
 * @author tianya<br />
 */
public class User {
    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;
    }

    @Override
    public String toString() {
        return "User{" +
                "userName='" + userName + '\'' +
                ", email='" + email + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}

3. BeanDefinitionParse对象
package com.tianya.spring.selftag;

import com.tianya.spring.selftag.model.User;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;

/**
 * ClassName: UserBeanDefinitionParser <br/>
 * Description: <br/>
 * date: 2021/5/27 10:00<br/>
 *
 * @author tianya<br />
 */
public class UserBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {

    /**
     * 返回属性值所对应的对象
     * @param element
     * @return
     */
    @Override
    protected Class<?> getBeanClass(Element element) {
        return User.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(password)){
            builder.addPropertyValue("password", password);
        }

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

4. NamespaceHandler对象
package com.tianya.spring.selftag;

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

/**
 * ClassName: UserNamespaceHandler <br/>
 * Description: <br/>
 * date: 2021/5/27 9:21<br/>
 *
 * @author tianya<br />
 */
public class UserNamespaceHandler extends NamespaceHandlerSupport {

    public void init() {
        registerBeanDefinitionParser("user", new UserBeanDefinitionParser());
    }
}
5. applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:tianya="http://www.tianya.com/schema/user"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.tianya.com/schema/user http://www.tianya.com/schema/user.xsd">

    <tianya:user id="tianya" email="1044916104@qq.com" password="123456" userName="wxp"></tianya:user>

</beans>
6.测试类
public class UserTagsTest {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
        User user = (User) ac.getBean("tianya");
        System.out.println(user);
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值