SpringIOC-命名空间解析
什么是命名空间 什么是spring命名空间?这个就要从XML说了,Spring的配置管理可以利用XML方式进行配置,而XML里面就有命名空间这个概念。。实际上就和标签的意思有点像 你给一个命名空间以后,这个XML文件里面就可以用那个命名空间上下文里面的标签了。简化配置用,你可以去看看Spring AOP用命名空间和不用命名空间的配置有什么区别。
首先xmlns=”http://www.springframework.org/schema/beans" xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance 是必须有的。 xsi:schemaLocation:为指定了用于解析和校验xml的定义文件(xsd)的位置。
xmlns 是为引用Spring的模块功能指定命名空间。其中的content是”http://www.springframework.org/schema/content"这个命名空间的简称,"http://www.springframework.org/schema/content"是命名空间的全称。必须在xsi中为命名空间指定对应的schema文件。
xsi:schemaLocation 是为每个命名空间指定了对应的Schema文档,其定义的语法为:xsi:schemaLocation =”全称命名空间1 全称命名空间1对应的Schema文件空格”。
命名空间分析
我们拿如下的spring配置文件举例
1 2 3 4 5 6 7 8 9 10 11 12 <?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:context ="http://www.springframework.org/schema/context" xmlns:p ="http://www.springframework.org/schema/p" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd" > <context:component-scan base-package ="com.test" /> <bean id ="userDao" class ="com.test.spring.dao.UserDao" p:dialect ="MYSQL" /> </beans >
解析步骤 定位命名
根据相应的标签找到对应的xmlns对应的地址
首先找到context或者p 命令空间的xmlns
context 的命令空间是 http://www.springframework.org/schema/context
p 的命令空间是 http://www.springframework.org/schema/p
找到解析类
根据相应的jar包,从jar包的META-INF下的spring.handlers找到xmlns地址对应的解析类
spring.handlers对应的内容
spring-bean对应标签的映射关系
spring-context对应标签的映射关系
解析类分析
我们发现映射的类都实现了NamespaceHandler接口
SimplePropertyNamespaceHandler 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 public class SimplePropertyNamespaceHandler implements NamespaceHandler { private static final String REF_SUFFIX = "-ref" ; @Override public void init () { } @Override @Nullable public BeanDefinition parse (Element element, ParserContext parserContext) { parserContext.getReaderContext().error( "Class [" + getClass().getName() + "] does not support custom elements." , element); return null ; } @Override public BeanDefinitionHolder decorate (Node node, BeanDefinitionHolder definition, ParserContext parserContext) { if (node instanceof Attr) { Attr attr = (Attr) node; String propertyName = parserContext.getDelegate().getLocalName(attr); String propertyValue = attr.getValue(); MutablePropertyValues pvs = definition.getBeanDefinition().getPropertyValues(); if (pvs.contains(propertyName)) { parserContext.getReaderContext().error("Property '" + propertyName + "' is already defined using " + "both <property> and inline syntax. Only one approach may be used per property." , attr); } if (propertyName.endsWith(REF_SUFFIX)) { propertyName = propertyName.substring(0 , propertyName.length() - REF_SUFFIX.length()); pvs.add(Conventions.attributeNameToPropertyName(propertyName), new RuntimeBeanReference (propertyValue)); } else { pvs.add(Conventions.attributeNameToPropertyName(propertyName), propertyValue); } } return definition; } }
ContextNamespaceHandler 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public class ContextNamespaceHandler extends NamespaceHandlerSupport { @Override public void init () { registerBeanDefinitionParser("property-placeholder" , new PropertyPlaceholderBeanDefinitionParser ()); registerBeanDefinitionParser("property-override" , new PropertyOverrideBeanDefinitionParser ()); registerBeanDefinitionParser("annotation-config" , new AnnotationConfigBeanDefinitionParser ()); registerBeanDefinitionParser("component-scan" , new ComponentScanBeanDefinitionParser ()); registerBeanDefinitionParser("load-time-weaver" , new LoadTimeWeaverBeanDefinitionParser ()); registerBeanDefinitionParser("spring-configured" , new SpringConfiguredBeanDefinitionParser ()); registerBeanDefinitionParser("mbean-export" , new MBeanExportBeanDefinitionParser ()); registerBeanDefinitionParser("mbean-server" , new MBeanServerBeanDefinitionParser ()); } }
进行初始化注册
NamespaceHandler接口是有一个init方法,主要来完成对应标签内容的解析类注册。
1 2 3 4 5 private final Map<String, BeanDefinitionParser> parsers = new HashMap <>();protected final void registerBeanDefinitionParser (String elementName, BeanDefinitionParser parser) { this .parsers.put(elementName, parser); }
命令空间相关解析类
Spring在解析xml文件时,主要用到NamespaceHandlerResolver接口、NamespaceHandler接口、BeanDefinitionParser接口。
NamespaceHandlerResolver
NamespaceHandlerResolver接口,是为了获取BeanDefinitionParser接口NamespaceHandler接口实例
1 2 3 4 5 6 7 8 9 10 11 12 @FunctionalInterface public interface NamespaceHandlerResolver { @Nullable NamespaceHandler resolve (String namespaceUri) ; }
DefaultNamespaceHandlerResolver 类
这个类实现了NamespaceHandlerResolver接口,是NamespaceHandlerResolver的唯一实现类,核心方法是resolve,迎来根据URI来获取对应的NamespaceHandler实现类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 public class DefaultNamespaceHandlerResolver implements NamespaceHandlerResolver {.... @Override @Nullable public NamespaceHandler resolve (String namespaceUri) { 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(); 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); } } } private Map<String, Object> getHandlerMappings () { Map<String, Object> handlerMappings = this .handlerMappings; if (handlerMappings == null ) { synchronized (this ) { handlerMappings = this .handlerMappings; if (handlerMappings == null ) { if (logger.isTraceEnabled()) { logger.trace("Loading NamespaceHandler mappings from [" + this .handlerMappingsLocation + "]" ); } try { Properties mappings = PropertiesLoaderUtils.loadAllProperties(this .handlerMappingsLocation, this .classLoader); if (logger.isTraceEnabled()) { logger.trace("Loaded NamespaceHandler mappings: " + mappings); } handlerMappings = new ConcurrentHashMap <>(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 handlerMappings; } ... }
我们知道 handlerMappingsLocation 的默认值为 “META-INF/spring.handlers”,因此在这边会使用指定的 classLoader 从所有类路径资源(META-INF/spring.handlers)加载所有属性,并使用 Properties 来存放 spring.handlers 文件中的内容(命名空间和 handler 的键值对,例如下图)。
2.2 将 Properties 转成 Map。其中 key 为命名空间,例如:http://www.springframework.org/schema/context;value 为命名空间对应的 handler,例如:org.springframework.context.config.ContextNamespaceHandler,所有的handler都需要自己实现。
2.3 最后将转换后的 handlerMappings 放到缓存。
NamespaceHandler接口
NamespaceHandler接口,是为了根据不同的命名空间和元素获取不同的BeanDefinitionParser接口实例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public interface NamespaceHandler { void init () ; @Nullable BeanDefinition parse (Element element, ParserContext parserContext) ; @Nullable BeanDefinitionHolder decorate (Node source, BeanDefinitionHolder definition, ParserContext parserContext) ; }
NamespaceHandlerSupport抽象类
NamespaceHandlerSupport实现了NamespaceHandler接口,是一个模板设计模式,是对通用的注册方法进行了抽取
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 public abstract class NamespaceHandlerSupport implements NamespaceHandler { private final Map<String, BeanDefinitionParser> parsers = new HashMap <>(); private final Map<String, BeanDefinitionDecorator> decorators = new HashMap <>(); private final Map<String, BeanDefinitionDecorator> attributeDecorators = new HashMap <>(); @Override @Nullable public BeanDefinition parse (Element element, ParserContext parserContext) { BeanDefinitionParser parser = findParserForElement(element, parserContext); return (parser != null ? parser.parse(element, parserContext) : null ); } @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; } @Override @Nullable public BeanDefinitionHolder decorate ( Node node, BeanDefinitionHolder definition, ParserContext parserContext) { BeanDefinitionDecorator decorator = findDecoratorForNode(node, parserContext); return (decorator != null ? decorator.decorate(node, definition, parserContext) : null ); } @Nullable private BeanDefinitionDecorator findDecoratorForNode (Node node, ParserContext parserContext) { BeanDefinitionDecorator decorator = null ; String localName = parserContext.getDelegate().getLocalName(node); if (node instanceof Element) { decorator = this .decorators.get(localName); } else if (node instanceof Attr) { decorator = this .attributeDecorators.get(localName); } else { parserContext.getReaderContext().fatal( "Cannot decorate based on Nodes of type [" + node.getClass().getName() + "]" , node); } if (decorator == null ) { parserContext.getReaderContext().fatal("Cannot locate BeanDefinitionDecorator for " + (node instanceof Element ? "element" : "attribute" ) + " [" + localName + "]" , node); } return decorator; } protected final void registerBeanDefinitionParser (String elementName, BeanDefinitionParser parser) { this .parsers.put(elementName, parser); } protected final void registerBeanDefinitionDecorator (String elementName, BeanDefinitionDecorator dec) { this .decorators.put(elementName, dec); } protected final void registerBeanDefinitionDecoratorForAttribute (String attrName, BeanDefinitionDecorator dec) { this .attributeDecorators.put(attrName, dec); } }
BeanDefinitionParser接口
用来不同解析类的实现接口,调用parse来解析元素
1 2 3 4 5 6 public interface BeanDefinitionParser { @Nullable BeanDefinition parse (Element element, ParserContext parserContext) ; }
解析流程
Spring在解析xml文件时,主要调用的是DefaultBeanDefinitionDocumentReader类的parseBeanDefinitions方法。
在parseBeanDefinitions方法内,http://www.springframework.org/schema/beans视为默认命名空间,调用DefaultBeanDefinitionDocumentReader类的parseDefaultElement方法解析。其他命名空间,spring视为自定义命名空间,需要调用对应的NamespaceHandler实现类的parse方法进行解析。
Spring对于xml元素的解析,最终落到BeanDefinitionParser接口的实现类上。具体顺序是:
先根据命名空间找到对应的NamespaceHandler接口的实现类。
然后在实现类的init方法中,初始化当前命名空间下,不同元素对应的BeanDefinitionParser解析类。根据这种元素与解析类的对应关系,调用对应BeanDefinitionParser接口实现类的parse方法解析即可。
命名空间与NamespaceHandler对应关系在对应工程的spring.handlers文件内。
代码流程
例如自定义标签解析的方法parseCustomElement
我们拿解析context标签举例
1 2 3 4 5 6 7 8 9 10 11 12 13 public BeanDefinition parseCustomElement (Element ele, @Nullable BeanDefinition containingBd) { 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)); }
获取标签的URI
会根据Element获取对应元素schema的namespaceUri,然后根据namespaceUri获取到具体的NamespaceHandler接口的实现。针对context这个schema,对应的是http://www.springframework.org/schema/context
1 String namespaceUri = getNamespaceURI(ele);
获取解析类
根据对应的URI获取对应的NamespaceHandler,例如context标签就会根据http://www.springframework.org/schema/context从spring.handlers配置文件获取ContextNamespaceHandler的解析文件,并在resolve方法中调用init方法完成了NamespaceHandler的初始化,接下来就是具体的解析流程
1 NamespaceHandler handler = this .readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
具体解析
调用parse方法完成了对具体元素的解析,完成将元素转换成BeanDefinition的过程,具体过程可以在具体的解析类会在讲源码的过程中讲解,这里只进行命名标签流程的梳理