spring5/springboot2源码学习 -- 自定义标签的解析

概念

在spring的xml配置中,我们除了可以使用默认的beans标签外,还可以自己定义标签。

其实spring自己内部的很多模块也是使用了自定义标签来实现功能的。

还有一些别的比较知名的案例,比如:dubbo

用法

首先,随便定义一个想通过标签来配置的bean:

public class CustomBean {
	private String name;
 	//get set
}

自定义一个解析器:

public class CustomBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
    @Override
    protected Class<?> getBeanClass(Element element) {
        return CustomBean.class;
    }

    @Override
    protected void doParse(Element element, BeanDefinitionBuilder builder) {
        String name = element.getAttribute("name");
        if(StringUtils.isNotBlank(name)){
            builder.addPropertyValue("name",name);
        }
    }
}

自定义一个handler

public class CustomHandler extends NamespaceHandlerSupport {
    @Override
    public void init() {
        registerBeanDefinitionParser("custom",new CustomBeanDefinitionParser());
    }
}

定义一个xsd验证文件

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

spring.handlers文件

http\://www.my.org/schema/custom=com.pk.study.spring.customer.CustomHandler

spring.schemas文件

http\://www.my.org/schema/custom.xsd=META-INF/custom.xsd

spring的配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:custom="http://www.my.org/schema/custom"
       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.my.org/schema/custom  http://www.my.org/schema/custom.xsd">

    <custom:custom id="custom" name="custom name"/>
</beans>

测试类:

public class CustomTest {

    @Test
    public void test(){
        //创建一个实现了BeanDefinitionRegistry的BeanFactory实现
        //DefaultListableBeanFactory也是绝大多数场景下,BeanFactory的具体实现
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        //XmlBeanDefinitionReader创建,从名字可以看出来 这个类是用来从xml文件中读取配置的
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
        //具体解析xml文件中的配置,并注册到BeanDefinitionRegistry中去
        reader.loadBeanDefinitions(new ClassPathResource("custom.xml"));

        CustomBean userInfo = beanFactory.getBean(CustomBean.class);
        System.out.println(userInfo.getName());
    }
}

会输出custom name

实现

自定义标签的解析入口在BeanDefinitionParserDelegate.parseCustomElement()方法:

public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
	//获取标签的namespace
  String namespaceUri = getNamespaceURI(ele);
  if (namespaceUri == null) {
    return null;
  }
  //获取namespace对应的NamespaceHandler
  NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
  if (handler == null) {
    error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
    return null;
  }
  //调用自定义的handler去解析标签
  return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}

我们自定义的handler并没有重写parse方法,而是重写了doParse方法,这也是借助了spring的模板方法模式,由父类AbstractBeanDefinitionParser定义了方法执行的框架,而我们自己的子类仅仅实现了parse方法中的一小部分。进入AbstractBeanDefinitionParser.parse()方法:

public final BeanDefinition parse(Element element, ParserContext parserContext) {
  //主要逻辑
  AbstractBeanDefinition definition = parseInternal(element, parserContext);
  if (definition != null && !parserContext.isNested()) {
    try {
      //必须要有id属性
      String id = resolveId(element, definition, parserContext);
      if (!StringUtils.hasText(id)) {
        parserContext.getReaderContext().error(
          "Id is required for element '" + parserContext.getDelegate().getLocalName(element)
          + "' when used as a top-level tag", element);
      }
      String[] aliases = null;
      if (shouldParseNameAsAliases()) {
        String name = element.getAttribute(NAME_ATTRIBUTE);
        if (StringUtils.hasLength(name)) {
          aliases = StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(name));
        }
      }
      BeanDefinitionHolder holder = new BeanDefinitionHolder(definition, id, aliases);
      registerBeanDefinition(holder, parserContext.getRegistry());
      if (shouldFireEvents()) {
        BeanComponentDefinition componentDefinition = new BeanComponentDefinition(holder);
        postProcessComponentDefinition(componentDefinition);
        parserContext.registerComponent(componentDefinition);
      }
    }
    catch (BeanDefinitionStoreException ex) {
      String msg = ex.getMessage();
      parserContext.getReaderContext().error((msg != null ? msg : ex.toString()), element);
      return null;
    }
  }
  return definition;
}

进入AbstractBeanDefinitionParser.parseInternal()

protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
  BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
  //判断是否有父标签
  String parentName = getParentName(element);
  if (parentName != null) {
    builder.getRawBeanDefinition().setParentName(parentName);
  }
  Class<?> beanClass = getBeanClass(element);
  if (beanClass != null) {
    builder.getRawBeanDefinition().setBeanClass(beanClass);
  }
  else {
    String beanClassName = getBeanClassName(element);
    if (beanClassName != null) {
      builder.getRawBeanDefinition().setBeanClassName(beanClassName);
    }
  }
  builder.getRawBeanDefinition().setSource(parserContext.extractSource(element));
  BeanDefinition containingBd = parserContext.getContainingBeanDefinition();
  if (containingBd != null) {
    // Inner bean definition must receive same scope as containing bean.
    builder.setScope(containingBd.getScope());
  }
  if (parserContext.isDefaultLazyInit()) {
    // Default-lazy-init applies to custom bean definitions as well.
    builder.setLazyInit(true);
  }
  //调用我们自定义的handler的方法
  doParse(element, parserContext, builder);
  return builder.getBeanDefinition();
}

结语

自定义标签的解析。可以自己定义插件集成到spring中。

(水平有限,最近在看spring源码,分享学习过程,希望对各位有点微小的帮助。如有错误,请指正~)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值