1:写在前面
关于例子可以参考spring的自定义标签
,后续文章依赖本文,请务必仔细阅读。
2:源码入口
我们就着spring的自定义标签
来分析,再简单贴下xml和测试代码。
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:ok="http://dongshi.daddy.com/schema/ok"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://dongshi.daddy.com/schema/ok
http://dongshi.daddy.com/schema/ok/dongshidaddy-1.0.xsd">
<ok:service id="testServer" serverName="HelloWorldService"/>
</beans>
测试代码:
@Test
public void testSelfDefineTag() {
// 定义资源
ClassPathResource classPathResource = new ClassPathResource("testselfdefinetag.xml");
// 定义IOC容器
DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory();
// 定义bean定义读取器
XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(defaultListableBeanFactory);
// 通过bean定义读取器从资源中读取bean定义
int i = xmlBeanDefinitionReader.loadBeanDefinitions(classPathResource);
System.out.println("bean定义的个数是:" + i);
}
在org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#parseBeanDefinitions
中就是我们的入口了。
3:parseBeanDefinitions
位置org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#parseBeanDefinitions
,源码:
org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#parseBeanDefinitions
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
// 这里就是beans标签,即xml的如下:
/*
<beans xmlns="http://www.springframework.org/schema/beans"
...>
</beans>
*/
if (delegate.isDefaultNamespace(root)) {
// 获取所有的子节点
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
// Element才处理
if (node instanceof Element) {
Element ele = (Element) node;
// <2021-03-06 20:50>
// 如果是默认命名空间
if (delegate.isDefaultNamespace(ele)) {
parseDefaultElement(ele, delegate);
}
// <2021-03-06 20:51>
else {
delegate.parseCustomElement(ele);
}
}
}
}
// 根标签不是默认命名空间,一般不会有这种情况,暂时忽略
else {
delegate.parseCustomElement(root);
}
}
<2021-03-06 20:50>
处命名空间的解析参考spring解析bean标签过程分析。<2021-03-06 20:51>
就是解析自定义标签,参考``。
3.1:parseCustomElement
源码:
org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parseCustomElement(org.w3c.dom.Element)
public BeanDefinition parseCustomElement(Element ele) {
return parseCustomElement(ele, null);
}
继续:
org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parseCustomElement(org.w3c.dom.Element, org.springframework.beans.factory.config.BeanDefinition)
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
// 获取元素的命名空间
String namespaceUri = getNamespaceURI(ele);
// 如果是没有命名空间,直接返回空,即要求必须有命名空间
// 因为后续操作需要依赖于命名空间
if (namespaceUri == null) {
return null;
}
// <2021-03-07 11:47>
// 根据命名空间解析出对应的命名空间处理器
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
// 如果是没有获取到对应命名空间的处理器直接异常,并返回null
if (handler == null) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
return null;
}
// <2021-03-07 11:49>
// 通过命名空间对应的处理器执行解析
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
<2021-03-07 11:47>
处参考3.2:getNamespaceHandlerResolver
,和3.3:resolve
,<2021-03-07 11:49>
处参考3.4:parse
。
3.2:getNamespaceHandlerResolver
此处是获取命名空间处理器的解析器,用来通过命名空间,获取对应命名空间的解析器,源码:
org.springframework.beans.factory.xml.XmlReaderContext#getNamespaceHandlerResolver
public final NamespaceHandlerResolver getNamespaceHandlerResolver() {
// private final NamespaceHandlerResolver namespaceHandlerResolver;
return this.namespaceHandlerResolver;
}
接下来我们看下namespaceHandlerResolver
是怎么初始化的,参考3.2.1:namespaceHandlerResolver的初始化
。
3.2.1:namespaceHandlerResolver的初始化
在注册BeanDefinition的方法org.springframework.beans.factory.xml.XmlBeanDefinitionReader#registerBeanDefinitions
中有如下源码:
org.springframework.beans.factory.xml.XmlBeanDefinitionReader#registerBeanDefinitions
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
// <2021-03-07 04:47>
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
<2021-03-07 04:47>
中方法createReaderContext(resource)
源码如下:
org.springframework.beans.factory.xml.XmlBeanDefinitionReader#createReaderContext
public XmlReaderContext createReaderContext(Resource resource) {
return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
this.sourceExtractor, this, getNamespaceHandlerResolver());
}
其中的方法getNamespaceHandlerResolver()
正是完成命名空间处理器解析器的地方,源码如下:
org.springframework.beans.factory.xml.XmlBeanDefinitionReader#getNamespaceHandlerResolver
public NamespaceHandlerResolver getNamespaceHandlerResolver() {
if (this.namespaceHandlerResolver == null) {
// <2021-03-07 04:51>
this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver();
}
return this.namespaceHandlerResolver;
}
<2021-03-07 04:51>
就是真正初始化的地方,源码如下:
org.springframework.beans.factory.xml.XmlBeanDefinitionReader#createDefaultNamespaceHandlerResolver
protected NamespaceHandlerResolver createDefaultNamespaceHandlerResolver() {
ClassLoader cl = (getResourceLoader() != null ? getResourceLoader().getClassLoader() : getBeanClassLoader());
return new DefaultNamespaceHandlerResolver(cl);
}
可以看到最终返回的类型是DefaultNamespaceHandlerResolver
。
3.3:resolve
该方法通过命名空间处理器的解析器对象来解析对应的命名空间获取对应命名空间的处理器,源码如下:
org.springframework.beans.factory.xml.DefaultNamespaceHandlerResolver#resolve
public NamespaceHandler resolve(String namespaceUri) {
// 从META-INF/spring.handlers文件中加载命名空间->处理器的map
// 这里不再贴源码
Map<String, Object> handlerMappings = getHandlerMappings();
// 通过命名空间获取对应的处理器
Object handlerOrClassName = handlerMappings.get(namespaceUri);
if (handlerOrClassName == null) {
return null;
}
// 如果已经是命名空间处理器实例,而非字符串限定名,一般第一次是类限定名称的字符串
// 而非类,后续创建后会直接存储命名空间->处理器实例,下次再需要这里就是true了
else if (handlerOrClassName instanceof NamespaceHandler) {
return (NamespaceHandler) handlerOrClassName;
}
else {
// 强转string作为处理器类名
String className = (String) handlerOrClassName;
try {
// 通过类名加载class,生成对应的Class对象
Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
// 要求handlerClass必须是NameSpaceHandler的子类,不是则直接异常
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);
// <2021-03-07 18:07>
namespaceHandler.init();
// 直接存储命名空间对应的命名空间处理器对象,下次就可以直接获取使用,不需要重新创建了
handlerMappings.put(namespaceUri, namespaceHandler);
// 返回命名空间处理器
return namespaceHandler;
}
// classpath下没有对应的类,直接异常
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);
}
}
}
<2021-03-07 18:07>
处是初始化命名空间处理器,这里其实就是调用我们的自定义标签处理器init方法来注册自定义标签解析器了,本例源码如下:
yudaosourcecode.selfdefinetag.CommonNamespaceHandler#init
@Override
public void init() {
this.registerBeanDefinitionParser("service",
new OkServerDefinitionParser(ServerBean.class));
}
其中的OkServerDefinitionParser
就是我们解析器,不再重复帖源码了,这样就完成了标签名称为service
的解析器的注册,这里注册处理器直接调用的是org.springframework.beans.factory.xml.NamespaceHandlerSupport
的方法,源码如下:
org.springframework.beans.factory.xml.NamespaceHandlerSupport#registerBeanDefinitionParser
protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) {
// <2021-03-07 18:12>
this.parsers.put(elementName, parser);
}
<2021-03-07 18:12>
处this.parsers定义为private final Map<String, BeanDefinitionParser> parsers = new HashMap<>();
因此标签名称和对应的标签名称的标签处理器就存储到map中了。
3.4:parse
在前面我们已经成功获取了自定义标签的标签处理器了,接下来就是解析了,源码:
org.springframework.beans.factory.xml.NamespaceHandlerSupport#parse
public BeanDefinition parse(Element element, ParserContext parserContext) {
// <2021-03-07 18:17>
BeanDefinitionParser parser = findParserForElement(element, parserContext);
// <2021-03-07 18:18>
return (parser != null ? parser.parse(element, parserContext) : null);
}
<2021-03-07 18:17>
处参考3.4.1:findParserForElement
,<2021-03-07 18:18>
处参考3.4.2:parser.parse
。
3.4.1:findParserForElement
获取标签处理器,源码:
org.springframework.beans.factory.xml.NamespaceHandlerSupport#findParserForElement
private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
// 获取localName,即不带有命名空间前缀的标签名称,如<ok:service>结果就是service
String localName = parserContext.getDelegate().getLocalName(element);
// 从parsersmap中根据localName获取标签处理器
BeanDefinitionParser parser = this.parsers.get(localName);
// 没有获取到则error
if (parser == null) {
parserContext.getReaderContext().fatal(
"Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
}
// 返回标签处理器
return parser;
}
3.4.2:parser.parse
这里具体解析其实就是调用我们的处理器的方法了,简单贴下源码,不再解析:
yudaosourcecode.selfdefinetag.OkServerDefinitionParser#parse
public BeanDefinition parse(Element element, ParserContext parserContext) {
return parseHelper(element, parserContext, this.clazz);
}
到这里BeanDefinition的解析工作就全面完成了,那么BeanDefinition是如何完成注册的呢,这部分内容参考文章spring IOC注册BeanDefinition
。