返回介绍

2.5.2 加载 Bean

发布于 2025-04-22 22:09:07 字数 5439 浏览 0 评论 0 收藏

之前提到的在 XmlBeanFactory 构造函数中调用了 XmlBeanDefinitionReader 类型的 reader 属性提供的方法 this.reader.loadBeanDefinitions(resource),而这句代码则是整个资源加载的切入点,我们先来看看这个方法的时序图,如图 2-9 所示。

figure_0031_0027

图 2-9 loadBeanDefinitions 函数执行时序图

看到图 2-9 我们才知道什么叫山路十八弯,绕了这么半天还没有真正地切入正题,比如加载 XML 文档和解析注册 Bean,一直还在做准备工作。我们根据上面的时序图来分析一下这里究竟在准备什么?从上面的时序图中我们尝试梳理整个的处理过程如下。

( 1 )封装资源文件。当进入 XmlBeanDefinitionReader 后首先对参数 Resource 使用 EncodedResource 类进行封装。

(2)获取输入流。从 Resource 中获取对应的 InputStream 并构造 InputSource。

(3)通过构造的 InputSource 实例和 Resource 实例继续调用函数 doLoadBeanDefinitions。

我们来看一下 loadBeanDefinitions 函数具体的实现过程:

public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {

  return loadBeanDefinitions(new EncodedResource(resource));

}

那么 EncodedResource 的作用是什么呢?通过名称,我们可以大致推断这个类主要是用于对资源文件的编码进行处理的。其中的主要逻辑体现在 getReader() 方法中,当设置了编码属性的时候 Spring 会使用相应的编码作为输入流的编码。

public Reader getReader() throws IOException {

  if (this.encoding != null) {

   return new InputStreamReader(this.resource.getInputStream(), this.encoding);

 }

  else {

   return new InputStreamReader(this.resource.getInputStream());

 }

}

上面代码构造了一个有编码(encoding)的 InputStreamReader。当构造好 encodedResource 对象后,再次转入了可复用方法 loadBeanDefinitions(new EncodedResource(resource))。

这个方法内部才是真正的数据准备阶段,也就是时序图所描述的逻辑:

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());

 }

//通过属性来记录已经加载的资源

  Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();

  if (currentResources == null) {

   currentResources = new HashSet<EncodedResource>(4);

  this.resourcesCurrentlyBeingLoaded.set(currentResources);

 }

  if (!currentResources.add(encodedResource)) {

   throw new BeanDefinitionStoreException(

    "Detected cyclic loading of " + encodedResource + " - check your

    import definitions!");

 }

  try {

//从 encodedResource 中获取已经封装的 Resource 对象并再次从 Resource 中获取其中的 inputStream

  InputStream inputStream = encodedResource.getResource().getInputStream();

  try {

 //InputSource 这个类并不来自于 Spring,它的全路径是 org.xml.sax.InputSource

  InputSource inputSource = new InputSource(inputStream);

  if (encodedResource.getEncoding() != null) {

  inputSource.setEncoding(encodedResource.getEncoding());

 }

 //真正进入了逻辑核心部分

   returndoLoadBeanDefinitions(inputSource, encodedResource.getResource());

  }

   finally {

   //关闭输入流

   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();

  }

 }

}

我们再次整理一下数据准备阶段的逻辑,首先对传入的 resource 参数做封装,目的是考虑到 Resource 可能存在编码要求的情况,其次,通过 SAX 读取 XML 文件的方式来准备 InputSource 对象,最后将准备的数据通过参数传入真正的核心处理部分 doLoadBeanDefinitions(inputSource, encodedResource.getResource())。

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);

 }

}

在上面冗长的代码中假如不考虑异常类的代码,其实只做了三件事,这三件事的每一件都必不可少。

(1)获取对 XML 文件的验证模式。

(2)加载 XML 文件,并得到对应的 Document。

(3)根据返回的 Document 注册 Bean 信息。

这 3 个步骤支撑着整个 Spring 容器部分的实现基础,尤其是第 3 步对配置文件的解析,逻辑非常的复杂,那么我们先从获取 XML 文件的验证模式开始讲起。

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。