Spring IOC源码:obtainFreshBeanFactory 详解(下)

文章目录

  • Spring源码系列:
  • 前言
    • 正文
    • 方法1:parseCustomElement
    • 方法2:getNamespaceHandlerResolver().resolve
    • 方法3:getHandlerMappings
    • 方法4:namespaceHandler.init()
    • 方法5:handler.parse
  • 自定义命名空间
    • 1、新建自定义命名空间处理器
    • 2、新建自定义命名空间解析器
    • 3、新建spring.handlers文件
    • 4、新建spring.schemas文件
    • 5、新建xsd文件,声明标签规范
    • 6、新建xml文件配置自定义标签
    • 6、测试用例
  • 总结

Spring源码系列:

Spring IOC源码:简单易懂的Spring IOC 思路介绍
Spring IOC源码:核心流程介绍
Spring IOC源码:ApplicationContext刷新前准备工作
Spring IOC源码:obtainFreshBeanFactory 详解(上)
Spring IOC源码:obtainFreshBeanFactory 详解(中)
Spring IOC源码:obtainFreshBeanFactory 详解(下)
Spring IOC源码:<context:component-scan>源码详解
Spring IOC源码:invokeBeanFactoryPostProcessors 后置处理器详解
Spring IOC源码:registerBeanPostProcessors 详解
Spring IOC源码:实例化前的准备工作
Spring IOC源码:finishBeanFactoryInitialization详解
Spring IoC源码:getBean 详解
Spring IoC源码:createBean( 上)
Spring IoC源码:createBean( 中)
Spring IoC源码:createBean( 下)
Spring IoC源码:finishRefresh 完成刷新详解

前言

前面两篇文章介绍了BeanDefinition的解析,以及默认命名空间的解析过程,这节介绍obtainFreshBeanFactory中另一个核心的解析逻辑,自定义命名空间的解析。

正文

自定义命名空间解析,入口方法为delegate.parseCustomElement(ele),见方法1详解

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
		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);
		}
	}

方法1:parseCustomElement

	public BeanDefinition parseCustomElement(Element ele) {
		return parseCustomElement(ele, null);
	}

	/**
	 * Parse a custom element (outside of the default namespace).
	 * @param ele the element to parse
	 * @param containingBd the containing bean definition (if any)
	 * @return the resulting bean definition
	 */
	@Nullable
	public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
		//获取命名空间路径 例如:http://www.springframework.org/schema/aop
		String namespaceUri = getNamespaceURI(ele);
		if (namespaceUri == null) {
			return null;
		}
		//通过命名空间解析器加载命名空间处理器
		NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
		if (handler == null) {
			error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
			return null;
		}
		//调用解析
		return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
	}

this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri)方法,见方法3详解
handler.parse(ele, new ParserContext(this.readerContext, this, containingBd))方法,见方法5详解

方法2:getNamespaceHandlerResolver().resolve

	public NamespaceHandler resolve(String namespaceUri) {
		//获取命名空间集合
		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
			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);
			}
		}
	}

getHandlerMappings(),见方法3详解
namespaceHandler.init(),见方法4详解

方法3:getHandlerMappings

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 {
						//根据处理器路径进行查找加载
						Properties mappings =
								PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
						if (logger.isTraceEnabled()) {
							logger.trace("Loaded NamespaceHandler mappings: " + mappings);
						}
						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;
	}

handlerMappings集合中的处理器如下图:
Spring IOC源码:obtainFreshBeanFactory 详解(下)_第1张图片

方法4:namespaceHandler.init()

因为当前配置文件中的自定义命名空间是AOP,所以这里会进入AOP处理器的init方法。

	public void init() {
		//这里主要注册需要用到的解析器
		// In 2.0 XSD as well as in 2.1 XSD.
		registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
		registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
		registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());

		// Only in 2.0 XSD: moved to context namespace as of 2.1
		registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
	}

方法5:handler.parse

进入方法1中的handler.parse(ele, new ParserContext(this.readerContext, this, containingBd))方法;

	public BeanDefinition parse(Element element, ParserContext parserContext) {
		//根据当前节点的标签去查找对应的解析器
		BeanDefinitionParser parser = findParserForElement(element, parserContext);
		//调用解析器处理逻辑
		return (parser != null ? parser.parse(element, parserContext) : null);
	}

	/**
	 * Locates the {@link BeanDefinitionParser} from the register implementations using
	 * the local name of the supplied {@link Element}.
	 */
	@Nullable
	private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
		//获取节点标签  如:config
		String localName = parserContext.getDelegate().getLocalName(element);
		//根据名称获取解析器
		BeanDefinitionParser parser = this.parsers.get(localName);
		if (parser == null) {
			parserContext.getReaderContext().fatal(
					"Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
		}
		return parser;
	}

到此整个obtainFreshBeanFactory 的讲解就结束了,自定义命名空间处理器及其解析方法,后续文章中会跟案例进行讲解。

自定义命名空间

看完上面的步骤,大家应该对自定义命名空间过程有一定的理解,下面我们自定义案例来加深理解,我们需要按以下步骤来完成Demo。
1、新建自定义命名空间处理器,一个处理器可以添加多个解析器
2、新建自定义命名空间解析器,编写解析处理逻辑
3、新建spring.handlers文件,按Key-Value格式指定处理器路径
4、新建spring.schemas文件,按Key-Value格式指定xsd自定义标签文件路径
5、新建xsd文件,声明标签规范,指定命名空间处理器
6、新建xml文件配置自定义标签,完成类的管理。

1、新建自定义命名空间处理器

我们新建一个空模块Gradle空模块

Spring IOC源码:obtainFreshBeanFactory 详解(下)_第2张图片编辑build.gradle文件,引入spring 包

Spring IOC源码:obtainFreshBeanFactory 详解(下)_第3张图片

新建处理器类 ZdcHandler

package com.zhudachang.namespace;

import org.springframework.beans.factory.xml.NamespaceHandlerSupport;

public class ZdcHandler extends NamespaceHandlerSupport {

	@Override
	public void init() {
		super.registerBeanDefinitionParser("zdc",new ZdcParser());
	}
}

2、新建自定义命名空间解析器

package com.zhudachang.namespace;

import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.w3c.dom.Element;

/**
 * Zdc解析器
 */
public class ZdcParser implements BeanDefinitionParser {
	@Override
	public BeanDefinition parse(Element element, ParserContext parserContext) {
		RootBeanDefinition beanDefinition = new RootBeanDefinition();
		beanDefinition.setBeanClass(ZdcDomain.class);
		//声明PropertyValues对象
		MutablePropertyValues mutablePropertyValues = beanDefinition.getPropertyValues();
		// 添加name属性
		if (element.hasAttribute("name")) {
			mutablePropertyValues.addPropertyValue("name", element.getAttribute("name"));
		}
		// 解析添加remark属性
		if (element.hasAttribute("remark")) {
			mutablePropertyValues.addPropertyValue("remark", element.getAttribute("remark"));
		}
		String id = element.getAttribute("id");
		// 拿到注册表, 注册BeanDefinition
		parserContext.getRegistry().registerBeanDefinition(id, beanDefinition);
		return beanDefinition;

	}
}

新建实体类

package com.zhudachang.namespace;

public class ZdcDomain {
	String name;
	String remark;



	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getRemark() {
		return remark;
	}

	public void setRemark(String remark) {
		this.remark = remark;
	}
}

3、新建spring.handlers文件

resource目录下创建META-INF目录,并创建spring.handlers文件,指定空间路径及其对应的处理器类路径

http\://com.zhudachang.namespace/schema/zdc=com.zhudachang.namespace.ZdcHandler

4、新建spring.schemas文件

resource目录下创建META-INF目录,并创建spring.schemas文件,指定路径及其对应xsd文件路径

http\://com.zhudachang.namespace/schema/zdc/zdc-1.0.xsd=./com/zdc/spancename/zdc/config/zdc-1.0.xsd

5、新建xsd文件,声明标签规范

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


	<xsd:element name="zdc" >
		<xsd:complexType>
			<xsd:attribute name="id" type="xsd:string"/>
			<xsd:attribute name="name" type="xsd:string"/>
			<xsd:attribute name="remark" type="xsd:string"/>
		</xsd:complexType>
	</xsd:element>
</xsd:schema>

6、新建xml文件配置自定义标签

如果你的测试模块跟上述定义解析器标签不在同个模块下,记得先引入模块。
Spring IOC源码:obtainFreshBeanFactory 详解(下)_第4张图片


<beans xmlns="http://www.springframework.org/schema/beans"
	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	   xmlns:zdc="http://com.zhudachang.namespace/schema/zdc"
	   xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://com.zhudachang.namespace/schema/zdc
       http://com.zhudachang.namespace/schema/zdc/zdc-1.0.xsd">
		<zdc:zdc id="zdc" name="猪大肠" remark="猪大肠备注">zdc:zdc>

beans>

6、测试用例

编写测试用例

	public static void main(String[] args) {
		ClassPathXmlApplicationContext applicationContext=new ClassPathXmlApplicationContext("application-namespace.xml");
		ZdcDomain zdcDomain = (ZdcDomain) applicationContext.getBean("zdc");
		System.out.println(zdcDomain.getName());
	}

测试结果:
Spring IOC源码:obtainFreshBeanFactory 详解(下)_第5张图片

总结

本篇文章介绍了自定义命名空间的解析过程,并且编写的自定义命名空间案例加深理解。后续文章中会拿常用的自定义标签context:component-scan 进行讲解。

你可能感兴趣的:(spring,Java,spring)