spring学习(三)——BeanDefinition的拼装(自定义标签解析)

参考文章:

http://www.iocoder.cn

自定义的标签解析

@Nullable
	public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
		// 获取 namespaceUri
		String namespaceUri = getNamespaceURI(ele);
		if (namespaceUri == null) {
			return null;
		}
		// 根据 namespaceUri 获取相应的 Handler
		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));
	}

使用自定义标签

扩展 Spring 自定义标签配置一般需要以下几个步骤:

  1. 创建一个需要扩展的组件。
  2. 定义一个 XSD 文件,用于描述组件内容。
  3. 创建一个实现 org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser 接口的类,用来解析 XSD 文件中的定义和组件定义。
  4. 创建一个 Handler,继承 org.springframework.beans.factory.xml.NamespaceHandlerSupport 抽象类 ,用于将组件注册到 Spring 容器。
  5. 编写 spring.handlersSpring.schemas 文件。

 

创建组件

普通的 Java Bean

public class User {

    private String id;
    private String userName;
    private String email;

}

定义 XSD 文件

对 User 这个 Java Bean 进行了描述外,还定义了 xmlns="http://www.cmsblogs.com/schema/user" 和 targetNamespace="http://www.cmsblogs.com/schema/user" 这两个值

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.cmsblogs.com/schema/user" targetNamespace="http://www.cmsblogs.com/schema/user" elementFormDefault="qualified">

    <xsd:element name="user">
        <xsd:complexType>
            <xsd:attribute name="id" type="xsd:string" />
            <xsd:attribute name="userName" type="xsd:string" />
            <xsd:attribute name="email" type="xsd:string" />
        </xsd:complexType>
    </xsd:element>

</xsd:schema>

定义 Parser 类

定义一个 Parser 类,该类继承 AbstractSingleBeanDefinitionParser ,并实现 #getBeanClass(Element element) 和 #doParse(Element element, BeanDefinitionBuilder builder) 两个方法。主要是用于解析 XSD 文件中的定义和组件定义

public class UserDefinitionParser extends AbstractSingleBeanDefinitionParser {

    @Override
    protected Class<?> getBeanClass(Element element) {
        return User.class;
    }

    @Override
    protected void doParse(Element element, BeanDefinitionBuilder builder) {
        String id = element.getAttribute("id");
        String userName = element.getAttribute("userName");
        String email = element.getAttribute("email");

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

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

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

定义 NamespaceHandler 类

定义 NamespaceHandler 类,继承 NamespaceHandlerSupport ,主要目的是将组件注册到 Spring 容器中。

public class UserNamespaceHandler extends NamespaceHandlerSupport {

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

定义 spring.handlers 文件

 

http\://www.cmsblogs.com/schema/user=org.springframework.core.customelement.UserNamespaceHandler

定义 Spring.schemas 文件

http\://www.cmsblogs.com/schema/user.xsd=user.xsd

运行

在 xml 配置文件中使用如下

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:myTag="http://www.cmsblogs.com/schema/user"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.cmsblogs.com/schema/user http://www.cmsblogs.com/schema/user.xsd">
    
    <myTag:user id="user" email="12233445566@qq.com" userName="chenssy" />

</beans>
public static void main(String[] args){
    ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
    User user = (User) context.getBean("user");
    System.out.println(user.getUserName() + "----" + user.getEmail());
}

 

解析自定义标签

parseCustomElement

负责标签的解析工作,根据命名空间的不同进行不同标签的解析。其中,自定义标签由 BeanDefinitionParserDelegate 的 #parseCustomElement(Element ele, BeanDefinition containingBd) 方法来实现

下面的代码在标签解析中以及分析过一次

 

@Nullable
	public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
		// 获取 namespaceUri
		String namespaceUri = getNamespaceURI(ele);
		if (namespaceUri == null) {
			return null;
		}
		// 根据 namespaceUri 获取相应的 Handler
		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));
	}

getNamespaceHandlerResolver

返回的命名空间的解析器

public final NamespaceHandlerResolver getNamespaceHandlerResolver() {
		return this.namespaceHandlerResolver;
	}

NamespaceHandlerResolver 的初始化

readerContext 实例对象由 XmlBeanDefinitionReader 的 #createReaderContext(Resource resource) 方法创建。namespaceHandlerResolver 实例对象就是在这个时候初始化的

public XmlReaderContext createReaderContext(Resource resource) {
		return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
				this.sourceExtractor, this, getNamespaceHandlerResolver());
	}

 

public NamespaceHandlerResolver getNamespaceHandlerResolver() {
		//假如reader还不存在namespaceHandlerResolver,则使用默认值
		if (this.namespaceHandlerResolver == null) {
			this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver();
		}
		return this.namespaceHandlerResolver;
	}

resolve

	@Override
	@Nullable
	public NamespaceHandler resolve(String namespaceUri) {
		//获取所有已经配置的 Handler 映射
		Map<String, Object> handlerMappings = getHandlerMappings();
		//根据 namespaceUri 获取 handler 的信息
		Object handlerOrClassName = handlerMappings.get(namespaceUri);
		//不存在
		if (handlerOrClassName == null) {
			return null;
		}
		//已经初始化
		else if (handlerOrClassName instanceof NamespaceHandler) {
			return (NamespaceHandler) handlerOrClassName;
		}
		else {
			String className = (String) handlerOrClassName;
			try {
				// 获得类,并创建 NamespaceHandler 对象
				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);
			}
		}
	}

getHandlerMappings

  • 获取所有配置文件中的映射关系 
	private Map<String, Object> getHandlerMappings() {
		// key:命名空间
		// value:分成两种情况:1)未初始化时,对应的 NamespaceHandler 的类路径;2)已初始化,对应的 NamespaceHandler 对象
		Map<String, Object> handlerMappings = this.handlerMappings;
		if (handlerMappings == null) {
			// 通过延迟加载( lazy-init )的方式,
			// 加载 handlerMappingsLocation 中配置的 NamespaceHandler 的映射,到 handlerMappings
			synchronized (this) {
				handlerMappings = this.handlerMappings;
				if (handlerMappings == null) {
					if (logger.isTraceEnabled()) {
						logger.trace("Loading NamespaceHandler mappings from [" + this.handlerMappingsLocation + "]");
					}
					try {
						// 读取 handlerMappingsLocation
						Properties mappings =
								PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
						if (logger.isTraceEnabled()) {
							logger.trace("Loaded NamespaceHandler mappings: " + mappings);
						}
						// 初始化handlerMappings
						handlerMappings = new ConcurrentHashMap<>(mappings.size());
						CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings);
						this.handlerMappings = handlerMappings;
					}
					catch (IOException ex) {
						throw new IllegalStateException(
								"Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", ex);
					}
				}
			}
		}
		return handlerMappings;
	}

 

parse

完成后返回 NamespaceHandler 对象,然后调用其 #parse(Element element, ParserContext parserContext) 方法开始自定义标签的解析

NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
public BeanDefinition parse(Element element, ParserContext parserContext) {
		//获得元素对应的 BeanDefinitionParser 对象
		BeanDefinitionParser parser = findParserForElement(element, parserContext);
		//执行解析
		return (parser != null ? parser.parse(element, parserContext) : null);
	}

调用 #findParserForElement(Element element, ParserContext parserContext) 方法,获取对应的 BeanDefinitionParser 实例。实际上,其实就是获取在 NamespaceHandlerSupport 的 #registerBeanDefinitionParser() 方法里面注册的实例对象

@Nullable
	private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
		// 获得元素名
		String localName = parserContext.getDelegate().getLocalName(element);
		// 从 Map 实例 parsers 中获取 BeanDefinitionParser 对象
		BeanDefinitionParser parser = this.parsers.get(localName);
		if (parser == null) {
			parserContext.getReaderContext().fatal(
					"Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
		}
		return parser;
	}

 

返回 BeanDefinitionParser 对象后,调用其 #parse(Element element, ParserContext parserContext) 方法。该方法在 AbstractBeanDefinitionParser 中实现

public final BeanDefinition parse(Element element, ParserContext parserContext) {
		//内部解析,返回 AbstractBeanDefinition 对象 真正的解析工作都交由 #parseInternal
		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 对象
				BeanDefinitionHolder holder = new BeanDefinitionHolder(definition, id, aliases);
				//注册 BeanDefinition
				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;
	}

 

整个类的解析过程

  1. 会加载 spring.handlers 文件,将其中内容进行一个解析,形成 <namespaceUri, 类路径> 这样的一个映射
  2. 根据获取的 namespaceUri 就可以得到相应的类路径,对其进行初始化等到相应的 NamespaceHandler 对象
  3. 调用该 NamespaceHandler 的 #parse(...) 方法,在该方法中根据标签的 localName 得到相应的 BeanDefinitionParser 实例对象
  4. 调用该 BeanDefinitionParser 的 #parse(...) 方法。该方法定义在 AbstractBeanDefinitionParser 抽象类中,核心逻辑封装在其 #parseInternal(...) 方法中,该方法返回一个 AbstractBeanDefinition 实例对象,其主要是在 AbstractSingleBeanDefinitionParser 中实现。其需要实现 #getBeanClass() 或者 #getBeanClassName() 任一方法

spring-201807151001

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大·风

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值