1. Xml自定义命名空间bean元素解析
1.1. 自定义命名空间
Spring xml配置文件的默认命名空间为http://www.springframework.org/schema/beans,除此之外的其他命名空间都为自定义命名空间。
Spring 默认提供了很多常用的自定义命名空间, 如:
http://www.springframework.org/schema/contexthttp://www.springframework.org/schema/chttp\://www.springframework.org/schema/phttp\://www.springframework.org/schema/util
1.2. 自定义命名空间解析过程
下面,我们以context命名空间为例,来看一下Spring是如何解析context命名空间的。
假如在xml配置文件中有一句配置:
<context:component-scan base-package="samples.spring"></context:component-scan>
1.2.1. BeanDefinitionParserDelegate
在解析xml元素的时候,Spring会判断此元素是否默认命名空间,如果不是,则会调用BeanDefinitionParserDelegate的parseCustomElement(ele)方法来进行解析。
if (delegate.isDefaultNamespace(ele)) {
parseDefaultElement(ele, delegate);
}
else {
delegate.parseCustomElement(ele);
}
看一下解析源码:
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));
}
- 获取element元素的namespaceUri;
- 通过
readerContext来找到此命名空间的NamespaceHandler; - 调用halder的
BeanDefinition parse(Element element, ParserContext parserContext);方法进行解析;
可以看到,Spring通过NamespaceHandler命名空间处理器来解析不同的命名空间。
1.2.2. NamespaceHandler
DefaultBeanDefinitionDocumentReader会调用NamespaceHandler来处理xml配置文件中的自定义命名空间。NamespaceHandler是一个接口,其实现类需要返回BeanDefinitionParser的一个实现。
public interface NamespaceHandler {
/**
* Invoked by the {@link DefaultBeanDefinitionDocumentReader} after
* construction but before any custom elements are parsed.
* @see NamespaceHandlerSupport#registerBeanDefinitionParser(String, BeanDefinitionParser)
*/
void init();
BeanDefinition parse(Element element, ParserContext parserContext);
BeanDefinitionHolder decorate(Node source, BeanDefinitionHolder definition, ParserContext parserContext);
}
1.2.3. XmlReaderContext
ReaderContext类是Spring 在解析BeanDefinition过程中使用的上下文,用来封装一些配置和状态信息;
public class ReaderContext {
private final Resource resource;
private final ProblemReporter problemReporter;
private final ReaderEventListener eventListener;
private final SourceExtractor sourceExtractor;
public ReaderContext(Resource resource, ProblemReporter problemReporter,
ReaderEventListener eventListener, SourceExtractor sourceExtractor) {
this.resource = resource;
this.problemReporter = problemReporter;
this.eventListener = eventListener;
this.sourceExtractor = sourceExtractor;
}
}
XmlReaderContext继承自ReaderContext,具体用于提供访问权限XmlBeanDefinitionReader中配置的NamespaceHandlerResolver。
public class XmlReaderContext extends ReaderContext {
private final XmlBeanDefinitionReader reader;
private final NamespaceHandlerResolver namespaceHandlerResolver;
public XmlReaderContext(
Resource resource, ProblemReporter problemReporter,
ReaderEventListener eventListener, SourceExtractor sourceExtractor,
XmlBeanDefinitionReader reader, NamespaceHandlerResolver namespaceHandlerResolver) {
super(resource, problemReporter, eventListener, sourceExtractor);
this.reader = reader;
this.namespaceHandlerResolver = namespaceHandlerResolver;
}
}
xmlReaderContext对象是在XmlBeanDefinitionReader类中创建的,其创建过程如下:
public XmlReaderContext createReaderContext(Resource resource) {
return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
this.sourceExtractor, this, getNamespaceHandlerResolver());
}
其他的参数先不管,重点关注一下最后一个参数即namespaceHandlerResolver的创建过程:
public NamespaceHandlerResolver getNamespaceHandlerResolver() {
if (this.namespaceHandlerResolver == null) {
this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver();
}
return this.namespaceHandlerResolver;
}
protected NamespaceHandlerResolver createDefaultNamespaceHandlerResolver() {
return new DefaultNamespaceHandlerResolver(getResourceLoader().getClassLoader());
}
找到这里,终于发现,决定Spring使用哪个NamespaceHandler来解析xml命名空间的是DefaultNamespaceHandlerResolver类。
1.2.4. DefaultNamespaceHandlerResolver
resolve方法解析命名空间的流程如下:
- 获取所有的hadlerMappings map对象,获取是从所有jar包下的
META-INF/spring.handlers文件中获取。 - 创建handler对象
- 调用handler的
init方法,此方法用来注册BeanDefinitionParser
public class DefaultNamespaceHandlerResolver implements NamespaceHandlerResolver {
public static final String DEFAULT_HANDLER_MAPPINGS_LOCATION = "META-INF/spring.handlers";
private final String handlerMappingsLocation;
public DefaultNamespaceHandlerResolver() {
this(null, DEFAULT_HANDLER_MAPPINGS_LOCATION);
}
public DefaultNamespaceHandlerResolver(ClassLoader classLoader, String handlerMappingsLocation) {
Assert.notNull(handlerMappingsLocation, "Handler mappings location must not be null");
this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
this.handlerMappingsLocation = handlerMappingsLocation;
}
@Override
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 {
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;
}
}
1.2.5. context命名空间
- 在
spring-contextjar包下的META-INF/spring.handlers文件内容:
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
可见,此包提供了5个命名空间的支持,key是命名空间,value是NamespaceHandler类的实现;
- 由上可见,
context命名空间处理类为org.springframework.context.config.ContextNamespaceHandler
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());
}
}
此类定义了一些xml元素的处理类,而我们关注的<component-scan/>元素的处理类是ComponentScanBeanDefinitionParser;