撸了今年阿里、头条和美团的面试,我有一个重要发现.......>>>
在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);
方法解析注册自定义标签内容。