Spring 自定义标签解析

  • Author: HuiFer
  • 源码阅读仓库: SourceHot-Spring
  • 与自定义标签解析相关的类
    1. org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser
    2. org.springframework.beans.factory.xml.NamespaceHandlerSupport
  • 开始源码之前先搭建一个环境

环境搭建

  • 创建对象
  1. public class UserXtd {
  2. private String userName;
  3. private String emailAddress;
  4. public String getUserName() {
  5. return userName;
  6. }
  7. public void setUserName(String userName) {
  8. this.userName = userName;
  9. }
  10. public String getEmailAddress() {
  11. return emailAddress;
  12. }
  13. public void setEmailAddress(String emailAddress) {
  14. this.emailAddress = emailAddress;
  15. }
  16. }
  • 创建 xsd 文件
  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <schema xmlns="http://www.w3.org/2001/XMLSchema"
  3. targetNamespace="http://www.huifer.com/schema/user"
  4. elementFormDefault="qualified">
  5. <element name="myUser">
  6. <complexType>
  7. <attribute name="id" type="string"/>
  8. <attribute name="userName" type="string"/>
  9. <attribute name="emailAddress" type="string"/>
  10. </complexType>
  11. </element>
  12. </schema>
  • 创建 namespaceHandler
  1. public class UserNamespaceHandler extends NamespaceHandlerSupport {
  2. @Override
  3. public void init() {
  4. registerBeanDefinitionParser("myUser", new UserBeanDefinitionParser());
  5. }
  6. }
  • 创建 beanDefinitionParser
  1. public class UserBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
  2. /**
  3. * 标签对应class
  4. * @param element the {@code Element} that is being parsed
  5. * @return
  6. */
  7. @Override
  8. protected Class<?> getBeanClass(Element element) {
  9. return UserXtd.class;
  10. }
  11. @Override
  12. protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
  13. // 获取 userName 标签属性值
  14. String name = element.getAttribute("userName");
  15. // 获取 emailAddress 标签属性值
  16. String address = element.getAttribute("emailAddress");
  17. if (StringUtils.hasText(name)) {
  18. builder.addPropertyValue("userName", name);
  19. }
  20. if (StringUtils.hasText(address)) {
  21. builder.addPropertyValue("emailAddress", address);
  22. }
  23. }
  24. }
  • 创建 resource/META-INF/spring.handlers
  1. http\://www.huifer.com/schema/user=com.huifer.source.spring.parser.UserNamespaceHandler
  • 创建 resource/META-INF/spring.schemas
  1. http\://www.huifer.com/schema/user.xsd=META-INF/spring-test.xsd
  • 创建测试用例 xml
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:myname="http://www.huifer.com/schema/user"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
  6. http://www.huifer.com/schema/user http://www.huifer.com/schema/user.xsd
  7. ">
  8. <myname:myUser id="testUserBean" userName="huifer" emailAddress="huifer97@163.com"/>
  9. </beans>
  • 创建 Java 运行方法
  1. /**
  2. * 自定义标签测试用例
  3. */
  4. public class XSDDemo {
  5. public static void main(String[] args) {
  6. ApplicationContext applicationContext = new ClassPathXmlApplicationContext("XTD-xml.xml");
  7. UserXtd user = (UserXtd) applicationContext.getBean("testUserBean");
  8. System.out.println(user.getEmailAddress());
  9. }
  10. }
  • 这里我们希望输出结果是huifer97@163.com,运行后结果也确实是huifer97@163.com

解析 DefaultNamespaceHandlerResolver

  • 入口方法org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.parseBeanDefinitions
  1. protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
  2. if (delegate.isDefaultNamespace(root)) {
  3. NodeList nl = root.getChildNodes();
  4. for (int i = 0; i < nl.getLength(); i++) {
  5. Node node = nl.item(i);
  6. if (node instanceof Element) {
  7. Element ele = (Element) node;
  8. if (delegate.isDefaultNamespace(ele)) {
  9. // 不同标签的解析
  10. parseDefaultElement(ele, delegate);
  11. }
  12. else {
  13. // 非spring 默认标签解析
  14. delegate.parseCustomElement(ele);
  15. }
  16. }
  17. }
  18. }
  19. else {
  20. delegate.parseCustomElement(root);
  21. }
  22. }
  • 调用链路
  • org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.parseCustomElement(org.w3c.dom.Element)
    • org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.parseCustomElement(org.w3c.dom.Element, org.springframework.beans.factory.config.BeanDefinition)
  1. /**
  2. * Parse a custom element (outside of the default namespace).
  3. * <p>
  4. * 自定义标签解析
  5. *
  6. * @param ele the element to parse
  7. * @param containingBd the containing bean definition (if any)
  8. * @return the resulting bean definition
  9. */
  10. @Nullable
  11. public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
  12. // 自定义标签解析
  13. String namespaceUri = getNamespaceURI(ele);
  14. if (namespaceUri == null) {
  15. return null;
  16. }
  17. // 根据命名空间获取处理类
  18. NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
  19. if (handler == null) {
  20. error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
  21. return null;
  22. }
  23. // 自定义处理器
  24. return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
  25. }

image-20200109084131415

  • http://www.huifer.com/schema/user和我们定义的 xsd 文件中的 url 相同,如何找到对应的 NamespaceHandler,在META-INF/spring.handlers中有定义,

    http\://www.huifer.com/schema/user=com.huifer.source.spring.parser.UserNamespaceHandler

    NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);这行代码就是获取spring.handlers中的定义

  • 处理方法org.springframework.beans.factory.xml.DefaultNamespaceHandlerResolver.resolve

  1. /**
  2. * Locate the {@link NamespaceHandler} for the supplied namespace URI
  3. * from the configured mappings.
  4. *
  5. * 根据 namespaceUri 获取对应的 {@link NamespaceHandler}
  6. * @param namespaceUri the relevant namespace URI
  7. * @return the located {@link NamespaceHandler}, or {@code null} if none found
  8. */
  9. @Override
  10. @Nullable
  11. public NamespaceHandler resolve(String namespaceUri) {
  12. // 获取handlerMapping
  13. Map<String, Object> handlerMappings = getHandlerMappings();
  14. // 从 handlerMapping 中获取类名
  15. Object handlerOrClassName = handlerMappings.get(namespaceUri);
  16. if (handlerOrClassName == null) {
  17. return null;
  18. }
  19. // 判断是否处理过,处理过直接返回
  20. else if (handlerOrClassName instanceof NamespaceHandler) {
  21. return (NamespaceHandler) handlerOrClassName;
  22. }
  23. else {
  24. // 没有处理,进行反射还原类
  25. String className = (String) handlerOrClassName;
  26. try {
  27. // 通过反射还原类
  28. Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
  29. if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
  30. throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
  31. "] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
  32. }
  33. // 初始化类
  34. NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
  35. // 调用init()方法,自定义类中实现
  36. namespaceHandler.init();
  37. // 放入缓存
  38. handlerMappings.put(namespaceUri, namespaceHandler);
  39. // 返回自定义的 namespaceHandler
  40. return namespaceHandler;
  41. }
  42. catch (ClassNotFoundException ex) {
  43. throw new FatalBeanException("Could not find NamespaceHandler class [" + className +
  44. "] for namespace [" + namespaceUri + "]", ex);
  45. }
  46. catch (LinkageError err) {
  47. throw new FatalBeanException("Unresolvable class definition for NamespaceHandler class [" +
  48. className + "] for namespace [" + namespaceUri + "]", err);
  49. }
  50. }
  51. }
  • org.springframework.beans.factory.xml.DefaultNamespaceHandlerResolver#getHandlerMappings跟踪这个方法
  1. /**
  2. * Load the specified NamespaceHandler mappings lazily.
  3. *
  4. * 获取handlerMappings
  5. */
  6. private Map<String, Object> getHandlerMappings() {
  7. Map<String, Object> handlerMappings = this.handlerMappings;
  8. }

image-20200109085606240

  • 这里直接存在数据了,他是从什么时候加载的?

  • org.springframework.beans.factory.xml.XmlBeanDefinitionReader#registerBeanDefinitions

    这个方法在注册 bean 定义的时候调用

    1. public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
    2. BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
    3. int countBefore = getRegistry().getBeanDefinitionCount();
    4. // 注册方法
    5. // createReaderContext 初始化HandlerMapping
    6. documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
    7. return getRegistry().getBeanDefinitionCount() - countBefore;
    8. }
  • 继续跟踪createReaderContext

    1. org.springframework.beans.factory.xml.XmlBeanDefinitionReader#createReaderContext
    1. public XmlReaderContext createReaderContext(Resource resource) {
    2. return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
    3. this.sourceExtractor, this, getNamespaceHandlerResolver());
    4. }
  • 继续跟踪getNamespaceHandlerResolver

    org.springframework.beans.factory.xml.XmlBeanDefinitionReader#getNamespaceHandlerResolver

    1. public NamespaceHandlerResolver getNamespaceHandlerResolver() {
    2. if (this.namespaceHandlerResolver == null) {
    3. this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver();
    4. }
    5. return this.namespaceHandlerResolver;
    6. }
  • 继续跟踪createDefaultNamespaceHandlerResolver

    org.springframework.beans.factory.xml.XmlBeanDefinitionReader#createDefaultNamespaceHandlerResolver

    1. protected NamespaceHandlerResolver createDefaultNamespaceHandlerResolver() {
    2. ClassLoader cl = (getResourceLoader() != null ? getResourceLoader().getClassLoader() : getBeanClassLoader());
    3. return new DefaultNamespaceHandlerResolver(cl);
    4. }
  • 继续跟踪DefaultNamespaceHandlerResolver

    org.springframework.beans.factory.xml.DefaultNamespaceHandlerResolver

    1. public DefaultNamespaceHandlerResolver(@Nullable ClassLoader classLoader) {
    2. this(classLoader, DEFAULT_HANDLER_MAPPINGS_LOCATION);
    3. }

    他回到了我们之前疑问的地方 handlerMappings 如何出现的

    断点

    image-20200109090456547

    1. public DefaultNamespaceHandlerResolver(@Nullable ClassLoader classLoader) {
    2. this(classLoader, DEFAULT_HANDLER_MAPPINGS_LOCATION);
    3. }

    public static final String DEFAULT_HANDLER_MAPPINGS_LOCATION = "META-INF/spring.handlers";

    image-20200109090655157

    此时还是空

    走完

    image-20200109091216505

    1. @Override
    2. public String toString() {
    3. return "NamespaceHandlerResolver using mappings " + getHandlerMappings();
    4. }
  1. /**
  2. * Load the specified NamespaceHandler mappings lazily.
  3. *
  4. * 获取handlerMappings
  5. */
  6. private Map<String, Object> getHandlerMappings() {
  7. Map<String, Object> handlerMappings = this.handlerMappings;
  8. // 缓存不存在
  9. if (handlerMappings == null) {
  10. synchronized (this) {
  11. handlerMappings = this.handlerMappings;
  12. if (handlerMappings == null) {
  13. if (logger.isTraceEnabled()) {
  14. logger.trace("Loading NamespaceHandler mappings from [" + this.handlerMappingsLocation + "]");
  15. }
  16. try {
  17. // 将本地文件读出
  18. Properties mappings =
  19. PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
  20. if (logger.isTraceEnabled()) {
  21. logger.trace("Loaded NamespaceHandler mappings: " + mappings);
  22. }
  23. handlerMappings = new ConcurrentHashMap<>(mappings.size());
  24. // 转换成map结构
  25. CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings);
  26. this.handlerMappings = handlerMappings;
  27. }
  28. catch (IOException ex) {
  29. throw new IllegalStateException(
  30. "Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", ex);
  31. }
  32. }
  33. }
  34. }
  35. return handlerMappings;
  36. }

image-20200109094032421

org.springframework.beans.factory.xml.DefaultNamespaceHandlerResolver#resolve

  1. @Override
  2. @Nullable
  3. public NamespaceHandler resolve(String namespaceUri) {
  4. // 获取handlerMapping
  5. Map<String, Object> handlerMappings = getHandlerMappings();
  6. // 从 handlerMapping 中获取类名
  7. Object handlerOrClassName = handlerMappings.get(namespaceUri);
  8. if (handlerOrClassName == null) {
  9. return null;
  10. }
  11. // 判断是否处理过,处理过直接返回
  12. else if (handlerOrClassName instanceof NamespaceHandler) {
  13. return (NamespaceHandler) handlerOrClassName;
  14. }
  15. else {
  16. // 没有处理,进行反射还原类
  17. String className = (String) handlerOrClassName;
  18. try {
  19. // 通过反射还原类
  20. Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
  21. if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
  22. throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
  23. "] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
  24. }
  25. // 初始化类
  26. NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
  27. // 调用init()方法,自定义类中实现
  28. namespaceHandler.init();
  29. // 放入缓存
  30. handlerMappings.put(namespaceUri, namespaceHandler);
  31. // 返回自定义的 namespaceHandler
  32. return namespaceHandler;
  33. }
  34. catch (ClassNotFoundException ex) {
  35. throw new FatalBeanException("Could not find NamespaceHandler class [" + className +
  36. "] for namespace [" + namespaceUri + "]", ex);
  37. }
  38. catch (LinkageError err) {
  39. throw new FatalBeanException("Unresolvable class definition for NamespaceHandler class [" +
  40. className + "] for namespace [" + namespaceUri + "]", err);
  41. }
  42. }
  43. }

执行init方法

  1. public class UserNamespaceHandler extends NamespaceHandlerSupport {
  2. @Override
  3. public void init() {
  4. registerBeanDefinitionParser("myUser", new UserBeanDefinitionParser());
  5. }
  6. }
  1. /**
  2. * Subclasses can call this to register the supplied {@link BeanDefinitionParser} to
  3. * handle the specified element. The element name is the local (non-namespace qualified)
  4. * name.
  5. *
  6. * 将标签名称,标签解析类放入
  7. */
  8. protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) {
  9. this.parsers.put(elementName, parser);
  10. }
  • 方法走完,回到开始的方法

    1. /**
    2. * Parse a custom element (outside of the default namespace).
    3. * <p>
    4. * 自定义标签解析
    5. *
    6. * @param ele the element to parse
    7. * @param containingBd the containing bean definition (if any)
    8. * @return the resulting bean definition
    9. */
    10. @Nullable
    11. public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
    12. // 自定义标签解析
    13. String namespaceUri = getNamespaceURI(ele);
    14. if (namespaceUri == null) {
    15. return null;
    16. }
    17. // 根据命名空间获取处理类
    18. // org.springframework.beans.factory.xml.DefaultNamespaceHandlerResolver.resolve
    19. NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
    20. if (handler == null) {
    21. error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
    22. return null;
    23. }
    24. // 自定义处理器
    25. return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
    26. }

image-20200109092801572

org.springframework.beans.factory.xml.NamespaceHandler#parse

  • org.springframework.beans.factory.xml.NamespaceHandlerSupport.parse

    1. @Override
    2. @Nullable
    3. public BeanDefinition parse(Element element, ParserContext parserContext) {
    4. BeanDefinitionParser parser = findParserForElement(element, parserContext);
    5. return (parser != null ? parser.parse(element, parserContext) : null);
    6. }

org.springframework.beans.factory.xml.NamespaceHandlerSupport#findParserForElement

  1. @Nullable
  2. private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
  3. // 获取标签名称
  4. String localName = parserContext.getDelegate().getLocalName(element);
  5. // 在map中获取对应的标签解析类
  6. BeanDefinitionParser parser = this.parsers.get(localName);
  7. // 空报错
  8. if (parser == null) {
  9. parserContext.getReaderContext().fatal(
  10. "Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
  11. }
  12. // 不为空返回
  13. return parser;
  14. }

image-20200109093242494

org.springframework.beans.factory.xml.BeanDefinitionParser#parse

  • org.springframework.beans.factory.xml.AbstractBeanDefinitionParser#parse
  1. public final BeanDefinition parse(Element element, ParserContext parserContext) {
  2. /**
  3. * {@link AbstractSingleBeanDefinitionParser#parseInternal(org.w3c.dom.Element, org.springframework.beans.factory.xml.ParserContext)}
  4. */
  5. AbstractBeanDefinition definition = parseInternal(element, parserContext);
  6. }

org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser#parseInternal

  1. @Override
  2. protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
  3. BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
  4. String parentName = getParentName(element);
  5. if (parentName != null) {
  6. builder.getRawBeanDefinition().setParentName(parentName);
  7. }
  8. // 调用自己实现的方法 com.huifer.source.spring.parser.UserBeanDefinitionParser.getBeanClass
  9. Class<?> beanClass = getBeanClass(element);
  10. if (beanClass != null) {
  11. builder.getRawBeanDefinition().setBeanClass(beanClass);
  12. }
  13. else {
  14. // getBeanClassName 同样也是可以在自定义的解析类中实现
  15. String beanClassName = getBeanClassName(element);
  16. if (beanClassName != null) {
  17. builder.getRawBeanDefinition().setBeanClassName(beanClassName);
  18. }
  19. }
  20. builder.getRawBeanDefinition().setSource(parserContext.extractSource(element));
  21. BeanDefinition containingBd = parserContext.getContainingBeanDefinition();
  22. if (containingBd != null) {
  23. // Inner bean definition must receive same scope as containing bean.
  24. // 设置scope
  25. builder.setScope(containingBd.getScope());
  26. }
  27. if (parserContext.isDefaultLazyInit()) {
  28. // Default-lazy-init applies to custom bean definitions as well.
  29. // 设置 lazy-init
  30. builder.setLazyInit(true);
  31. }
  32. // 执行解析方法,在自定义解析类中存在com.huifer.source.spring.parser.UserBeanDefinitionParser.doParse
  33. doParse(element, parserContext, builder);
  34. return builder.getBeanDefinition();
  35. }

image-20200109094654409

执行com.huifer.source.spring.parser.UserBeanDefinitionParser#doParse

  1. @Override
  2. protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
  3. // 获取 userName 标签属性值
  4. String name = element.getAttribute("userName");
  5. // 获取 emailAddress 标签属性值
  6. String address = element.getAttribute("emailAddress");
  7. if (StringUtils.hasText(name)) {
  8. builder.addPropertyValue("userName", name);
  9. }
  10. if (StringUtils.hasText(address)) {
  11. builder.addPropertyValue("emailAddress", address);
  12. }
  13. }