一、自定义标签的使用案例
依赖:
spring-beans
、spring-oxm
、spring-core
、spring-context
(一)使用流程
- 创建一个需要扩展的组件
- 定义一个
XSD
文件描述组件内容 - 创建一个文件,实现
BeanDefinitionParser
接口,用来解析XSD
文件中的定义和组件定义 - 创建一个
Handler
文件,扩展自NamespaceHandlerSupport
,目的是将组件注册到 Spring 容器 - 编写
Spring.handlers
和Spring.schemas
文件
1.创建一个POJO
package org.pc;
public class User {
private String userName;
private String email;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String toString() {
return "User{" +
"userName='" + userName + '\'' +
", email='" + email + '\'' +
'}';
}
}
2.定义一个 XSD
文件描述组件内容
resources/META-INF/spring-test.xsd
<schema xmlns="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.lexueba.com/schema/user"
xmlns:tns="http://www.lexueba.com/schema/user"
elementFormDefault="qualified">
<element name="user">
<complexType>
<attribute name="id" type="string"/>
<attribute name="userName" type="string"/>
<attribute name="email" type="string"/>
</complexType>
</element>
</schema>
3. 创建一个文件,实现 BeanDefinitionParser
接口,用来解析 XSD
文件中的定义和组件定义
package org.pc;
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 {
protected Class<?> getBeanClass(Element element) {
return User.class;
}
protected void doParse(Element element, BeanDefinitionBuilder builder) {
String userName = element.getAttribute("userName");
String email = element.getAttribute("email");
if (StringUtils.hasText(userName)) {
builder.addPropertyValue("userName", userName);
}
if (StringUtils.hasText(email)) {
builder.addPropertyValue("email", email);
}
}
}
4. 创建一个 Handler
文件,扩展自 NamespaceHandlerSupport
,目的是将组件注册到 Spring 容器
package org.pc;
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
public class MyNamespaceHandler extends NamespaceHandlerSupport {
public void init() {
registerBeanDefinitionParser("user", new UserBeanDefinitionParser());
}
}
5. 编写 Spring.handlers
和 Spring.schemas
文件
resources/META-INF/spring.handlers
对应 Handler
文件
http\://www.lexueba.com/schema/user=org.pc.MyNamespaceHandler
resources/META-INF/spring.schemas
对应 XSD 文件
http\://www.lexueba.com/schema/user.xsd=META-INF/spring-test.xsd
6. 测试
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
public static void main(String[] args) {
ApplicationContext cpx = new ClassPathXmlApplicationContext("spring-dev.xml");
User user = (User) cpx.getBean("testbean");
System.out.println(user);
}
}
二、自定义标签解析
(一)先期准备
前期准备工作参见 二、Spring之解析及注册 BeanDefinitions 前期准备
(二)解析流程
- 1.1.1.1
BeanDefinitionParserDelegate#getNamespaceURI(Node node)
:获取标签的命名空间 - 1.1.1.2
DefaultNamespaceHandlerResolver#resolve(String namespaceUri)
:提取自定义标签处理器
- 1.1.1.2.1
DefaultNamespaceHandlerResolver#getHandlerMappings()
:读取 Spring.handlers 配置文件并将配置文件缓存在 map 中
- 1.1.1.3.1
NamespaceHandlerSupport#findParserForElement(Element element, ParserContext parserContext)
:寻找解析器 - 1.1.1.3.2
AbstractBeanDefinitionParser#parse(Element element, ParserContext parserContext)
:解析
- 1.1.1.3.2.1
AbstractSingleBeanDefinitionParser#parseInternal(Element element, ParserContext parserContext)
:调用我们自定义的解析函数
- DefaultBeanDefinitionDocumentReader#parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate)
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
// 对 beans 处理
/*
* Spring 的 XML 配置里有两大类 Bean 声明,一个是默认的,如:
* <bean id="test" class="test.TestBean" />
* 另一类是自定义的,如:
* <tx:annotation-driven />
* 两种方式的读取及解析差别是非常大的,如果采用 Spring 默认的配置,Spring 当然知道该怎么做,但是如果是自定义
* 的。那么就需要用户实现一些接口及配置了。对于根节点或者子节点如果是默认命名空间的话采用 parseDefaultElement()
* 方法进行解析,否则使用 delegate.parseCustomElement() 方法对自定义命名空间进行解析。而判断是否默认命名空间还是
* 自定义命名空间的办法其实是使用 node.getNamespaceURI() 获取命名空间,并与 Spring 中固定的命名空间
* http://www.springframework.org/schema/beans 进行对比。如果一致则认为是默认,否则就认为是自定义。
*/
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
if (delegate.isDefaultNamespace(ele)) {
parseDefaultElement(ele, delegate);
}
else {
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
- BeanDefinitionParserDelegate#parseCustomElement(Element ele, @Nullable BeanDefinition containingBd)
- 获取标签的命名空间
- 提取自定义标签处理器
- 标签解析
public BeanDefinition parseCustomElement(Element ele) {
return parseCustomElement(ele, null);
}
/**
* containingBd:父类 bean,对顶层元素的解析应设置为null
*/
public BeanDefinition parseCustomElement(Element ele, 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(String namespaceUri)
主要功能:读取 Spring.handlers 配置文件并将配置文件缓存在 map 中(是命名空间和命名处理器之间的映射关系),然后根据命名空间找到对应的处理器,执行 namespaceHandler.init()
来进行自定义 BeanDefinitionParser
的注册(注册是标签和解析器之间的映射)。注册后,命名空间处理器就可以根据标签的不同来调用不同的解析器进行解析。
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.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);
}
}
}
- DefaultNamespaceHandlerResolver#resolve(String namespaceUri)
/**
* 读取 Spring.handlers 配置文件并将配置文件缓存在 map 中
*/
private Map<String, Object> getHandlerMappings() {
Map<String, Object> handlerMappings = this.handlerMappings;
// 若没有被缓存,则开始进行缓存
if (handlerMappings == null) {
synchronized (this) {
handlerMappings = this.handlerMappings;
if (handlerMappings == null) {
if (logger.isTraceEnabled()) {
logger.trace("Loading NamespaceHandler mappings from [" + this.handlerMappingsLocation + "]");
}
try {
// this.handlerMappingsLocation 在构造函数中已经被初始化为:META-INF/Spring.handlers
Properties mappings =
PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
if (logger.isTraceEnabled()) {
logger.trace("Loaded NamespaceHandler mappings: " + mappings);
}
handlerMappings = new ConcurrentHashMap<>(mappings.size());
// 将 Properties 格式文件合并到 Map 格式的 handlerMappings 中
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;
}
- NamespaceHandlerSupport#parse(Element element, ParserContext parserContext)
public BeanDefinition parse(Element element, ParserContext parserContext) {
// 寻找解析器并进行解析操作
BeanDefinitionParser parser = findParserForElement(element, parserContext);
return (parser != null ? parser.parse(element, parserContext) : null);
}
- NamespaceHandlerSupport#parse(Element element, ParserContext parserContext)
private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
// 获取元素名称,也就是 <myname:user id="testbean" userName="aaa" email="bbb"/> 中的 user
String localName = parserContext.getDelegate().getLocalName(element);
// 根据 user 找到对应的解析器,也就是在 registerBeanDefinitionParser("user", new UserBeanDefinitionParser()) 方法中注册的解析器
BeanDefinitionParser parser = this.parsers.get(localName);
if (parser == null) {
parserContext.getReaderContext().fatal(
"Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
}
return parser;
}
- AbstractBeanDefinitionParser#parse(Element element, ParserContext parserContext)
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;
// 若自定义子类未重写 shouldParseNameAsAliases(),则默认为true
if (shouldParseNameAsAliases()) {
String name = element.getAttribute(NAME_ATTRIBUTE);
if (StringUtils.hasLength(name)) {
// 将 name 解析为别名
aliases = StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(name));
}
}
// 将 AbstractBeanDefinition 转换为 BeanDefinitionHolder
BeanDefinitionHolder holder = new BeanDefinitionHolder(definition, id, aliases);
// 注册
registerBeanDefinition(holder, parserContext.getRegistry());
// 若自定义子类未重写 shouldFireEvents(),则默认为true
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;
}
protected void registerBeanDefinition(BeanDefinitionHolder definition, BeanDefinitionRegistry registry) {
BeanDefinitionReaderUtils.registerBeanDefinition(definition, registry);
}
// BeanDefinitionReaderUtils类下的方法
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// Register bean definition under primary name.
// 使用 beanName 做唯一标识注册
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// Register aliases for bean name, if any.
// 注册所有别名
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}
- AbstractSingleBeanDefinitionParser#parseInternal(Element element, ParserContext parserContext)
/**
* 先进行一系列的数据准备,包括对 beanClass、scope、lazyInit 等属性的准备,然后调用自定义的
* doParse() 方法进行解析,例如:UserBeanDefinitionParser#doParse(Element element, BeanDefinitionBuilder builder)
*/
protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
String parentName = getParentName(element);
if (parentName != null) {
builder.getRawBeanDefinition().setParentName(parentName);
}
// 获取自定义标签中的class,此时会调用自定义解析器如 UserBeanDefinitionParser#getBeanClass(Element element)
Class<?> beanClass = getBeanClass(element);
if (beanClass != null) {
builder.getRawBeanDefinition().setBeanClass(beanClass);
}
else {
// 若子类没有重写 getBeanClass(Element element) 方法则尝试检查子类是否重写 getBeanClassName(Element element) 方法
String beanClassName = getBeanClassName(element);
if (beanClassName != null) {
builder.getRawBeanDefinition().setBeanClassName(beanClassName);
}
}
builder.getRawBeanDefinition().setSource(parserContext.extractSource(element));
BeanDefinition containingBd = parserContext.getContainingBeanDefinition();
// 若存在父类则使用父类的 scope 属性
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);
}
// 调用子类重写的 doParse() 方法进行解析,例如:UserBeanDefinitionParser#doParse(Element element, BeanDefinitionBuilder builder)
doParse(element, parserContext, builder);
return builder.getBeanDefinition();
}
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
doParse(element, builder);
}