Spring中的NamespaceHandler加载过程源码详解
作者:securitit
NamespaceHandler加载过程流程
流程图中罗列了Spring加载配置的整个过程,其中,从BeanDefinitionParserDelegate.parseCustomElement(...)开始,即开始了对命名空间NamespaceHandler的解析,本文对这部分内容进行解析。
NamespaceHandler加载过程解析
1) BeanDefinitionParserDelegate.parseCustomElement(…)方法
① 取得指定元素的命名空间。
② 通过DefaultNamespaceHandlerResolver解析指定元素命名空间对应的NamespaceHandler。
③ 通过查找的NamespaceHandler解析指定元素,取得Bean定义实例。
/** * 解析自定义元素. * @param ele 元素. * @param containingBd Bean定义. * @return 解析Bean定义. */ @Nullable public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) { // 获取元素命名空间. String namespaceUri = getNamespaceURI(ele); if (namespaceUri == null) { return null; } // 解析元素命名空间对应的NamespaceHandler. 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)); }
2) DefaultNamespaceHandlerResolver.resolve(…)方法
① 加载所有依赖的META-INF/spring.handlers文件,取得命名空间对应处理器映射。
② 根据命名空间从①的映射中查找对应的NamespaceHandler类。
③ 映射配置的NamespaceHandler类必须实现NamespaceHandler接口,否则抛出异常。
④ 根据配置映射的NamespaceHandler类路径进行实例化。
⑤ 调用NamespaceHandler的init()方法,指定命名空间元素对应的解析器。
/** * 从配置映射中查找所提供命名空间URI的NamespaceHandler. * @param namespaceUri 相关命名空间URI. * @return NamespaceHandler,若未找到,则为null. */ @Override @Nullable public NamespaceHandler resolve(String namespaceUri) { // 加载处理器映射. Map<String, Object> handlerMappings = getHandlerMappings(); // 根据namespaceUri查找处理器类路径. 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); // 处理器需实现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); // 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); } } }
3) DefaultNamespaceHandlerResolver.getHandlerMappings()方法
① 加载所有依赖的META-INF/spring.handlers文件,取得命名空间对应处理器映射,存储在Properties实例中。
② 将数据由Properties转存到Map中。
/** * 延迟加载指定的NamespaceHandler映射. */ private Map<String, Object> getHandlerMappings() { Map<String, Object> handlerMappings = this.handlerMappings; if (handlerMappings == null) { synchronized (this) { handlerMappings = this.handlerMappings; if (handlerMappings == null) { try { // 搜索META-INF/spring.handlers文件. Properties mappings = PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader); if (logger.isDebugEnabled()) { logger.debug("Loaded NamespaceHandler mappings: " + mappings); } Map<String, Object> mappingsToUse = new ConcurrentHashMap<>(mappings.size()); // 将Properties数据转换到Map中. 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; }
4) NamespaceHandlerSupport.parse(…)方法
① 使用提供元素的本地名称查找BeanDefinitionParser。
② 调用BeanDefinitionParser实现进行解析。
/** * 通过委托给为Element注册的BeanDefinitionParser来解析提供的Element. */ @Override @Nullable public BeanDefinition parse(Element element, ParserContext parserContext) { // 使用提供元素的本地名称查找BeanDefinitionParser. BeanDefinitionParser parser = findParserForElement(element, parserContext); // 调用BeanDefinitionParser实现进行解析. return (parser != null ? parser.parse(element, parserContext) : null); }
5) NamespaceHandlerSupport.findParserForElement(…)方法
① 通过指定元素,取得元素的本地参数名称。
② 根据本地名称查找已注册的BeanDefinitionParser。
/** * 使用提供的元素的本地名称查找BeanDefinitionParser. */ @Nullable private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) { // 获取提供元素的本地名称. String localName = parserContext.getDelegate().getLocalName(element); // 根据本地名称查找BeanDefinitionParser. BeanDefinitionParser parser = this.parsers.get(localName); if (parser == null) { parserContext.getReaderContext().fatal( "Cannot locate BeanDefinitionParser for element [" + localName + "]", element); } return parser; }
6) BeanDefinitionParser.parse(…)方法
BeanDefinitionParser.parse(...)是由具体开发进行编写的,其中涉及元素解析、元素校验、注册对应Beam。
总结
‘工欲善其事必先利其器’,只有当对原理的了解达到一定程度,才能在实际遇到问题,简洁、快速、高效的解决问题。
源码解析基于spring-framework-5.0.5.RELEASE版本源码。
若文中存在错误和不足,欢迎指正!
到此这篇关于Spring中的NamespaceHandler加载过程源码详解的文章就介绍到这了,更多相关NamespaceHandler加载过程内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!