导读:

XmlBeanFactory继承自DefaultListableBeanFactory,扩展了从xml文档中读取bean definition的能力。从本质上讲,XmlBeanFactory等同于DefaultListableBeanFactory+XmlBeanDefinitionReader ,如果有更好的需求,可以考虑使用DefaultListableBeanFactory+XmlBeanDefinitionReader方案,因为该方案可以从多个xml文件读取资源,并且在解析xml上具有更灵活的可配置性。

1 XmlBeanFactory的使用

典型构造方法:

  1. public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
  2. super(parentBeanFactory);
  3. this.reader.loadBeanDefinitions(resource);
  4. }

其中,

reader是XmlBeanDefinitionReader的实例,XmlBeanDefinitionReader继承自AbstractBeanDefinitionReader(http://www.cnblogs.com/davidwang456/p/4190428.html 已经介绍过)。

Resource 接口封装了各种可能的资源类型,也就是对使用者来说屏蔽了文件类型的不同。这样所有的资源都被可以通过 InputStream 这个类来获取,所以也屏蔽了资源的提供者。另外还有一个问题就是加载资源的问题,也就是资源的加载者要统一,从上图中可以看出这个任务是由 ResourceLoader 接口完成,他屏蔽了所有的资源加载者的差异,只需要实现这个接口就可以加载所有的资源,他的默认实现是 DefaultResourceLoader。

1.1 Resource 接口继承了 InputStreamSource 接口,这个接口中有个 getInputStream 方法,返回的是 InputStream 类。

  1. public interface InputStreamSource {
  2. InputStream getInputStream() throws IOException;
  3. }
  1. getInputStream设计希望每次请求都返回一个新的流。这种要求在一个场合非常重要,例如javamail api,它在创建多个邮件附件时需要多次读取到流,在这中情况下,它就要求每个getInputStream请求能返回一个新的流。
  2.  
  3. 1.2 抽象资源类AbstractResource继承自Source接口,实现了部分方法。
    exists方法检查一个文件或者输入量是否可以打开;
    isOpen 通常返回false;
      getUrlgetFile 抛出异常;
    toString返回描述信息。
  4.  
  5. 另外有一个有用的方法:lastModified()返回文件的最后修改时间。调用了File.lastModified() 返回此抽象路径名表示的文件最后一次被修改的时间。
  1. @Override
  2. public long lastModified() throws IOException {
  3. long lastModified = getFileForLastModifiedCheck().lastModified();
  4. if (lastModified == 0L) {
  5. throw new FileNotFoundException(getDescription() +
  6. " cannot be resolved in the file system for resolving its last-modified timestamp");
  7. }
  8. return lastModified;
  9. }

1.3 Resource的实现子类:

  1. AbstractResource的直接子类有:AbstractFileResolvingResource, ByteArrayResource,BeanDefinitionResource, DescriptiveResource, FileSystemResource, InputStreamResource, PathResource, VfsResource
    其中,AbstractFileResolvingResource的直接子类有:ClassPathResource, PortletContextResource, ServletContextResource, UrlResource
  1. 比较重要的实现类为实现了ContextResource接口的资源类,ContextResource接口继承了Resource接口,通过从一个封装的context中加载资源来扩展Resource接口的功能。例如:可以从ServletContextPortletContext,普通的classpth路径或者文件系统的相对路径(特别是没有指明前缀,因此适用于本地ResourceLoadercontext),其子类实现有:PortletContextResource, ServletContextResource,ClassPathContextResource,ClassRelativeContextResource,FileSystemContextResource.

1.4 资源的加载ResourceLoader,ResourceLoader是一个加载资源的策略接口,可以从classpth或者文件系统中,ApplicationContext需要加载资源功能,使用了其扩展类ResourcePatternReolver。DefaultResourceLoader是该接口的标准实现,适用于一个ApplicationContext外部,可以用在ResourceEditor中。在application允许过程中,使用特定的context子原因加载策略,可以将type资源和资源组通过string的形式设置bean的属性。

(来源:http://www.ibm.com/developerworks/cn/java/j-lo-spring-principle/)

2. Bean的解析与注册详细过程分析

    (来源:http://www.ibm.com/developerworks/cn/java/j-lo-spring-principle/)

首先, XmlBeanFactory构造函数this.reader.loadBeanDefinitions(resource);

  1. XmlBeanDefinitionReader加载bean定义:loadBeanDefinitions方法

2. XmlBeanDefinitionReader解析bean定义,并注册:doLoadBeanDefinitions方法

Document doc = doLoadDocument(inputSource, resource);
return registerBeanDefinitions(doc, resource);

3.1. 使用标准的JAXP配置的xml解析器从Resource中加载到Document。

  1. /**
  2. * Load the {@link Document} at the supplied {@link InputSource} using the standard JAXP-configured
  3. * XML parser.
  4. */
  5. @Override
  6. public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
  7. ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
  8.  
  9. DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
  10. if (logger.isDebugEnabled()) {
  11. logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
  12. }
  13. DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
  14. return builder.parse(inputSource);
  15. }

3.2.根据加载的Document 注册Bean definition。

  1. public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
  2. BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
  3. documentReader.setEnvironment(getEnvironment());
  4. int countBefore = getRegistry().getBeanDefinitionCount();
  5. documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
  6. return getRegistry().getBeanDefinitionCount() - countBefore;
  7. }

3.2.1 创建BeanDefinitionDocument的解析器BeanUtils.instantiateClass(this.documentReaderClass)

3.2.2 BeanDefinitionDocument解析器注册bean definition,从根节点<beans>开始

  1. /**
  2. * Register each bean definition within the given root {@code <beans/>} element.
  3. */
  4. protected void doRegisterBeanDefinitions(Element root) {
  5. String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
  6. if (StringUtils.hasText(profileSpec)) {
  7. String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
  8. profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
  9. if (!getEnvironment().acceptsProfiles(specifiedProfiles)) {
  10. return;
  11. }
  12. }
  13.  
  14. // Any nested <beans> elements will cause recursion in this method. In
  15. // order to propagate and preserve <beans> default-* attributes correctly,
  16. // keep track of the current (parent) delegate, which may be null. Create
  17. // the new (child) delegate with a reference to the parent for fallback purposes,
  18. // then ultimately reset this.delegate back to its original (parent) reference.
  19. // this behavior emulates a stack of delegates without actually necessitating one.
  20. BeanDefinitionParserDelegate parent = this.delegate;
  21. this.delegate = createDelegate(this.readerContext, root, parent);
  22.  
  23. preProcessXml(root);
  24. parseBeanDefinitions(root, this.delegate);
  25. postProcessXml(root);
  26.  
  27. this.delegate = parent;
  28. }
  1. /**
  2. * Parse the elements at the root level in the document:
  3. * "import", "alias", "bean".
  4. * @param root the DOM root element of the document
  5. */
  6. protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
  7. if (delegate.isDefaultNamespace(root)) {
  8. NodeList nl = root.getChildNodes();
  9. for (int i = 0; i < nl.getLength(); i++) {
  10. Node node = nl.item(i);
  11. if (node instanceof Element) {
  12. Element ele = (Element) node;
  13. if (delegate.isDefaultNamespace(ele)) {
  14. parseDefaultElement(ele, delegate);
  15. }
  16. else {
  17. delegate.parseCustomElement(ele);
  18. }
  19. }
  20. }
  21. }
  22. else {
  23. delegate.parseCustomElement(root);
  24. }
  25. }
  26.  
  27. private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
  28. if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
  29. importBeanDefinitionResource(ele);
  30. }
  31. else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
  32. processAliasRegistration(ele);
  33. }
  34. else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
  35. processBeanDefinition(ele, delegate);
  36. }
  37. else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
  38. // recurse
  39. doRegisterBeanDefinitions(ele);
  40. }
  41. }

4. documentReader.registerBeanDefinitions(doc, createReaderContext(resource));--->

  1. public XmlReaderContext createReaderContext(Resource resource) {
  2. return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
  3. this.sourceExtractor, this, getNamespaceHandlerResolver());
  4. }

5.调用:

  1. protected NamespaceHandlerResolver createDefaultNamespaceHandlerResolver() {
  2. return new DefaultNamespaceHandlerResolver(getResourceLoader().getClassLoader());
  3. }

6.BeanDefinitionReaderUtils.registerBeanDefinition

  1. /**
  2. * Process the given bean element, parsing the bean definition
  3. * and registering it with the registry.
  4. */
  5. protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
  6. BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
  7. if (bdHolder != null) {
  8. bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
  9. try {
  10. // Register the final decorated instance.
  11. BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
  12. }
  13. catch (BeanDefinitionStoreException ex) {
  14. getReaderContext().error("Failed to register bean definition with name '" +
  15. bdHolder.getBeanName() + "'", ele, ex);
  16. }
  17. // Send registration event.
  18. getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
  19. }
  20. }

7.处理给定的Bean元素,并解析到BeanDefinition。

  1. /**
  2. * Process the given bean element, parsing the bean definition
  3. * and registering it with the registry.
  4. */
  5. protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
  6. BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
  7. if (bdHolder != null) {
  8. bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
  9. try {
  10. // Register the final decorated instance.
  11. BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
  12. }
  13. catch (BeanDefinitionStoreException ex) {
  14. getReaderContext().error("Failed to register bean definition with name '" +
  15. bdHolder.getBeanName() + "'", ele, ex);
  16. }
  17. // Send registration event.
  18. getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
  19. }
  20. }

8.

  1. public static void registerBeanDefinition(
  2. BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
  3. throws BeanDefinitionStoreException {
  4.  
  5. // Register bean definition under primary name.
  6. String beanName = definitionHolder.getBeanName();
  7. registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
  8.  
  9. // Register aliases for bean name, if any.
  10. String[] aliases = definitionHolder.getAliases();
  11. if (aliases != null) {
  12. for (String aliase : aliases) {
  13. registry.registerAlias(beanName, aliase);
  14. }
  15. }
  16. }

9.将解析的Bean定义放入BeanDefinitionNames和BeanDefinitionMap保存已经aliasMap等

  1. @Override
  2. public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
  3. throws BeanDefinitionStoreException {
  4.  
  5. Assert.hasText(beanName, "Bean name must not be empty");
  6. Assert.notNull(beanDefinition, "BeanDefinition must not be null");
  7.  
  8. if (beanDefinition instanceof AbstractBeanDefinition) {
  9. try {
  10. ((AbstractBeanDefinition) beanDefinition).validate();
  11. }
  12. catch (BeanDefinitionValidationException ex) {
  13. throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
  14. "Validation of bean definition failed", ex);
  15. }
  16. }
  17.  
  18. BeanDefinition oldBeanDefinition;
  19.  
  20. synchronized (this.beanDefinitionMap) {
  21. oldBeanDefinition = this.beanDefinitionMap.get(beanName);
  22. if (oldBeanDefinition != null) {
  23. if (!this.allowBeanDefinitionOverriding) {
  24. throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
  25. "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
  26. "': There is already [" + oldBeanDefinition + "] bound.");
  27. }
  28. else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
  29. // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
  30. if (this.logger.isWarnEnabled()) {
  31. this.logger.warn("Overriding user-defined bean definition for bean '" + beanName +
  32. " with a framework-generated bean definition ': replacing [" +
  33. oldBeanDefinition + "] with [" + beanDefinition + "]");
  34. }
  35. }
  36. else {
  37. if (this.logger.isInfoEnabled()) {
  38. this.logger.info("Overriding bean definition for bean '" + beanName +
  39. "': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");
  40. }
  41. }
  42. }
  43. else {
  44. this.beanDefinitionNames.add(beanName);
  45. this.frozenBeanDefinitionNames = null;
  46. }
  47. this.beanDefinitionMap.put(beanName, beanDefinition);
  48. }
  49.  
  50. if (oldBeanDefinition != null || containsSingleton(beanName)) {
  51. resetBeanDefinition(beanName);
  52. }
  53. }

3. 一个非常重要的部分就是建立 Bean 对象实例之间的关系,这也是 Spring 框架的核心竞争力,何时、如何建立他们之间的关系请看下面的时序图:

来源:http://www.ibm.com/developerworks/cn/java/j-lo-spring-principle/

4. 小结:

  从spring 3.1版本后,xmlBeanFactory被表明为Deprecated. 推荐使用DefaultListableBeanFactory和XmlBeanDefinitionReader替换。

本文从xmlBeanFactory扩展到XmlBeanDefinitionReader,再到Resource接口。

一些情节还没有理顺,显得有些杂乱,后续会修改(写的很累,休整一下)。写这篇博客的时候,这句话一直在我脑边回想:

"写书就像冒险。一开始它是玩具和娱乐。然后她成了你的情妇。然后是你的主人。然后变成一个暴君。最后你终于认命的时候,它死了,然后给拖到外面游街。" ----温斯顿.丘吉尔

参考文献:

1. http://www.ibm.com/developerworks/cn/java/j-lo-spring-principle/

spring beans源码解读之--XmlBeanFactory的更多相关文章

  1. spring beans源码解读

    spring beans下面有如下源文件包: org.springframework.beans, 包含了操作java bean的接口和类.org.springframework.beans.anno ...

  2. spring beans源码解读之--总结篇

    spring beans下面有如下源文件包: org.springframework.beans, 包含了操作java bean的接口和类.org.springframework.beans.anno ...

  3. spring beans源码解读之 ioc容器之始祖--DefaultListableBeanFactory

    spring Ioc容器的实现,从根源上是beanfactory,但真正可以作为一个可以独立使用的ioc容器还是DefaultListableBeanFactory,因此可以这么说, DefaultL ...

  4. spring beans 源码解读

    从把spring下下来,导入到eclipse,花了几个小时的时间. 本来壮志雄心的说要,满满深入学习研读spring源码,现在看来还是不太现实,太难懂了,各种依赖,说明都是英文,整个串起来理解,深入研 ...

  5. spring beans源码解读之--Bean的注解(annotation)

    随着spring注解的引入,越来越多的开发者开始使用注解,这篇文章将对注解的机制进行串联式的讲解,不求深入透彻,但求串起spring beans注解的珍珠,展示给大家. 1. spring beans ...

  6. spring beans源码解读之--bean definiton解析器

    spring提供了有两种方式的bean definition解析器:PropertiesBeanDefinitionReader和XmLBeanDefinitionReader即属性文件格式的bean ...

  7. spring beans源码解读之--BeanFactory进化史

    BeanFactory是访问bean容器的根接口,它是一个bean容器的基本客户端视图. 先让我们看看beanfactory的前生后世吧! beanFactory有四个重要的子接口: SimpleJn ...

  8. spring beans源码解读之--Bean的定义及包装

    bean的定义,包装是java bean的基础.再怎么强调它的重要性都不为过,因此深入 了解这块的代码对以后的代码研究可以起到事半功倍的功效. 1. Bean的定义BeanDefinition 1.1 ...

  9. spring beans源码解读之--BeanFactory的注册

    beanFactory的继承关系如下图所示: (图片来源:http://www.myexception.cn/software-architecture-design/925888.html) 在上节 ...

随机推荐

  1. libSVM 参数选择

    libSVM 参数选择  [预测标签,准确率,决策值]=svmpredict(测试标签,测试数据,训练的模型);    原文参考:http://blog.csdn.net/carson2005/art ...

  2. jquery mobile backbone

    http://www.appliness.com/getting-started-with-html-mobile-application-development-using-jquery-mobil ...

  3. 【POJ 2987】Firing (最小割-最大权闭合子图)

    裁员 [问题描述] 在一个公司里,老板发现,手下的员工很多都不务正业,真正干事员工的没几个,于是老板决定大裁员,每开除一个人,同时要将其下属一并开除,如果该下属还有下属,照斩不误.给出每个人的贡献值和 ...

  4. mysql 连接url中useUnicode=true&characterEncoding=UTF-8 的作用

    添加的作用是:指定字符的编码.解码格式.            例如:mysql数据库用的是gbk编码,而项目数据库用的是utf-8编码.这时候如果添加了useUnicode=true&cha ...

  5. NAND FLASH的容量、特性、市场和应用

    NAND Flash的容量   一直到2006年,MLC芯片的容量每年都成倍数增长:由于NAND Flash的制程升级的挑战越来越大,所以NAND Flash之后的容量成倍增长所需要的时间也在不断增加 ...

  6. Sharepoint 2010 之 WebPart

    转:http://blog.csdn.net/bestbadgod/article/details/6895542 Sharepoint系列的博客,都是我个人自学过程中的点滴的积累,毕竟没做过C#及A ...

  7. 国内外最全的asp.net开源项目

    转自:http://www.cnblogs.com/fengtangquan/archive/2010/10/19/1855472.html 最近一些项目开始用到CMS系统,最开始是研究JAVA的,无 ...

  8. selenium webdriver(2)---页面对象定位

    webdriver的元素定位很灵活,提供了多种定位方式: Id LinkText PartialLinkText Name TagName Xpath ClassName CssSelector 这些 ...

  9. JavaScript---网络编程(2)-函数与数组

    上节,学完循环了~ 现在学Javascript的函数和数组. JavaScript语法 每一种语言都有自己的语法规则,JS语法与Java很像,所以学习起来比较容易.JS中也一样有变量,语句,函数,数组 ...

  10. JavaScript高级程序设计31.pdf

    其它方面的变化 DOM的其他部分在“DOM2级核心”中也发生了一些变化,这些变化与XML命名空间无关,而是确保API的可靠性及完整性 DocumentType类型的变化 DocumentType类型新 ...