spring中xml文件解析说明及NamespaceHandler接口应用

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());
	}

}




类图





  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
您可以按照以下步骤来给spring.xml文件配置应用程序上下文: 1. 在您的项目创建一个名为“resources”的文件夹,并在其创建一个名为“spring.xml”的文件。 2. 在spring.xml文件定义应用程序上下文。例如,您可以使用以下代码: ``` <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- 定义bean --> <bean id="exampleBean" class="com.example.ExampleBean"> <property name="exampleProperty" value="exampleValue" /> </bean> </beans> ``` 在这个例子,我们定义了一个名为“exampleBean”的bean,它的类是“com.example.ExampleBean”,并设置了一个名为“exampleProperty”的属性值为“exampleValue”。 3. 在您的代码,创建一个ApplicationContext对象,并将其初始化为spring.xml文件定义的应用程序上下文。例如,您可以使用以下代码: ``` ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml"); ``` 这将创建一个基于类路径的应用程序上下文,并将其初始化为spring.xml文件定义的上下文。 4. 现在您可以使用ApplicationContext对象来获取您在spring.xml文件定义的bean。例如,您可以使用以下代码: ``` ExampleBean exampleBean = (ExampleBean) context.getBean("exampleBean"); ``` 这将获取名为“exampleBean”的bean,并将其强制转换为“com.example.ExampleBean”类型的对象。 这样,您就可以配置应用程序上下文并使用它来获取您在spring.xml文件定义的bean了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值