Spring在解析xml文件时,主要用到NamespaceHandlerResolver接口、NamespaceHandler接口、BeanDefinitionParser接口。
NamespaceHandlerResolver接口,是为了获取BeanDefinitionParser接口NamespaceHandler接口实例。
package org.springframework.beans.factory.xml;
/**
* Used by the {@link org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader} to
* locate a {@link NamespaceHandler} implementation for a particular namespace URI.
*
* @author Rob Harrop
* @since 2.0
* @see NamespaceHandler
* @see org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader
*/
public interface NamespaceHandlerResolver {
/**
* Resolve the namespace URI and return the located {@link NamespaceHandler}
* implementation.
* @param namespaceUri the relevant namespace URI
* @return the located {@link NamespaceHandler} (may be {@code null})
*/
NamespaceHandler resolve(String namespaceUri);
}
NamespaceHandler接口,是为了根据不同的命名空间和元素获取不同的BeanDefinitionParser接口实例。
/**
* Base interface used by the {@link DefaultBeanDefinitionDocumentReader}
* for handling custom namespaces in a Spring XML configuration file.
*
* <p>Implementations are expected to return implementations of the
* {@link BeanDefinitionParser} interface for custom top-level tags and
* implementations of the {@link BeanDefinitionDecorator} interface for
* custom nested tags.
*
* <p>The parser will call {@link #parse} when it encounters a custom tag
* directly under the {@code <beans>} tags and {@link #decorate} when
* it encounters a custom tag directly under a {@code <bean>} tag.
*
* <p>Developers writing their own custom element extensions typically will
* not implement this interface drectly, but rather make use of the provided
* {@link NamespaceHandlerSupport} class.
*
* @author Rob Harrop
* @author Erik Wiersma
* @since 2.0
* @see DefaultBeanDefinitionDocumentReader
* @see NamespaceHandlerResolver
*/
public interface NamespaceHandler {
/**
* Invoked by the {@link DefaultBeanDefinitionDocumentReader} after
* construction but before any custom elements are parsed.
* @see NamespaceHandlerSupport#registerBeanDefinitionParser(String, BeanDefinitionParser)
*/
void init();
/**
* Parse the specified {@link Element} and register any resulting
* {@link BeanDefinition BeanDefinitions} with the
* {@link org.springframework.beans.factory.support.BeanDefinitionRegistry}
* that is embedded in the supplied {@link ParserContext}.
* <p>Implementations should return the primary {@code BeanDefinition}
* that results from the parse phase if they wish to be used nested
* inside (for example) a {@code <property>} tag.
* <p>Implementations may return {@code null} if they will
* <strong>not</strong> be used in a nested scenario.
* @param element the element that is to be parsed into one or more {@code BeanDefinitions}
* @param parserContext the object encapsulating the current state of the parsing process
* @return the primary {@code BeanDefinition} (can be {@code null} as explained above)
*/
BeanDefinition parse(Element element, ParserContext parserContext);
/**
* Parse the specified {@link Node} and decorate the supplied
* {@link BeanDefinitionHolder}, returning the decorated definition.
* <p>The {@link Node} may be either an {@link org.w3c.dom.Attr} or an
* {@link Element}, depending on whether a custom attribute or element
* is being parsed.
* <p>Implementations may choose to return a completely new definition,
* which will replace the original definition in the resulting
* {@link org.springframework.beans.factory.BeanFactory}.
* <p>The supplied {@link ParserContext} can be used to register any
* additional beans needed to support the main definition.
* @param source the source element or attribute that is to be parsed
* @param definition the current bean definition
* @param parserContext the object encapsulating the current state of the parsing process
* @return the decorated definition (to be registered in the BeanFactory),
* or simply the original bean definition if no decoration is required.
* A {@code null} value is strictly speaking invalid, but will be leniently
* treated like the case where the original bean definition gets returned.
*/
BeanDefinitionHolder decorate(Node source, BeanDefinitionHolder definition, ParserContext parserContext);
}
BeanDefinitionParser接口,不同元素的解析实现。
/**
* Interface used by the {@link DefaultBeanDefinitionDocumentReader} to handle custom,
* top-level (directly under {@code <beans/>}) tags.
*
* <p>Implementations are free to turn the metadata in the custom tag into as many
* {@link BeanDefinition BeanDefinitions} as required.
*
* <p>The parser locates a {@link BeanDefinitionParser} from the associated
* {@link NamespaceHandler} for the namespace in which the custom tag resides.
*
* @author Rob Harrop
* @since 2.0
* @see NamespaceHandler
* @see AbstractBeanDefinitionParser
*/
public interface BeanDefinitionParser {
/**
* Parse the specified {@link Element} and register the resulting
* {@link BeanDefinition BeanDefinition(s)} with the
* {@link org.springframework.beans.factory.xml.ParserContext#getRegistry() BeanDefinitionRegistry}
* embedded in the supplied {@link ParserContext}.
* <p>Implementations must return the primary {@link BeanDefinition} that results
* from the parse if they will ever be used in a nested fashion (for example as
* an inner tag in a {@code <property/>} tag). Implementations may return
* {@code null} if they will <strong>not</strong> be used in a nested fashion.
* @param element the element that is to be parsed into one or more {@link BeanDefinition BeanDefinitions}
* @param parserContext the object encapsulating the current state of the parsing process;
* provides access to a {@link org.springframework.beans.factory.support.BeanDefinitionRegistry}
* @return the primary {@link BeanDefinition}
*/
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文件内。
例如,spring-aop工程中,spring.handlers内容如下
http\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler
这说明,针对aop命名空间,都采用AopNamespaceHandler类进行解析,以一段aop的xml配置文件为例。
<aop:config>
<aop:aspect id="aspect" ref="xmlHandler">
<aop:pointcut id="pointUserMgr" expression="execution(* com.tgb.aop.*.find*(..))"/>
<aop:before method="doBefore" pointcut-ref="pointUserMgr"/>
</aop:aspect>
</aop:config>
细化到aop命名空间下的元素,元素与对应的parser关系,在AopNamespaceHandler类的init方法中指定。针对上文的xml配置<aop:config>,可以根据config得出这段配置
需要用ConfigBeanDefinitionParser类进行解析的结论。
public class AopNamespaceHandler extends NamespaceHandlerSupport {
/**
* Register the {@link BeanDefinitionParser BeanDefinitionParsers} for the
* '{@code config}', '{@code spring-configured}', '{@code aspectj-autoproxy}'
* and '{@code scoped-proxy}' tags.
*/
public void init() {
// In 2.0 XSD as well as in 2.1 XSD.
registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());
// Only in 2.0 XSD: moved to context namespace as of 2.1
registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
}
}
代码细化,例如,http://www.springframework.org/schema/aop,调用BeanDefinitionParserDelegate类的parseCustomElement方法解析。parseCustomElement方法内,
会根据Element获取对应元素schema的namespaceUri,然后根据namespaceUri获取到具体的NamespaceHandler接口的实现。针对aop这个schema,对应的是
AopNamespaceHandler类。所以针对aop的解析,最终应该调用的都是AopNamespaceHandler.parse这个方法。但是,AopNamespaceHandler没有parse方法,所以最终调用
的是AopNamespaceHandler父类,即抽象类NamespaceHandlerSupport的parse方法。spring中非默认命名空间的解析,基本都是使用的NamespaceHandlerSupport这个抽象
类。
public abstract class NamespaceHandlerSupport implements NamespaceHandler {
/**
* Stores the {@link BeanDefinitionParser} implementations keyed by the
* local name of the {@link Element Elements} they handle.
*/
private final Map<String, BeanDefinitionParser> parsers = new HashMap<String, BeanDefinitionParser>();
..............
/**
* Parses the supplied {@link Element} by delegating to the {@link BeanDefinitionParser} that is
* registered for that {@link Element}.
*/
public BeanDefinition parse(Element element, ParserContext parserContext) {
return findParserForElement(element, parserContext).parse(element, parserContext);
}
/**
* Locates the {@link BeanDefinitionParser} from the register implementations using
* the local name of the supplied {@link Element}.
*/
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;
}
......
}
NamespaceHandlerSupport的parse方法内的代码,可以看出最终解析元素的都是BeanDefinitionParser接口的实现类。
public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader {
.............
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
//对beans处理
if (delegate.isDefaultNamespace(root)) {
logger.info("root nodeToString() = " + nodeToString(root));
//System.out.println("root.getNodeName() = " + root.getNodeName());
//System.out.println("root.nodeToString() = " + nodeToString(root));
NodeList nl = root.getChildNodes();
//System.out.println("nl.getLength() = " + nl.getLength());
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
logger.info("node.nodeToString() = " + nodeToString(node));
if (node instanceof Element) {
Element ele = (Element) node;
logger.info("parseBeanDefinitions ele getNamespaceURI = " + delegate.getNamespaceURI(ele));
//BEANS_NAMESPACE_URI = "http://www.springframework.org/schema/beans";
if (delegate.isDefaultNamespace(ele)) {
//对bean处理,对import、alias、bean和beans标签处理
//System.out.println("ele.nodeToString() = " + nodeToString(ele));
//logger.info("parseDefaultElement nodeToString(ele) = " + nodeToString(ele));
parseDefaultElement(ele, delegate);
}else {
//对beans处理
//logger.info("parseCustomElement nodeToString(ele) = " + nodeToString(ele));
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
}
BeanDefinitionParserDelegate类的说明
对于非beans的schema,例如http://www.springframework.org/schema/mvc,采用BeanDefinitionParserDelegate.parseCustomElement方法进行解析。
parseCustomElement方法内部,根据通过参数ele解析出对应的namespaceUri,之后根据namespaceUri找到对应的NamespaceHandler接口实现,之后调用接口实现类的
parse实现解析。
public class BeanDefinitionParserDelegate {
public static final String BEANS_NAMESPACE_URI = "http://www.springframework.org/schema/beans";
......
public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
logger.info("parseCustomElement = " + DefaultBeanDefinitionDocumentReader.nodeToString(ele));
String namespaceUri = getNamespaceURI(ele);
logger.info("parseCustomElement namespaceUri = " + namespaceUri);
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()返回的是NamespaceHandlerResolver接口的实现类,具体是DefaultNamespaceHandlerResolver类。
DefaultNamespaceHandlerResolver.resolve方法负责根据namespaceUri,找到对应的NamespaceHandler接口的实例并返回。
/**
* Default implementation of the {@link NamespaceHandlerResolver} interface.
* Resolves namespace URIs to implementation classes based on the mappings
* contained in mapping file.
*
* <p>By default, this implementation looks for the mapping file at
* {@code META-INF/spring.handlers}, but this can be changed using the
* {@link #DefaultNamespaceHandlerResolver(ClassLoader, String)} constructor.
*
* @author Rob Harrop
* @author Juergen Hoeller
* @since 2.0
* @see NamespaceHandler
* @see DefaultBeanDefinitionDocumentReader
*/
public class DefaultNamespaceHandlerResolver implements NamespaceHandlerResolver {
/**
* The location to look for the mapping files. Can be present in multiple JAR files.
*/
public static final String DEFAULT_HANDLER_MAPPINGS_LOCATION = "META-INF/spring.handlers";
public DefaultNamespaceHandlerResolver() {
this(null, DEFAULT_HANDLER_MAPPINGS_LOCATION);
}
/**
* 参数namespaceUri值,例如http://www.springframework.org/schema/aop
* 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
*/
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("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.handlers,DEFAULT_HANDLER_MAPPINGS_LOCATION = "META-INF/spring.handlers";
Properties 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;
}
}
DefaultNamespaceHandlerResolver说明
DefaultNamespaceHandlerResolver类内,代码Map<String, Object> handlerMappings = getHandlerMappings(),getHandlerMappings()方法内,读取的是
spring.handlers文件内的属性。
spring.handlers举例,
spring-aop工程中内容如下
http\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler
spring-beans工程中内容如下
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-context工程中内容如下
http\://www.springframework.org/schema/context=org.springframework.context.config.ContextNamespaceHandler
http\://www.springframework.org/schema/jee=org.springframework.ejb.config.JeeNamespaceHandler
http\://www.springframework.org/schema/lang=org.springframework.scripting.config.LangNamespaceHandler
http\://www.springframework.org/schema/task=org.springframework.scheduling.config.TaskNamespaceHandler
http\://www.springframework.org/schema/cache=org.springframework.cache.config.CacheNamespaceHandler
spring-webmvc工程中内容如下
http\://www.springframework.org/schema/mvc=org.springframework.web.servlet.config.MvcNamespaceHandler
public class MvcNamespaceHandler extends NamespaceHandlerSupport {
public 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());
}
}
类图