五、Spring自定义标签的解析及注册

一、自定义标签的使用案例

依赖:spring-beansspring-oxmspring-corespring-context

(一)使用流程

  1. 创建一个需要扩展的组件
  2. 定义一个 XSD 文件描述组件内容
  3. 创建一个文件,实现 BeanDefinitionParser 接口,用来解析 XSD 文件中的定义和组件定义
  4. 创建一个 Handler 文件,扩展自 NamespaceHandlerSupport,目的是将组件注册到 Spring 容器
  5. 编写 Spring.handlersSpring.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;
	}

	@Override
	public String toString() {
		return "User{" +
				"userName='" + userName + '\'' +
				", email='" + email + '\'' +
				'}';
	}
}

2.定义一个 XSD 文件描述组件内容

resources/META-INF/spring-test.xsd

<?xml version="1.0" encoding="UTF-8"?>
<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 {
	@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");
		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 {
	@Override
	public void init() {
		registerBeanDefinitionParser("user", new UserBeanDefinitionParser());
	}
}

5. 编写 Spring.handlersSpring.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 前期准备

(二)解析流程

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)
  1. 获取标签的命名空间
  2. 提取自定义标签处理器
  3. 标签解析
	@Nullable
	public BeanDefinition parseCustomElement(Element ele) {
		return parseCustomElement(ele, null);
	}
	
	/**
	 * containingBd:父类 bean,对顶层元素的解析应设置为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(String namespaceUri)

主要功能:读取 Spring.handlers 配置文件并将配置文件缓存在 map 中(是命名空间和命名处理器之间的映射关系),然后根据命名空间找到对应的处理器,执行 namespaceHandler.init() 来进行自定义 BeanDefinitionParser 的注册(注册是标签和解析器之间的映射)。注册后,命名空间处理器就可以根据标签的不同来调用不同的解析器进行解析。

	@Override
	@Nullable
	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)
	@Override
	@Nullable
	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)
	@Nullable
	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)
	@Override
	@Nullable
	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)
	 */
	@Override
	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);
	}
发布了444 篇原创文章 · 获赞 113 · 访问量 40万+

猜你喜欢

转载自blog.csdn.net/panchang199266/article/details/100939810