Spring读书笔记——bean加载

我们的日常开发几乎离不开Spring,他为我们的开发带来了很大的便捷,那么Spring框架是如何做到方便他人的呢。今天就来说说bean如何被加载加载。

我们在xml文件中写过太多类似这样的bean声明

<bean id="jsonParser" class="com.jackie.json.FastjsonJsonParser" />

那么这样的声明是如果被Spring读取并加载的呢?

DefaultListableBeanFactory

继承关系

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
      implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable 

上面是DefaultListableBeanFactory类源码的声明,集成了父类AbstractAutowireCapableBeanFactory以及实现了接口 ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable(且不管这些又长有难懂的父类和父接口都是干什么的)

上面是DefaultListableBeanFactory的所有依赖父类,可以看到最顶层使我们熟悉的Object类。

地位 DefaultListableBeanFactory类是整个bean加载的核心部分。后面我们要说到的XmlBeanFactory就是DefaultListableBeanFactory的子类。

XmlBeanFactory

XmlBeanFactory是DefaultListableBeanFactory的子类。其与DefaultListableBeanFactory类的不同之处通过看源码就会发现,XmlBeanFactory使用了自定义的XmlBeanDefinitionReader用于读取xml文件内容。

XmlBeanFactory的源码并不复杂

public class XmlBeanFactory extends DefaultListableBeanFactory {

   private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);


   /**
    * Create a new XmlBeanFactory with the given resource,
    * which must be parsable using DOM.
    * @param resource XML resource to load bean definitions from
    * @throws BeansException in case of loading or parsing errors
    */
   public XmlBeanFactory(Resource resource) throws BeansException {
      this(resource, null);
   }

   /**
    * Create a new XmlBeanFactory with the given input stream,
    * which must be parsable using DOM.
    * @param resource XML resource to load bean definitions from
    * @param parentBeanFactory parent bean factory
    * @throws BeansException in case of loading or parsing errors
    */
   public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
      super(parentBeanFactory);
      this.reader.loadBeanDefinitions(resource);
   }

}
  • 有两个构造函数
  • 两个构造函数都有参数Resource,我们在调用前可以通过各种实现比如new ClassPathResource("application-context.xml")的方式传值
  • Resource有很多实现比如 文件(FileSystemResource)、URL资源(UrlResource)、InputStream资源(InputStreamResource)、Byte数组(ByteArrayResource)等
  • 使用Resource是因为该接口抽象出了Spring用到的各种底层资源比如File、Url、Classpath等

bean加载过程

说到这里的bean加载就离不开我们上面提到的XmlBeanFactory类。 一切的加载XML以及解析bean的操作都要从XmlBeanFactory的 XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) 说起。

步骤分析

  • 理所当然,我们首先点击进入XmlBeanDefinitionReader类的loadBeanDefinitions方法。整个过程简单说就是: 1.首先对传入的Resouce进行封装,成为EncodeResource对象(之所以这么做,是考虑可以对于资源文件进行编码)。 Assert.notNull(encodedResource, "EncodedResource must not be null"); if (logger.isInfoEnabled()) { logger.info("Loading XML bean definitions from " + encodedResource.getResource()); }
  1. 得到Resource的输入流 InputStream inputStream = encodedResource.getResource().getInputStream();

3.调用doLoadBeanDefinitions方法

  • 进入doLoadBeanDefinitions方法,我们可以看到方法实现也很简单
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
        throws BeanDefinitionStoreException {
    try {
        int validationMode = getValidationModeForResource(resource);
        Document doc = this.documentLoader.loadDocument(
                inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());
        return registerBeanDefinitions(doc, resource);
    }
    catch (BeanDefinitionStoreException ex) {
        throw ex;
    }
    catch (SAXParseException ex) {
        throw new XmlBeanDefinitionStoreException(resource.getDescription(),
                "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
    }
    catch (SAXException ex) {
        throw new XmlBeanDefinitionStoreException(resource.getDescription(),
                "XML document from " + resource + " is invalid", ex);
    }
    catch (ParserConfigurationException ex) {
        throw new BeanDefinitionStoreException(resource.getDescription(),
                "Parser configuration exception parsing XML from " + resource, ex);
    }
    catch (IOException ex) {
        throw new BeanDefinitionStoreException(resource.getDescription(),
                "IOException parsing XML document from " + resource, ex);
    }
    catch (Throwable ex) {
        throw new BeanDefinitionStoreException(resource.getDescription(),
                "Unexpected exception parsing XML document from " + resource, ex);
    }
}

主要做了三件事 得到XML文件的验证模式

int validationMode = getValidationModeForResource(resource);

2.加载XML文件,得到对应的Document对象(这里可以发现是调用了this.documentLoader即DefaultDocmentLoader类)

Document doc = this.documentLoader.loadDocument(
      inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());

这里的解析成Document其实就是常规的SAX解析XML的操作。

3.根据得到的Document对象注册Bean

registerBeanDefinitions(doc, resource)

解析注册BeanDefinitions

上面的在得到Docment对象后,就是需要解析注册BeanDefinitions了。 进入registerBeanDefinitions方法我们可以看到其实现为

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
        BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
        documentReader.setEnvironment(this.getEnvironment());
        int countBefore = getRegistry().getBeanDefinitionCount();
        documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
        return getRegistry().getBeanDefinitionCount() - countBefore;
    }

我们继续深入,则进入到类DefaultBeanDefinitionDocumentReaderregisterBeanDefinitions方法的doRegisterBeanDefinitions实现。 在这个方法里实现了对于XML中的bean真正的解析

protected void doRegisterBeanDefinitions(Element root) {
   String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
   if (StringUtils.hasText(profileSpec)) {
      Assert.state(this.environment != null, "Environment must be set for evaluating profiles");
      String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
            profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
      if (!this.environment.acceptsProfiles(specifiedProfiles)) {
         return;
      }
   }

   // Any nested <beans> elements will cause recursion in this method. In
   // order to propagate and preserve <beans> default-* attributes correctly,
   // keep track of the current (parent) delegate, which may be null. Create
   // the new (child) delegate with a reference to the parent for fallback purposes,
   // then ultimately reset this.delegate back to its original (parent) reference.
   // this behavior emulates a stack of delegates without actually necessitating one.
   BeanDefinitionParserDelegate parent = this.delegate;
   this.delegate = createHelper(this.readerContext, root, parent);

   preProcessXml(root);
   parseBeanDefinitions(root, this.delegate);
   postProcessXml(root);

   this.delegate = parent;
}

追踪该方法中的每一个方法就是具体执行解析xml并加载bean的过程,有兴趣可以打断点调试一遍。

如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我最大的写作动力!如果您想持续关注我的文章,请扫描二维码,关注JackieZheng的微信公众号,我会将我的文章推送给您,并和您一起分享我日常阅读过的优质文章。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏大前端_Web

easyUI组件datagrid的二次封装

版权声明:本文为吴孔云博客原创文章,转载请注明出处并带上链接,谢谢。 https://blog.csdn.net/wkyseo/articl...

2633
来自专栏xingoo, 一个梦想做发明家的程序员

【插件开发】—— 9 编辑器代码分块着色-高亮显示!

前文回顾: 1 插件学习篇 2 简单的建立插件工程以及模型文件分析 3 利用扩展点,开发透视图 4 SWT编程须知 5 SWT简单控件的使用与布局搭...

2846
来自专栏Java成神之路

Java企业微信开发_07_JSSDK多图上传

 所有的JS接口只能在企业微信应用的可信域名下调用(包括子域名),可在企业微信的管理后台“我的应用”里设置应用可信域名。这个域名必须要通过ICP备案,不然jss...

1892
来自专栏Android 开发学习

JsBridge 源码分析

1733
来自专栏何俊林

LeakCanary的原理,你知道么?

2862
来自专栏琯琯博客

Yii2 学习笔记之助手类

2857
来自专栏Java架构沉思录

深入理解-Spring-之源码剖析IOC(一)

作为Java程序员,Spirng我们再熟悉不过,可以说比自己的女朋友还要亲密,每天都会和他在一起,然而我们真的了解spring吗?

1003
来自专栏大内老A

为ASP.NET MVC创建一个基于Unity的ControllerFactory

谈到IoC和ASP.NET的集成,很多人会先后想到Ninject,不过我们个人还是倾向于Unity。这篇文章简单地介绍如果创建基于Unity的Controlle...

1937
来自专栏LhWorld哥陪你聊算法

Hadoop源码篇--Client源码

今天起剖析源码,先从Client看起,因为Client在MapReduce的过程中承担了很多重要的角色。

1683
来自专栏Flutter入门

Weex是如何在Android客户端上跑起来的

Weex可以通过自己设计的DSL,书写.we文件或者.vue文件来开发界面,整个页面书写分成了3段,template、style、script,借鉴了成熟的MV...

4325

扫码关注云+社区

领取腾讯云代金券