Spring Bean加载流程分析(通过 XML 方式加载)( 四 )

该方法的主要功能是为了从指定的 Resource 文件中加载 BeanDefinition , 该方法的返回值是返回 BeanDefinition 的数量 。 此处 Spring 将传入的 Resource 对象封装成了一个 EncodedResource 对象 。 顾名思义我们知道该对象只是对 Resource 进行了封装 , 其中除了包含指定的 Resource 资源外 , 还包含了编码信息 , 进入 EncodedResource 源码看看 。
public EncodedResource(Resource resource) {this(resource, null, null);}public EncodedResource(Resource resource, @Nullable String encoding) {this(resource, encoding, null);}public EncodedResource(Resource resource, @Nullable Charset charset) {this(resource, null, charset);}/** * 私有构造方法 **/private EncodedResource(Resource resource, @Nullable String encoding, @Nullable Charset charset) {super();Assert.notNull(resource, "Resource must not be null");this.resource = resource;this.encoding = encoding;this.charset = charset;}复制代码可以看到 , Charset 和 encoding 是互斥的属性 。 显然我们这里仅仅只传入了 Resource 对象 , 那么默认的 encoding 了 charset 均为空 。
接下来看看 loadBeanDefinitions(EncodedResource e) 的具体实现 。
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {// .... 省略无效代码Set currentResources = this.resourcesCurrentlyBeingLoaded.get();if (currentResources == null) {currentResources = new HashSet<>(4);this.resourcesCurrentlyBeingLoaded.set(currentResources);}if (!currentResources.add(encodedResource)) {throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!");}try (InputStream inputStream = encodedResource.getResource().getInputStream()) {InputSource inputSource = new InputSource(inputStream);if (encodedResource.getEncoding() != null) {inputSource.setEncoding(encodedResource.getEncoding());}return doLoadBeanDefinitions(inputSource, encodedResource.getResource());}catch (IOException ex) {throw new BeanDefinitionStoreException("IOException parsing XML document from " + encodedResource.getResource(), ex);}finally {currentResources.remove(encodedResource);if (currentResources.isEmpty()) {this.resourcesCurrentlyBeingLoaded.remove();}}}复制代码resourcesCurrentlyBeingLoaded 是一个 ThreadLocal 对象 , 首先先从 resourcesCurrentlyBeingLoaded 中获取当前的 encodedResource , 如果获取出来的为空 , 则初始化一个 new HashSet 对象 , 将其放置到 resourcesCurrentlyBeingLoaded 对象中 。 接下来判断该对象是否在 resourcesCurrentlyBeingLoaded 中的 set 集合中已经存在 , 如果存在 , 则抛出 BeanDefinitionStoreException 异常 , 那么这个异常会在何时出现呢?我们可以尝试将 applicationContext.xml 进行改造一下 。
复制代码很显然这里面造成了一个循环引用 , 执行至此必然会抛出异常 。
Spring Bean加载流程分析(通过 XML 方式加载)文章插图
这里使用了一种巧妙的方式 , 通过 Set 集合不能有重复数据的特性来判断 applicationContext.xml 文件中的定义是否出现了循环导入 。