Spring源码阅读系列之obtainFreshBeanFactory()实现

接着上一篇文章,本篇来接着谈谈 Spring 容器的创建及 bean 定义的解析。Debug 使用的代码和上一篇文章提供的保持一致

本篇文章由于代码调用流程较长,所以先给大家呈现一张调用流程图,方便大家更容易理解调用层次,废话不多说了,先上图一睹为快。

创建BeanFactory及注册BeanDefinition流程

  1. 直接来看下obtainFreshBeanFactory源码实现

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
    // Step1:创建BeanFactory并注册BeanDefinition定义
    // 通过这个方法可以看出,所有实现是在这个方法里面完成的
    refreshBeanFactory();
    // Step2:获取创建好的BeanFactory对象,并返回
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    if (logger.isDebugEnabled()) {
      logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
    }
    return beanFactory;
}

  1. refreshBeanFactory实现其实调用了其子类AbstractRefreshableApplicationContext.refreshBeanFactory()方法。

protected final void refreshBeanFactory() throws BeansException {
    // Step1:判断BeanFactory是否已经创建好,一般情况下第一次初始化Spring容器的时候if是不成立的,因为在此之前没有地方可以创建出BeanFactory对象
    if (hasBeanFactory()) {
      // 如果已经创建好,先将创建好的bean实例,相关缓存,BeanDefinition定义全部清空
      // Spring源码中大量运用了缓存,来提高Spring容器初始化的速度
      destroyBeans();
      // 然后在将BeanFactory置为null
      closeBeanFactory();
    }
    try {
      // Step2:创建一个DefaultListableBeanFactory对象,该对象用于存储Bean实例、相关缓存、BeanDefinition定义等等
      DefaultListableBeanFactory beanFactory = createBeanFactory();
      beanFactory.setSerializationId(getId());
      // Step3:设置是否允许BeanDefinition是否可以被覆盖
      // 设置循环引用
      customizeBeanFactory(beanFactory);
      // Step4:加载BeanDefinition定义,并将其注册到BeanFactory中
      loadBeanDefinitions(beanFactory);
      synchronized (this.beanFactoryMonitor) {
        this.beanFactory = beanFactory;
      }
    }
    catch (IOException ex) {
      throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
    }
}

  1. loadBeanDefinitions() 实现由AbstractRefreshableApplicationContext子类AbstractXmlApplicationContext.loadBeanDefinitions()方法实现的。

protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
    // Create a new XmlBeanDefinitionReader for the given BeanFactory.
    // Step1:创建一个XmlBeanDefinitionReader对象,用于读取xml文档
    XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

    // Configure the bean definition reader with this context's
    // resource loading environment.
    // Step2:设置环境变量
    beanDefinitionReader.setEnvironment(this.getEnvironment());
    beanDefinitionReader.setResourceLoader(this);
    beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

    // Allow a subclass to provide custom initialization of the reader,
    // then proceed with actually loading the bean definitions.
    // Step3:初始化BeanDefinitionReader,设置是否启用xml文档校验,默认情况下是需要校验的
    // 说白了就是是否要校验xml文档的合法性,避免错误解析
    initBeanDefinitionReader(beanDefinitionReader);
    // Step4:加载beanDefinitions
    loadBeanDefinitions(beanDefinitionReader);
}

  1. 初始化XmlBeanDefinitionReader对象完成后,开始读取配置文件资源。

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
    // Step1:首先读取ClassPathXmlApplicationContext里面存放点的Resource资源
    Resource[] configResources = getConfigResources();
    if (configResources != null) {
      // Step1.1:如果Resource资源不为空,则开始加载Resource资源
      reader.loadBeanDefinitions(configResources);
    }
    // Step2:读取AbstractRefreshableConfigApplicationContext指定配置文件资源
    String[] configLocations = getConfigLocations();
    if (configLocations != null) {
      // Step2.1:如果Location资源不为空,则开始加载Location资源
      reader.loadBeanDefinitions(configLocations);
    }
}

  1. 获取到配置文件路径后,开始遍历解析每个配置文件,不管是第一种资源还是第二种资源,最后都统一调用了XmlBeanDefinitionReader类的父类AbstractBeanDefinitionReader的loadBeanDefinitions()方法。

public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
    // Step1:获取资源读取器
    ResourceLoader resourceLoader = getResourceLoader();
    if (resourceLoader == null) {
      throw new BeanDefinitionStoreException(
          "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
    }
    // Step2:如果当前的资源读取器实现了ResourcePatternResolver接口
    if (resourceLoader instanceof ResourcePatternResolver) {
      // Resource pattern matching available.
      try {
        // Step2.1:获取resource资源
        Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
        // Step2.2:加载所有指定的resource资源里面定义的BeanDefinitions
        int loadCount = loadBeanDefinitions(resources);
        if (actualResources != null) {
          for (Resource resource : resources) {
            actualResources.add(resource);
          }
        }
        if (logger.isDebugEnabled()) {
          logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
        }
        return loadCount;
      }
      catch (IOException ex) {
        throw new BeanDefinitionStoreException(
            "Could not resolve bean definition resource pattern [" + location + "]", ex);
      }
    }
    else {
      // Can only load single resources by absolute URL.
      // Step3:如果没有实现ResourcePatternResolver接口,则根据当前的resourceLoader获取Resource资源
      Resource resource = resourceLoader.getResource(location);
      // Step3.1:加载指定的Resource资源定义的BeanDefinitions
      int loadCount = loadBeanDefinitions(resource);
      if (actualResources != null) {
        actualResources.add(resource);
      }
      if (logger.isDebugEnabled()) {
        logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
      }
      return loadCount;
    }
}

  1. 上面这段代码主要作用是使用不同的BeanDefinitionReader读取器读取不同类型的配置文件。拿我们提供的测试例子,最后其实调用了XmlBeanDefinitioneader.loadBeanDefinitions()方法。在这个方法里面我们看到一个比较特别的方法,叫做doLoadBeanDefinitions(),其实真正加载BeanDefinition是在这个方法里面实现的。

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
    Assert.notNull(encodedResource, "EncodedResource must not be null");
    if (logger.isInfoEnabled()) {
      logger.info("Loading XML bean definitions from " + encodedResource.getResource());
    }
    // Step1:获取当前的Resource对象,刚开始肯定是没有的
    Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
    if (currentResources == null) {
      // Step1.1:将当前Resource设置进去
      currentResources = new HashSet<>(4);
      this.resourcesCurrentlyBeingLoaded.set(currentResources);
    }
    // Step2:只有在第一次添加的时候才会返回true,也就意味着如果当前resource已经被设置过了,则出现了重复加载,直接抛出异常
    if (!currentResources.add(encodedResource)) {
      throw new BeanDefinitionStoreException(
          "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
    }
    try {
      // Step3:根据resource对象获取对应的InputStream
      InputStream inputStream = encodedResource.getResource().getInputStream();
      try {
        InputSource inputSource = new InputSource(inputStream);
        if (encodedResource.getEncoding() != null) {
          inputSource.setEncoding(encodedResource.getEncoding());
        }
        // Step4:拿到inputSource对象,开始正式读取配置文件,并创建配置文件中定义的BeanDefinition
        return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
      }
      finally {
        // Setp5:关闭输入流
        inputStream.close();
      }
    }
    catch (IOException ex) {
      throw new BeanDefinitionStoreException(
          "IOException parsing XML document from " + encodedResource.getResource(), ex);
    }
    finally {
      currentResources.remove(encodedResource);
      if (currentResources.isEmpty()) {
        this.resourcesCurrentlyBeingLoaded.remove();
      }
    }
}

  1. 拿到了InputSource之后,来看看配置文件到底是如何被解析的。

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
      throws BeanDefinitionStoreException {
    try {
      // Step1:加载Document对象,其实就是把xml文件解析成一个文档对象返回
      Document doc = doLoadDocument(inputSource, resource);
      // Step2:对Document对象遍历,读取不同的标签名,创建对应的BeanDefinition,并将创建出来的BeanDefinition注册到BeanFactory中去
      return registerBeanDefinitions(doc, resource);
    }
    // 此处为了方便阅读,将各种异常代码用.....代替
    ......
    catch (Throwable ex) {
      throw new BeanDefinitionStoreException(resource.getDescription(),
          "Unexpected exception parsing XML document from " + resource, ex);
    }
}

  1. 先来看看Document对象是怎么创建出来的。

protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
    return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
        getValidationModeForResource(resource), isNamespaceAware());
}

public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
      ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
    // Step1:创建Document工厂,这里面其实就是设置配置文件的文件头
    // 比如applicationContext.xml文件里面最开始,beans标签里面指定的 .xsd。了解即可
    DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
    if (logger.isDebugEnabled()) {
      logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
    }
    // Step2:利用Document工厂,创建对应的DocumentBuilder。
    DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
    // Step3:利用builder去解析输入流,从而构建出Document对象
    return builder.parse(inputSource);
}

// 这个方法就是文档的一些解析处理,了解一下
public Document parse(InputSource is) throws SAXException, IOException {
        if (is == null) {
            throw new IllegalArgumentException(
                DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN,
                "jaxp-null-input-source", null));
        }
        if (fSchemaValidator != null) {
            if (fSchemaValidationManager != null) {
                fSchemaValidationManager.reset();
                fUnparsedEntityHandler.reset();
            }
            resetSchemaValidator();
        }
        domParser.parse(is);
        Document doc = domParser.getDocument();
        domParser.dropDocumentReferences();
        return doc;
}

  1. 到现在,已经将 xml 文件解析成Document对象了,接下来看看根据Document对象如何创建BeanDefinition对象并注册到BeanFactory里面。

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
    BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
    int countBefore = getRegistry().getBeanDefinitionCount();
    // 这里我们只关注如何把Document对象注册到BeanFactory里面
    documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
    return getRegistry().getBeanDefinitionCount() - countBefore;
}

protected void doRegisterBeanDefinitions(Element root) {
    // 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 = createDelegate(getReaderContext(), root, parent);
    // Step1:以beans开始,是否可以取到 “profile”标签,我们没有在xml文件里面指定,所以这个地方可以忽略
    if (this.delegate.isDefaultNamespace(root)) {
      String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
      if (StringUtils.hasText(profileSpec)) {
        String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
            profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
        if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
          if (logger.isInfoEnabled()) {
            logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
                "] not matching: " + getReaderContext().getResource());
          }
          return;
        }
      }
    }
    // Step2:解析前工作,默认没有实现,留出来扩展
    preProcessXml(root);
    // Step3:开始解析Document
    parseBeanDefinitions(root, this.delegate);
    // Step4:解析后工作,默认没有实现,留出来扩展
    postProcessXml(root);

    this.delegate = parent;
}

  1. 重点来了,我们在 xml 文件里面配置的Bean标签就是在这个里面被转化成一个个BeanDefinition对象的。

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
    // Step1:如果是默认的命名空间,使用默认的解析逻辑
    if (delegate.isDefaultNamespace(root)) {
      // Step1.1:获取所有子节点
      NodeList nl = root.getChildNodes();
      for (int i = 0; i < nl.getLength(); i++) {
        Node node = nl.item(i);
        if (node instanceof Element) {
          Element ele = (Element) node;
          // Step1.2:如果子节点是 “import”、“alias”、“bean”、“beans”等等的
          // 则走默认的标签解析处理逻辑
          if (delegate.isDefaultNamespace(ele)) {
            parseDefaultElement(ele, delegate);
          }
          else {
            // Step1.3:如果不是上面指定的这些标签的话,则走特殊的标签解析逻辑
            delegate.parseCustomElement(ele);
          }
        }
      }
    }
    // Step2:不是默认的命名空间,则走自己的解析逻辑
    else {
      delegate.parseCustomElement(root);
    }
}

总结

从本篇文章不难看出,obtainFreshBeanFactory 就是创建了一个 BeanFactory 对象,然后把 xml 配置文件解析成 Document 对象,然后对 Document 对象遍历,创建出相应的 BeanDefinition 定义,并把 BeanDefinition 对象注册到 BeanFactory 里面。

到这里我们拿到了 xml 配置的 bean 标签。限于文章篇幅过长,我在下一篇文章中来继续解释,bean 标签是如何被解析的,比如 id,class,scope 等等属性。


欢迎关注我,共同学习

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值