spring源码学习之四 BeanDefinitionParserDelegate分析
2016-02-03 15:34
821 查看
BeanDefinitionParserDelegate分析
上篇文章中提到过,在DefaultBeanDefinitionDocumentReader处理Document元素时,将Document文档内元素具体解析工作委托给BeanDefinitionParserDelegate类来处理,默认BeanDefinitionParserDelegate会处理”http://www.springframework.org/schema/beans“命名空间下元素及其属性,查看源码可以看到BeanDefinitionParserDelegate下面定义了一堆元素和属性名称,这些元素和属性名称分别可以在类中找到处理方法./** * Parse the bean definition itself, without regard to name or aliases. May return * {@code null} if problems occurred during the parsing of the bean definition. */ public AbstractBeanDefinition parseBeanDefinitionElement( Element ele, String beanName, BeanDefinition containingBean) { this.parseState.push(new BeanEntry(beanName)); String className = null; if (ele.hasAttribute(CLASS_ATTRIBUTE)) { className = ele.getAttribute(CLASS_ATTRIBUTE).trim(); } try { String parent = null; if (ele.hasAttribute(PARENT_ATTRIBUTE)) { parent = ele.getAttribute(PARENT_ATTRIBUTE); } AbstractBeanDefinition bd = createBeanDefinition(className, parent); parseBeanDefinitionAttributes(ele, beanName, containingBean, bd); bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT)); parseMetaElements(ele, bd); parseLookupOverrideSubElements(ele, bd.getMethodOverrides()); parseReplacedMethodSubElements(ele, bd.getMethodOverrides()); parseConstructorArgElements(ele, bd); parsePropertyElements(ele, bd); parseQualifierElements(ele, bd); bd.setResource(this.readerContext.getResource()); bd.setSource(extractSource(ele)); return bd; } catch (ClassNotFoundException ex) { error("Bean class [" + className + "] not found", ele, ex); } catch (NoClassDefFoundError err) { error("Class that bean class [" + className + "] depends on not found", ele, err); } catch (Throwable ex) { error("Unexpected failure during bean definition parsing", ele, ex); } finally { this.parseState.pop(); } return null; }
parseBeanDefinitionElement方法提供了解析元素并返回一个BeanDefinition对象,在方法内部通过
AbstractBeanDefinition bd = createBeanDefinition(className, parent);返回className对应的BeanDefinition对象.
/** * Create a bean definition for the given class name and parent name. * @param className the name of the bean class * @param parentName the name of the bean's parent bean * @return the newly created bean definition * @throws ClassNotFoundException if bean class resolution was attempted but failed */ protected AbstractBeanDefinition createBeanDefinition(String className, String parentName) throws ClassNotFoundException { return BeanDefinitionReaderUtils.createBeanDefinition( parentName, className, this.readerContext.getBeanClassLoader()); }
上一篇提到当处理为其它命名空间元素时会调用BeanDefinitionParserDelegate.parseCustomElement(Element 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)); }
上述代码首先根据元素查找命名空间,然后根据命名空间查找对应的NamespaceHandler,最后调用NamespaceHandler.parse方法来处理元素.
NamespaceHandler
NamespaceHandler接口定义了如何处理xml文档中个命名空间下元素,该处理过程通过parse方法来完成,下图为NamespaceHandler类结构图:部分实现:
从上图可以看出,每个命名空间对应一个NameSpaceHandler实例
如
<context:component-scan>对应ContextNameSpaceHandler来处理.
那么如何获取一个命名空间对应的NamespaceHandler对象呢?
我们返回XmlBeanDefinitionReader.registerBeanDefinitions源码:
/** * Register the bean definitions contained in the given DOM document. * Called by {@code loadBeanDefinitions}. * <p>Creates a new instance of the parser class and invokes * {@code registerBeanDefinitions} on it. * @param doc the DOM document * @param resource the resource descriptor (for context information) * @return the number of bean definitions found * @throws BeanDefinitionStoreException in case of parsing errors * @see #loadBeanDefinitions * @see #setDocumentReaderClass * @see BeanDefinitionDocumentReader#registerBeanDefinitions */ 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; }
在上面代码中通过
createReaderContext(resource)创建了一个XmlReaderContext,
createReaderContext(resource)源码如下:
/** * Create the {@link XmlReaderContext} to pass over to the document reader. */ public XmlReaderContext createReaderContext(Resource resource) { return new XmlReaderContext(resource, this.problemReporter, this.eventListener, this.sourceExtractor, this, getNamespaceHandlerResolver()); }
可以看到创建XmlReaderContext传递了一个NamespaceHandlerResolver对象,在接下来看getNamespaceHandlerResolver这个方法源码:
/** * Lazily create a default NamespaceHandlerResolver, if not set before. * @see #createDefaultNamespaceHandlerResolver() */ public NamespaceHandlerResolver getNamespaceHandlerResolver() { if (this.namespaceHandlerResolver == null) { this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver(); } return this.namespaceHandlerResolver; } /** * Create the default implementation of {@link NamespaceHandlerResolver} used if none is specified. * Default implementation returns an instance of {@link DefaultNamespaceHandlerResolver}. */ protected NamespaceHandlerResolver createDefaultNamespaceHandlerResolver() { return new DefaultNamespaceHandlerResolver(getResourceLoader().getClassLoader()); }
可以看到XmlReaderContext绑定的是一个DefaultNamespaceHandlerResolver类实例,默认就是使用这个类来关联命名空间和具体处理类.
在接下来返回BeanDefinitionParserDelegate的
parseCustomElement(Element ele, BeanDefinition containingBd)方法中:
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)); }
其中通过
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);这行代码来获取NamespaceHandler,而this.readerContext.getNamespaceHandlerResolver()获取的就是上面的DefaultNamespaceHandlerResolver,现在进入DefaultNamespaceHandlerResolver这个类
DefaultNamespaceHandlerResolver
查看源代码类中第一行有这么一段代码:public static final String DEFAULT_HANDLER_MAPPINGS_LOCATION = "META-INF/spring.handlers";
这句代码指定了默认handlers文件所在的位置,默认会去类路径下加载所有的META-INF目录下的spring.handlers,这个文件指定了命名空间和具体NamespaceHandler之间的关联,我们来看下spring-context jar文件下的”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
DefaultNamespaceHandlerResolver类会读取所有的spring.handler文件并加载保存到一个map中去,当调用resolve方法时会根据传递进来的namespace来关联对应NamespaceHandler,如上面提到的”
<context:component-scan>“元素对应命名空间为”http://www.springframework.org/schema/context“,该命名空间对应Namespacehandler为
org.springframework.context.config.ContextNamespaceHandler,所以会调用ContextNamespaceHandler.parse方法来处理
<context:component-scan>元素.
ContextNamespaceHandler
UML图源码:
/** * {@link org.springframework.beans.factory.xml.NamespaceHandler} * for the '{@code context}' namespace. * * @author Mark Fisher * @author Juergen Hoeller * @since 2.5 */ 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()); } }
可以看到ContextNamespaceHandler并没有提供具体parse()方法,那么spring是如何完成元素parse()过程呢.
spring并没有把所有的命名空间下的元素使用一个parse()方法来处理,(BeanDefinitionParserDelegate默认处理了所有的默认命名空间元素,不过该类很长了.) 在ContextNamespaceHandler的
init()方法中绑定了各个元素和对应处理类,该处理类实现了BeanDefinitionParser接口,该接口提供一个
BeanDefinition parse(Element element, ParserContext parserContext);方法,该方法提供了解析元素然后返回一个BeanDefinition对象.
NamespaceHandlerSupport的parse()方法:
/** * Parses the supplied {@link Element} by delegating to the {@link BeanDefinitionParser} that is * registered for that {@link Element}. */ @Override 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; }
在前面BeanDefinitionParserDelegate的
parseCustomElement(Element ele, BeanDefinition containingBd)方法的最后一行,在获取到命名空间处理类后调用命名空间处理类的parse()其实就是调用
NamespaceHandlerSupport.parse()方法.一般自定义NamespaceHandler扩展自NamespaceHandlerSupport类.而在NamespaceHandlerSupport.parse()方法中根据元素名称来查找之前在init()方法中注册的BeanDefinitionParser对象,然后调用该对象的parse()方法实际处理元素.那么init()方法是什么时候调用的呢?
我们返回NamespaceHandler接口查看init()方法定义:
/** * Invoked by the {@link DefaultBeanDefinitionDocumentReader} after * construction but before any custom elements are parsed. * @see NamespaceHandlerSupport#registerBeanDefinitionParser(String, BeanDefinitionParser) */ void init();
注释意思:在DefaultBeanDefinitionDocumentReader构造方法创建后以及自定义元素解析前调用,其实就是在解析前注册各个自定义BeanDefinitionParser, 解析入口点是在BeanDefinitionParserDelegate.parseCustomElement方法中:
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)); }
解析前调用了NamespaceHandlerResolver.resolve(String namespaceUri);这个方法来获取NamespaceHandler,现在进入resolve实现方法:DefaultNamespaceHandlerResolver.resolve(),在方法实现代码里面有下面2行代码:
NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass); namespaceHandler.init();
首先实例化NamespaceHandler对象,然后调用init()方法.
总结
BeanDefinitionParserDelegate类提供了解析spring配置文件功能,对于默认空间下的元素()在该类内部实现,对于其它命名空间下的元素可以通过绑定NamespaceHandler的方式来实现,针对每个命名空间下的元素提供不同BeanDefinitionParser来实现.相关文章推荐
- 在Eclipse中将Java项目打包为jar
- Spring Controller层记录日志配置二
- Spring中的IoC(控制反转)具体是什么东西
- JAVA中关于链表的操作和基本算法
- springMVC导入excel案例poi
- 基于Spring + Spring MVC + Mybatis 高性能web构建
- java反射实例
- java31:异常
- Spring切入点表达式
- struts2拦截器
- spring ioc原理 和 java反射 体会
- 关于struts2.3.24.1所必须的jar包
- Spring Ioc就是对java反射及读取xml文件的使用的封装
- Spring 中注入bean的properties配置文件位置问题解决
- 序列化java
- java-string与byte互换
- [Java基础]多线程之并发性以及解决方法
- 跟我学 Spring 3
- Struts2中Action访问Servlet API的三种方法
- Java的设计模式编程中迪米特法则的应用示例