【Spring 源码深度解析】03 自定义标签的解析

1 parseCustomElement

当完成从配置文件到 Document 的转换并提取对应的 root 后,就开始了所有元素的解析,而在这过程中便开始了默认标签与自定义标签两种梢式的区分,函数如下:

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
	//对 beans 的处理,根节点是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;
				//isDefaultNamespace :node.getNamespaceURI() 返回的uri 和
				//"http://www.springframework.org/schema/beans" 对比
				if (delegate.isDefaultNamespace(ele)) {
					//默认标签的解析 如 <bean id="test" class="test.TestBean">
					parseDefaultElement(ele, delegate);
				}
				else {
					//对bean 的处理,自定义的<tx:annotation-driven>
					delegate.parseCustomElement(ele);
				}
			}
		}
	}
	else {
		//解析自定义的标签
		delegate.parseCustomElement(root);
	}
}

上面代码可以看出,当 Spring 拿到一个元素时首先要做的是根据命名空间进行解析,如果是默认的命名空间,则使用parseDefaultElement方法进行元素解析,否则使用parseCustomElement方法进行解析。

2 自定义标签的使用

Spring 提供了可扩展的 Schema 的支持(需要 spring-core 包),解析工作主要包含以下几个步骤。

  1. 创建一个需要扩展的组件。
  2. 创建一个 XSD 文件描述组件的内容。
  3. 创建一个文件,实现 BeanDefinitionParser 接口,用来解析 XSD 文件中的定义和组件定义。
  4. 创建一个 Handler 文件,扩展自 NamespaceHandlerSupport,目的是将组件注册到 Spring 容器中。
  5. 编写 spring.handlers 和 spring.schemas 文件。

应用实例:
1)创建一个 POJO

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 文件描述组件内容
在 META-INF 下定义

<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema"
		targetNamespace="http://www.lyb.xyz/schema/user"
		xmlns:tns="http://www.lyb.xyz/schema/user">
	<element name="user">
		<complexType>
			<attribute name="id" type="string"/>
			<attribute name="userName" type="string"/>
			<attribute name="email" type="string"/>
		</complexType>
	</element>
</schema>

上述 XSD 文件描述了一个新的 targetNamespace,并定义了一个 name 为 user 的 element。

3)创建一个类实现 BeanDefinitionParser 接口,用来解析 XSD 文件中的定义和组件定义。

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

		builder.addPropertyValue("userName", userName);
		builder.addPropertyValue("email", email);
	}

	@Override
	protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
		super.doParse(element, parserContext, builder);
	}
}

4)创建一个 Hadnler 文件,扩展自 NamespaceHandlerSupport,目的是将组件注册到 Spring 容器。

public class MyNamespaceHandler extends NamespaceHandlerSupport {

	@Override
	public void init() {
		//当遇到<user:xxx 开头的元素 就给 UserBeanDefinitionParser 解析
		registerBeanDefinitionParser("user", new UserBeanDefinitionParser());
	}
}

以上代码当遇到自定义标签 <user:aa 这样类似于以 user 开头的元素,就会把这个元素委托给对应的 UserBeanDefinitionParser解析。

5)编写 spring.handlers 和 spring.schemas 文件。默认位置在 /META-INF/ 文件夹下。

spring.schemas
http\://www.lyb.xyz/schema/user.xsd=META-INF/spring-user.xsd

spring.handlers
http\://www.lyb.xyz/schema/user=org.springframework.customtag.MyNamespaceHandler

6)测试配置文件

<?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.lyb.xyz/schema/user"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.lyb.xyz/schema/user http://www.lyb.xyz/schema/user.xsd">

	<mytag:user id="userBean" userName="里斯" email="14813133"/>
</beans>

7)测试类

public class Main {

	public static void main(String[] args) {
		ApplicationContext context = new ClassPathXmlApplicationContext("test/customtag/customTag.xml");

		User userBean = (User) context.getBean("userBean");

		System.out.println(userBean);
	}
}

3 自定义标签的解析

// BeanDefinitionParserDelegate.java
public BeanDefinition parseCustomElement(Element ele) {
	return parseCustomElement(ele, null);
}

// containingBd为父类 bean, 顶层元素是 null
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
	//获取对应的命名空间
	String namespaceUri = getNamespaceURI(ele);
	if (namespaceUri == null) {
		return null;
	}
	//根据命名空间从上下文中获取 NamespaceHandler, DefaultNamespaceHandlerResolver
	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 进行解析, 调用 NamespaceHandlerSupport 的 parse 方法
	return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}

上述代码的解析步骤:

  1. 获取对应的命名空间
  2. 根据命名空间从上下文中获取 NamespaceHandler
  3. 调用自定义的 NamespaceHandler 进行解析

3.1 获取标签的命名空间

标签的解析都是从命名空间的提取开始,无论是区分默认标签和自定义标签还是区分自定义标签中不同的处理器都是以标签所提供的名称空间为基础的。

public String getNamespaceURI(Node node) {
	return node.getNamespaceURI();
}

3.2 提取自定义标签处理器

有了命名空间,就可以进行 NamespaceHandler 提取了。由this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri)该方法实现,在 readerContext 初始化的时候 namespaceHandlerResolver 已经被初始化为了 DefaultNamespaceHandlerResolver 了。

namespaceHandlerResolver 初始化:

XmlBeanDefinitionReader.java

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

public NamespaceHandlerResolver getNamespaceHandlerResolver() {
	if (this.namespaceHandlerResolver == null) {
		this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver();
	}
	return this.namespaceHandlerResolver;
}

protected NamespaceHandlerResolver createDefaultNamespaceHandlerResolver() {
	ClassLoader cl = (getResourceLoader() != null ? getResourceLoader().getClassLoader() : getBeanClassLoader());
	return new DefaultNamespaceHandlerResolver(cl);
}

进入 DefaultNamespaceHandlerResolver 的 resolve 方法。

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

上述代码主要步骤:
1)获取所有已经配置的 handler 映射,懒加载, getHandlerMappings 该操作将 spring.handlers 文件中的所有映射读取放入缓存 handlerMappings 中。

private Map<String, Object> getHandlerMappings() {
	Map<String, Object> handlerMappings = this.handlerMappings;
	// double check
	if (handlerMappings == null) {
		// 同步
		synchronized (this) {
			handlerMappings = this.handlerMappings;
			// 第一次加载
			if (handlerMappings == null) {
				if (logger.isTraceEnabled()) {
					logger.trace("Loading NamespaceHandler mappings from [" + this.handlerMappingsLocation + "]");
				}
				try {
					// META-INF/spring.handlers 默认在这个文件中
					//以 Properties 的方式读取
					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;
}

2)根据命名空间在 handlerMappings 中查找对应的信息,如果已经解析过则直接返回。
3)如果没有解析过则反射生成 NamespaceHandler 实例。
4)调用 namespaceHandler 的初始化方法,注册 parser,将 namespaceHandler 放入缓存。

3.3 标签解析

得到了解析器以及要分析的元素后, Spring 就可以将解析工作委托给自定义解析器去解析了。在 Spring 中的代码为:

return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));

parse 方法在父类 NamespaceHandlerSupport 中实现。

// NamespaceHandlerSupport.java
public BeanDefinition parse(Element element, ParserContext parserContext) {
	//查找解析器 实现了 BeanDefinitionParser
	BeanDefinitionParser parser = findParserForElement(element, parserContext);
	// AbstractBeanDefinitionParser -> AbstractSingletonBeanDefinitionParser
	return (parser != null ? parser.parse(element, parserContext) : null);
}

解析过程:
1)找到对应的 parser

private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
	//获取元素名称,即 <myname:user 中的 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;
}

2)调用 parser.parse(element, parserContext) 方法

public final BeanDefinition parse(Element element, ParserContext parserContext) {
	//这里调用了自定义的解析函数
	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));
				}
			}
			//将 AbstractBeanDefinition 转换为 BeanDefinitionHolder
			BeanDefinitionHolder holder = new BeanDefinitionHolder(definition, id, aliases);
			//注册 holder ,放入缓存中,和默认标签注册一样
			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;
}

3)解析获得 AbstractBeanDefinition
说是对自定义配置文件的解析。但是,在这个函数中大部分的代码是用来处理将解析后的 AbstractBeanDefinition 转化为 BeanDefinitionHolder 并注册的功能,而真正去做解析的事情委托给了 parseInternal 方法。正是这句代码调用了自定义的解析函数。

protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
	//建造者模式,封装了 GenericBeanDefinition
	BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
	String parentName = getParentName(element);
	if (parentName != null) {
		builder.getRawBeanDefinition().setParentName(parentName);
	}
	//获取自定义标签中的class ,会调用自定义解析器中如 UserBeanDefinitionParser 的 getBeanClass 方法
	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));
	//父类 bean
	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(element, parserContext, builder);
	return builder.getBeanDefinition();
}

虽然在实例中我们定义 UserBeanDefinitionParser,但是在其中我们只是做了与自己业务逻辑相关的部分。在这个处理过程中同样也是按照 Spring 标准的处理方式进行,包括创建 BeanDefinition 以及进行相应默认属性的设置,并暴露出一些接口来供用户实现个性化的业务。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值