Spring-自定义标签

撸了今年阿里、头条和美团的面试,我有一个重要发现.......>>> hot3.png

在spring启动,对配置文件进行解析的代码如下:

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

if else中分别是对自定义标签和默认标签的继续。跟进自定义标签的解析过程,进入到BeanDefinitionParserDelegate类的下面方法:

@Nullable
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
	//获取namespaceUri
	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);这行代码, 获取readerContext的namespaceHandlerResolver属性进行解析,然后看一下NamespaceHandlerResolver类发现是一个函数式接口,实现类只有一个是DefaultNamespaceHandlerResolver。所以这个resolve(...)方法调用的就是DefaultNamespaceHandlerResolver类下面的方法:

@Override
@Nullable
public NamespaceHandler resolve(String namespaceUri) {
	//获取handlerMappings
	Map handlerMappings = getHandlerMappings();
	//通过namespaceUri获取对应的处理类的classname
	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方法
			namespaceHandler.init();
			//最终将namespaceHandler和对应的namespaceUri放到handlerMappings中
			handlerMappings.put(namespaceUri, namespaceHandler);
			return namespaceHandler;
		} catch (ClassNotFoundException ex) {
			throw new FatalBeanException("NamespaceHandler class [" + className + "] for namespace [" +
			                             namespaceUri + "] not found", ex);
		} catch (LinkageError err) {
			throw new FatalBeanException("Invalid NamespaceHandler class [" + className + "] for namespace [" +
			                             namespaceUri + "]: problem with handler class file or dependent class", err);
		}
	}
}

这个方法的说明是:为命名空间获取对应的NamespaceHandler。 记住这里的namespaceHandler.init();调用namespaceHandler实现类的init方法。 再看一下NamespaceHandler类是一个接口,其中有三个方法。但在实际当中我们不实现此接口,而是实现NamespaceHandlerSupport,但NamespaceHandlerSupport是个抽象类,并没有实现init()方法,所以init()方法由用户自己实现。通常用来注册一个BeanDefinitionParser实现类,下面会用到。重点看一下其getHandlerMappings();方法:

private Map getHandlerMappings() {
	Map handlerMappings = this.handlerMappings;
	if (handlerMappings == null) {
		synchronized (this) {
			handlerMappings = this.handlerMappings;
			if (handlerMappings == null) {
				try {
					Properties mappings =
							PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
					if (logger.isDebugEnabled()) {
						logger.debug("Loaded NamespaceHandler mappings: " + mappings);
					}
					Map mappingsToUse = new ConcurrentHashMap<>(mappings.size());
					CollectionUtils.mergePropertiesIntoMap(mappings, mappingsToUse);
					handlerMappings = mappingsToUse;
					this.handlerMappings = handlerMappings;
				}
				catch (IOException ex) {
					throw new IllegalStateException(
							"Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", ex);
				}
			}
		}
	}
	return handlerMappings;
}

方法比较简单就是获取handlerMappings,如果为null则加载对应位置handlerMappingsLocation下的文件中的内容。 看一下handlerMappingsLocation在哪里被赋值的。是在构造方法中,默认值为当前类中定义的常量:DEFAULT_HANDLER_MAPPINGS_LOCATION = "META-INF/spring.handlers";

继续分析handler.parse(ele, new ParserContext(this.readerContext, this, containingBd)); handler为NamespaceHandlerSupport类中的parse方法。如下:

@Override
@Nullable
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) {
	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;
}

通过handler的parse方法获取一个BeanDefinitionParser接口的实现类。最终调用BeanDefinitionParser接口的BeanDefinition parse(Element element, ParserContext parserContext);方法解析注册自定义标签内容。

你可能感兴趣的:(Spring-自定义标签)