@joshsulin
2018-07-20T03:03:36.000000Z
字数 6211
阅读 657
技术分享
参考网上文章: https://blog.csdn.net/tong_xinglong/article/details/52035131

??????
开发流程
1、创建一个需要扩展的组件。
2、定义一个XSD文件描述组件内容。
3、创建一个文件, 实现BeanDefinitionParser接口, 用来解析XSD文件中的定义和组件定义。
4、创建一个Handler文件, 扩展自NamespaceHandlerSuport, 目的就是实现我们该自定义标签要完成的功能。
5、编写Spring.handlers和Spring.schemas文件。
归功于Spring在设计之初就一贯坚持的设计原则: 开闭原则: 对扩展开放(Open for extension), 对修改关闭(Closed for modification)。所以我们可以在不修改Spring源代码的情况下扩展Spring的功能。
下面我们就来看一下Spring对应XML标签的处理
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);}}
上面的代码逻辑还是很清晰的, 一种处理默认标签, 如
<bean id="test" class="com.Test"/>
另一类是自定义的。如下
<mvc:interceptors>
下面我们来看一下自定义标签的解析原理
public class BeanDefinitionParserDelegate {public BeanDefinition parseCustomElement(Element ele) {return parseCustomElement(ele, null);}public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {String namespaceUri = getNamespaceURI(ele);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));}}
我们看到, 其实自定义标签的处理流程很简单。
1、获取到命名空间
2、根据命名空间找到对应的命名空间处理器
3、根据用户自定义的处理器进行解析
第一步很简单, 我们跳过。我们来看一下第二步的实现。
public class DefaultNamespaceHandlerResolver implements NamespaceHandlerResolver {/*** Locate the {@link NamespaceHandler} for the supplied namespace URI* from the configured mappings.* @param namespaceUri the relevant namespace URI* @return the located {@link NamespaceHandler}, or {@code null} if none found*/@Overridepublic NamespaceHandler resolve(String namespaceUri) {// 获取所有已经配置的handler映射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 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();// 存入缓存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);}}}/*** Load the specified NamespaceHandler mappings lazily.*/private Map<String, Object> getHandlerMappings() {// 如果没有缓存则开始进行缓存if (this.handlerMappings == null) {synchronized (this) {if (this.handlerMappings == null) {try {// this.handlerMappingsLocation在构造函数中初始化为META-INF/spring.handlersProperties mappings =PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);if (logger.isDebugEnabled()) {logger.debug("Loaded NamespaceHandler mappings: " + mappings);}Map<String, Object> handlerMappings = new ConcurrentHashMap<String, Object>(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 this.handlerMappings;}}
现在我们知道了为什么需要在METE-INF/spring.handlers文件中那样配置了。因为在加载所有的处理器的配置的默认目录就是META-INF/spring.handlers
通过上面代码可以看到, 初始化时都调用了 init 方法
// 调用自定义的NameSpaceHandler的init方法namespaceHandler.init();
进入init方法进行分析。
public class MvcNamespaceHandler extends NamespaceHandlerSupport {@Overridepublic void init() {registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());registerBeanDefinitionParser("default-servlet-handler", new DefaultServletHandlerBeanDefinitionParser());registerBeanDefinitionParser("interceptors", new InterceptorsBeanDefinitionParser());registerBeanDefinitionParser("resources", new ResourcesBeanDefinitionParser());registerBeanDefinitionParser("view-controller", new ViewControllerBeanDefinitionParser());registerBeanDefinitionParser("redirect-view-controller", new ViewControllerBeanDefinitionParser());registerBeanDefinitionParser("status-controller", new ViewControllerBeanDefinitionParser());registerBeanDefinitionParser("view-resolvers", new ViewResolversBeanDefinitionParser());registerBeanDefinitionParser("tiles-configurer", new TilesConfigurerBeanDefinitionParser());registerBeanDefinitionParser("freemarker-configurer", new FreeMarkerConfigurerBeanDefinitionParser());registerBeanDefinitionParser("velocity-configurer", new VelocityConfigurerBeanDefinitionParser());registerBeanDefinitionParser("groovy-configurer", new GroovyMarkupConfigurerBeanDefinitionParser());registerBeanDefinitionParser("script-template-configurer", new ScriptTemplateConfigurerBeanDefinitionParser());registerBeanDefinitionParser("cors", new CorsBeanDefinitionParser());}}
registerBeanDefinitionParser 方法由父抽象类NamespaceHandlerSupport默认实现。
protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) {this.parsers.put(elementName, parser);}
第一个参数是elementName, 即元素名称, 即告诉Spring你要解析哪个标签, 第二个参数是BeanDefinitionParser的实现类, BeanDefinitionParser是Spring用来将xml元素转换成BeanDefinition对象的接口。