org\geekbang\thinking\in\spring\configuration\metadata\users.xsd
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <xsd:schema xmlns="http://time.geekbang.org/schema/users" xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://time.geekbang.org/schema/users"> <xsd:import namespace="http://www.w3.org/XML/1998/namespace"/> <!-- 定义 User 类型(复杂类型) --> <xsd:complexType> <xsd:attribute type="xsd:long" use="required"/> <xsd:attribute type="xsd:string" use="required"/> <xsd:attribute type="City"/> </xsd:complexType> <!-- 定义 City 类型(简单类型,枚举) --> <xsd:simpleType> <xsd:restriction base="xsd:string"> <xsd:enumeration value="BEIJING"/> <xsd:enumeration value="HANGZHOU"/> <xsd:enumeration value="SHANGHAI"/> </xsd:restriction> </xsd:simpleType> <!-- 定义 user 元素 --> <xsd:element type="User"/> </xsd:schema> 自定义 NamespaceHandler 实现 package org.geekbang.thinking.in.spring.configuration.metadata; import org.springframework.beans.factory.xml.NamespaceHandler; import org.springframework.beans.factory.xml.NamespaceHandlerSupport; public class UsersNamespaceHandler extends NamespaceHandlerSupport { @Override public void init() { // 将 "user" 元素注册对应的 BeanDefinitionParser 实现 registerBeanDefinitionParser("user", new UserBeanDefinitionParser()); } } 自定义 BeanDefinitionParser 实现 package org.geekbang.thinking.in.spring.configuration.metadata; import org.geekbang.thinking.in.spring.ioc.overview.domain.User; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; import org.springframework.beans.factory.xml.BeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; 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, ParserContext parserContext, BeanDefinitionBuilder builder) { setPropertyValue("id", element, builder); setPropertyValue("name", element, builder); setPropertyValue("city", element, builder); } private void setPropertyValue(String attributeName, Element element, BeanDefinitionBuilder builder) { String attributeValue = element.getAttribute(attributeName); if (StringUtils.hasText(attributeValue)) { builder.addPropertyValue(attributeName, attributeValue); // -> <property value=""/> } } } 注册 XML 扩展(spring.handlers 文件)META-INF/spring.handlers
## 定义 namespace 与 NamespaceHandler 的映射 http\://time.geekbang.org/schema/users=org.geekbang.thinking.in.spring.configuration.metadata.UsersNamespaceHandler 编写 Spring Schema 资源映射文件(spring.schemas 文件)META-INF/spring.schemas
http\://time.geekbang.org/schema/users.xsd = org/geekbang/thinking/in/spring/configuration/metadata/users.xsd 使用示例 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:users="http://time.geekbang.org/schema/users" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd "> <!-- <bean> <property value="1"/> <property value="小马哥"/> <property value="HANGZHOU"/> </bean> --> <users:user city="HANGZHOU"/> </beans>至此,通过使用 users 命名空间下的 user 标签也能定义一个 Bean
Mybatis 对 Spring 的集成项目中的 <mybatis:scan /> 标签就是这样实现的,可以参考:NamespaceHandler、MapperScannerBeanDefinitionParser、XSD 等文件
总结Spring 默认命名空间为 ,也就是 <bean /> 标签,解析过程在上一篇《BeanDefinition 的解析阶段(XML 文件)》文章中已经分析过了。
非默认命名空间的处理方式需要单独的 NamespaceHandler 命名空间处理器进行处理,这中方式属于扩展 Spring XML 元素,也可以说是自定义标签。在 Spring 内部很多地方都使用到这种方式。例如 <context:component-scan />、<util:list />、AOP 相关标签都有对应的 NamespaceHandler 命名空间处理器
对于这种自定义 Spring XML 元素的实现步骤如下:
编写 XML Schema 文件(XSD 文件):定义 XML 结构
自定义 NamespaceHandler 实现:定义命名空间的处理器,实现 NamespaceHandler 接口,我们通常继承 NamespaceHandlerSupport 抽象类,Spring 提供了通用实现,只需要实现其 init() 方法即可