Spring自定义命名空间的解析原理与实现

13 篇文章 0 订阅

Spring自定义命名空间的解析原理与实现

原理

由上篇文章refresh() -> obtainFreshBeanFactory()跟踪源码可知Spring在解析除默认命名空间import、alias、bean、beans以外的命名空间都会调用BeanDefinitionParserDelegate的**BeanDefinition parseCustomElement(Element ele)**方法进行解析

BeanDefinitionParserDelegate -> parseCustomElement()

public BeanDefinition parseCustomElement(Element ele) {
  return parseCustomElement(ele, null);
}

@Nullable
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
  // 获取对应的命名空间
  String namespaceUri = getNamespaceURI(ele);
  if (namespaceUri == null) {
    return null;
  }
  // 根据命名空间找到对应的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;
  }
  // 调用自定义的NamespaceHandler进行解析
  return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}

DefaultNamespaceHandlerResolver -> resolve():获取handler的方法

resolve()中可识别找到自定义的NamespaceHandler对象

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);
      }
   }
}

NamespaceHandlerSupport -> parse():找到对应的handler进行解析

public BeanDefinition parse(Element element, ParserContext parserContext) {
  // 获取元素的解析器
  BeanDefinitionParser parser = findParserForElement(element, parserContext);
  return (parser != null ? parser.parse(element, parserContext) : null);
}
通常调用的为AbstractBeanDefinitionParser -> parse()
public final BeanDefinition parse(Element element, ParserContext parserContext) {
  // 主要实现自定义解析方法
   AbstractBeanDefinition definition = parseInternal(element, parserContext);
   if (definition != null && !parserContext.isNested()) {
      try {
         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));
            }
         }
         // 将AbstractBeanDefinition转换为BeanDefinitionHolder并注册
         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;
}
AbstractSingleBeanDefinitionParser -> parseInternal() 其中 doParse() 为自定义实现解析
protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
   BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
   String parentName = getParentName(element);
   if (parentName != null) {
      builder.getRawBeanDefinition().setParentName(parentName);
   }
   // 获取自定义标签中的class,此时会调用自定义解析器
   Class<?> beanClass = getBeanClass(element);
   if (beanClass != null) {
      builder.getRawBeanDefinition().setBeanClass(beanClass);
   }
   else {
      // 若子类没有重写getBeanClass方法则尝试检查子类是否重写getBeanClassName方法
      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.
      // 若存在父类则使用父类的scope属性
      builder.setScope(containingBd.getScope());
   }
   if (parserContext.isDefaultLazyInit()) {
      // Default-lazy-init applies to custom bean definitions as well.
      // 配置延迟加载
      builder.setLazyInit(true);
   }
   // 调用子类重写的doParse方法进行解析
   doParse(element, parserContext, builder);
   return builder.getBeanDefinition();
}
AbstractSingleBeanDefinitionParser -> doParse() 调用子类重写的doParse方法进行解析
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
   doParse(element, builder);
}

// 实际执行(什么也没做,预示子类实现自行扩展功能)
protected void doParse(Element element, BeanDefinitionBuilder builder) {
}

实现

  1. 自定义xsd文件,模仿spring中的xsd文件进行自定义xsd文件编写

  2. 自定义实体类定义属性对应xsd中对应的自定义标签

  3. 自定义BeanDefinitionParser类继承org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser重写

    1. Class<?> getBeanClass(Element element):返回自定义实体的class对象
    2. void doParse(Element element, BeanDefinitionBuilder builder):实现重文档Element中获取参数存入BeanDefinitionBuilder
  4. 自定义NamespaceHandler处理类继承org.springframework.beans.factory.xml.NamespaceHandlerSupport重写

    • init():调用void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser)将具体的自定义标签名与自定义的解析转换对象以kv形式存入map集合
  5. resource目录下新建META-INF文件夹

  6. META-INF文件夹中新增

    1. spring.handlers文件:模仿spring中的handler文件

    2. spring.schemas文件:模仿spring中的schemas文件

      在自定义时可能spring.handlers/spring.schemas这种写法可能会抛出异常,将s改为S即可
      异常信息

      FAILURE: Build failed with an exception.
      
      * Where:
      Script '/Users/armin/IdeaProjects/spring-framework/gradle/docs.gradle' line: 228
      
      * What went wrong:
      A problem occurred evaluating script.
      > assert shortName != key
               |         |  |
               |         |  'http://www.armin.com/schema/user.xsd'
               |         false
               'http://www.armin.com/schema/user.xsd'
      
      * Try:
      Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.
      
      * Get more help at https://help.gradle.org
      
      BUILD FAILED in 659ms
      

      或如以拉下源码在源码中找到spring-framework -> gradle -> docs.gradle中注释以下代码

      //		for (def key : schemas.keySet()) {
      //			def shortName = key.replaceAll(/http.*schema.(.*).spring-.*/, '$1')
      //			assert shortName != key
      //			File xsdFile = module.sourceSets.main.resources.find {
      //				(it.path.endsWith(schemas.get(key)) || it.path.endsWith(schemas.get(key).replaceAll('\\/','\\\\')))
      //			}
      //			assert xsdFile != null
      //			into (shortName) {
      //				from xsdFile.path
      //			}
      //		}
      
  7. 使用时在xml文件中像引入其他命名空间一样引入即可使用

1.自定义xsd文件

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema"
			targetNamespace="http://www.armin.com/schema/user"
			xmlns:tns="http://www.armin.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 ="age" type="string"/>
		</complexType>
	</element>
</schema>

2.自定义实体类定义接收属性

@Data
public class User {
	private String username;
	private String email;
	private String age;
}

3.自定义parse对象

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 UserBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {

	@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 age = element.getAttribute("age");
		if (StringUtils.hasText(username)) {
			builder.addPropertyValue("username", username);
		}
		if (StringUtils.hasText(email)) {
			builder.addPropertyValue("email", email);
		}
		if (StringUtils.hasText(age)) {
			builder.addPropertyValue("age", age);
		}
	}
}

4.自定义NamespaceHandler对象

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

public class UserNamespaceHandler extends NamespaceHandlerSupport {

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

}

5.resource文件夹新建META-INF文件夹

6.新建spring.handlers与spring.schemas文件

spring.handlers
http\://www.armin.com/schema/user=com.armin.selftag.UserNamespaceHandler
spring.schemas
http\://www.armin.com/schema/user.xsd=META-INF/user.xsd

7.在xml文件中引入

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:armin="http://www.armin.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.armin.com/schema/user http://www.armin.com/schema/user.xsd
        ">
	<armin:user id="armin" username="enzo" email="enzo@armin.com" age="18"/>
</beans>

测试

import com.armin.selftag.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {

	public static void main(String[] args) {
		ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
		User armin = (User) context.getBean("armin");
		System.out.println(armin);
	}

}

测试结果

> Task :spring-debug:Test.main()
User{username='enzo', email='enzo@armin.com', age='18'}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值