xml 配置解析
xml 配置文件的解析由 XmlBeanDefinitionReader 来完成。XmlBeanDefinitionReader 加载 BeanDefinition 的入口方法如下。
@Override public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException { return loadBeanDefinitions(new EncodedResource(resource)); }
这里正是将前面提到的 AbstractBeanDefinitionReader 解析出的资源文件作为参数,然后转换为 EncodedResource。继续跟踪源码如下。
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { ... 省略部分代码 try (InputStream inputStream = encodedResource.getResource().getInputStream()) { InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); } ... 省略部分代码 }
这里将 EncodedResource 转换为 InputSource ,继续跟踪源码如下。
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { Document doc = doLoadDocument(inputSource, resource); int count = registerBeanDefinitions(doc, resource); if (logger.isDebugEnabled()) { logger.debug("Loaded " + count + " bean definitions from " + resource); } return count; } ... 省略部分代码 }
先把 InputSource 转换为 Document,然后解析 Document 注册 bean。解析 xml 是使用 jdk org.xml.sax 进行完成,查看 doLoadDocument 方法如下。
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception { return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler, getValidationModeForResource(resource), isNamespaceAware()); }
这里有一个 EntityResolver 需要留意,其用来获取本地的 dtd 或 xsd ,避免网络请求的开销或网络请求失败。主要的实现为 DelegatingEntityResolver,其会将 dtd 文件的获取委托为 BeansDtdResolver,将 xsd 文件的获取委托给 PluggableSchemaResolver。PluggableSchemaResolver 会从类路径下 META-INF/spring.schemas文件读取 xsd 文件的位置。spring-beans 模块下 spring.schemas 的部分内容如下。
http\://www.springframework.org/schema/beans/spring-beans.xsd=org/springframework/beans/factory/xml/spring-beans.xsd http\://www.springframework.org/schema/tool/spring-tool.xsd=org/springframework/beans/factory/xml/spring-tool.xsd http\://www.springframework.org/schema/util/spring-util.xsd=org/springframework/beans/factory/xml/spring-util.xsd
跟踪注册 BeanDefinition 的方法如下。
private Class<? extends BeanDefinitionDocumentReader> documentReaderClass = DefaultBeanDefinitionDocumentReader.class; // 注册 BeanDefinition public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); int countBefore = getRegistry().getBeanDefinitionCount(); documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); return getRegistry().getBeanDefinitionCount() - countBefore; } protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() { return BeanUtils.instantiateClass(this.documentReaderClass); }
这里 XmlBeanDefinitionReader 创建了一个 BeanDefinitionDocumentReader,其实现为 DefaultBeanDefinitionDocumentReader ,并将 BeanDefinition 的解析注册委托为这个对象。跟踪 DefaultBeanDefinitionDocumentReader#registerBeanDefinitions 源码,其调用方法如下。
protected void doRegisterBeanDefinitions(Element root) { BeanDefinitionParserDelegate parent = this.delegate; this.delegate = createDelegate(getReaderContext(), root, parent); if (this.delegate.isDefaultNamespace(root)) { // 如果 <beans/> 标签使用 Spring 默认的命名空间,并且指定的 profile 未被激活,则不处理 String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); if (StringUtils.hasText(profileSpec)) { String[] specifiedProfiles = StringUtils.tokenizeToStringArray( profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) { return; } } } preProcessXml(root); parseBeanDefinitions(root, this.delegate); postProcessXml(root); this.delegate = parent; }
这里已经进入了较为核心的处理逻辑,先获取 <beans/>
标签的 profile 属性,如果未被激活则不会再处理,否则开始解析 <beans/>
标签。后面的逻辑已经比较重要,在看的朋友留意。跟踪 parseBeanDefinitions 方法。
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); } }
这里对<beans/> 的子标签进行解析,如果发现使用的是默认的命名空间则直接解析,否则解析自定义的命名空间。<beans/> 标签下默认命名空间的标签包括<import/>、<alias/>、<bean/>、<beans/>,按照 xml dtd 或 xsd 的定义中规中矩的解析即可,而非默认命名空间的解析则是 Spring 留给我们扩展使用。下面把重点放到自定义标签的解析,跟踪BeanDefinitionParserDelegate#parseCustomElement(Element)方法,其又会调用如下的方法。
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)); }
该方法根据命名空间找到相应的命名空间处理器 NamespaceHandler ,然后进行解析。先看 NamespaceHandler 的定义。
public interface NamespaceHandler { // 初始化 void init(); // 将标签解析为 BeanDefinition @Nullable BeanDefinition parse(Element element, ParserContext parserContext); // 对 BeanDefinition 再次处理,Spring 使用其解析默认命名空间下标签的非默认命名空间属性或子标签 @Nullable BeanDefinitionHolder decorate(Node source, BeanDefinitionHolder definition, ParserContext parserContext);
那么命名空间处理器是如何获取的呢?其实现源码如下。
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(); // 缓存 NamespaceHandler 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); } } } }
NamespaceHandler 是从 handlerMappings 获取的,获取到后会先调用其 init 方法进行初始化。再来看 handlerMappings 的获取方法。
public class DefaultNamespaceHandlerResolver implements NamespaceHandlerResolver { public static final String DEFAULT_HANDLER_MAPPINGS_LOCATION = "META-INF/spring.handlers"; public DefaultNamespaceHandlerResolver() { this(null, DEFAULT_HANDLER_MAPPINGS_LOCATION); } public DefaultNamespaceHandlerResolver(@Nullable ClassLoader classLoader) { this(classLoader, DEFAULT_HANDLER_MAPPINGS_LOCATION); } private Map<String, Object> getHandlerMappings() { Map<String, Object> handlerMappings = this.handlerMappings; if (handlerMappings == null) { synchronized (this) { handlerMappings = this.handlerMappings; if (handlerMappings == null) { try { Properties mappings = PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader); 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; } }
这里看到,NamespaceHandler 最终是定义在 META-INF/spring.handlers
位置中,spring-beans 模块中也定义了这样的文件,用于解析命名空间c
、p
、util
。查看如下。
http\://www.springframework.org/schema/c=org.springframework.beans.factory.xml.SimpleConstructorNamespaceHandler http\://www.springframework.org/schema/p=org.springframework.beans.factory.xml.SimplePropertyNamespaceHandler http\://www.springframework.org/schema/util=org.springframework.beans.factory.xml.UtilNamespaceHandler
至此,Spring 默认命名空间和非默认命名空间的 xml 标签,Spring 都完美进行了解析。
自定义 xml 配置解析
根据上面的描述,如果需要解析 Spring xml 配置文件自定义的标签,我们只需要在类路径下 META-INF/spring.handlers 文件中定义自己的 NamespaceHandler 即可,事实上 Spring 提供了一个便于我们实现自己业务逻辑的 NamespaceHandlerSupport。代码较少,这里直接拿来。
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); } }
NamespaceHandlerSupport 将标签、属性、子标签的解析分别委托给 BeanDefinitionParser、BeanDefinitionDecorator。我们在 init 方法进行注册即可。
至此,总结 Spring 自定义 xml 配置文件标签解析的流程如下。
定义自己的命名空间对应的 xsd 文件,并在 META-INF/spring.schemas 文件注册,便于直接查找本地文件。
在 Spring xml 配置文件中指定自己的命名空间及对应的 xsd 文件路径。
实现 NamespaceHandlerSupport 类,在 init 方法注册自己的 BeanDefinitionParser、BeanDefinitionDecorator。
在类路径文件 META-INF/spring.handlers 中定义自己的 NamespaceHandler 。
BeanDefinition 注册
BeanDefinition 由 BeanDefinitionRegistry 进行注册。BeanDefinitionRegistry 定义如下。
public interface BeanDefinitionRegistry extends AliasRegistry { // 注册 BeanDefinition void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException; // 移除注册的 BeanDefinition void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException; // 获取注册的 BeanDefinition BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException; // 是否注册了给定名称的 BeanDefinition boolean containsBeanDefinition(String beanName); // 获取已经注册的 BeanDefinition String[] getBeanDefinitionNames(); // 获取注册的 BeanDefinition 的数量 int getBeanDefinitionCount(); // 给定名称的 BeanDefinition 是否已经注册,包括别名 boolean isBeanNameInUse(String beanName); }
BeanDefinitionRegistry 由 DefaultListableBeanFactory 进行实现,内部使用 map 保存 BeanDefinition,ApplicationContext 应用上下文的实现类中也实现了 BeanDefinitionRegistry ,其底层会委托给 DefaultListableBeanFactory 。如果我们需要注册自定义的 BeanDefinition,只需要通过 BeanFactoryAware 、@Autowire 注入、ConfigurableApplicationContext 等方法获取 DefaultListableBeanFactory 实例即可。
总结
BeanDefinition 作为 bean 定义的元数据,是理解 Spring 的基石,本篇对 BeanDefinition 的分类、解析、注册详细进行了描述,希望对大家有帮忙,欢迎留言讨论。