Spring源码学习(三)---XmlBeanFactory执行流程

这里主要是使用org.springframework:spring-beans:5.2.0.RELEASE进行分析

1. 加载配置代码

        BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("beans.xml"));
        Hello hello = (Hello) beanFactory.getBean("hello");

XmlBeanFactory初始化的执行流程

在这里插入图片描述

调用了ClassPathResource的构造函数来构造Resource资源文件的实例对象,有了Resource后就可以进行XmlBeanFactory的初始化

2. 配置文件封装

Spring的配置文件通过ClassPathResource进行读取

Resource接口解析

Resource接口抽象了所有Spring内部使用到的底层资源:File、URL、Classpath等。首先,它定义了3个判断当前资源状态的方法:存在性(exists)、可读性(isReadable)、是否处于打开状态(isOpen)。另外,Resource接口还提供了不同资源到URL、URI、File类型的转换,以及获取lastModified属性、文件名(不带文件信息的文件名,getFilename())的方法。为了便于操作,Resource还提供了基于当前资源创建一个相对资源的方法:createRelative()。在错误处理中需要详细地打印出错的资源文件,因而Resource还提供了getDescription()方法用于在错误处理中的打印信息。
在这里插入图片描述

父接口InputStreamSource 解析

InputStreamSource封装任何能返回InputStream的类,比如File、Classpath下的资源和Byte Array等。它只有一个方法定义:getInputStream(),该方法返回一个新的InputStream对象。
在这里插入图片描述

对不同来源的资源文件都有相应的Resource实现:文件(FileSystemResource)、Classpath资源(ClasspathResource)、URL资源(URLResource)、InputStream资源(InputStreamResource)、Byte数组(ByteArrayResource)等
在这里插入图片描述

获取 inputStream 对象

      Resource resource = new ClassPathResource("beans.xml");
        InputStream inputStream = resource.getInputStream();
  1. ClassPathResource里面的getInputStream() 源码
	@Override
	public InputStream getInputStream() throws IOException {
		InputStream is;
		if (this.clazz != null) {
			is = this.clazz.getResourceAsStream(this.path);
		}
		else if (this.classLoader != null) {
			is = this.classLoader.getResourceAsStream(this.path);
		}
		else {
			is = ClassLoader.getSystemResourceAsStream(this.path);
		}
		if (is == null) {
			throw new FileNotFoundException(getDescription() + " cannot be opened because it does not exist");
		}
		return is;
	}
  1. FileSystemResource的实现直接使用FileInputStream对文件进行实例化。

FileSystemResource getInputStream()源码

	@Override
	public InputStream getInputStream() throws IOException {
		return Files.newInputStream(this.file.toPath());
	}

在这里插入图片描述
当Resource相关类完成了对配置文件进行封装后配置文件的读取工作交给XmlBeanDefinitionReader来处理,

XmlBeanFactory初始化有许多方法,Spring中提供了很多的构造函数,在这里分析的是使用Resource实例作为构造函数参数的方法,

在这里插入图片描述
this.reader.loadBeanDefinitions(resource)才是资源加载的真正实现

XmlBeanDefinitionReader加载数据前还有一个调用父类构造函数初始化的过程:super(parentBeanFactory),跟踪代码到父类
在这里插入图片描述
ignoreDependencyInterface的主要功能,是忽略给定接口的自动装配功能

this.reader.loadBeanDefinitions(resource); 分析

在这里插入图片描述
EncodeResource的作用这个类主要是用于对资源文件的编码进行处理

在这里插入图片描述

在这里插入图片描述

EncodeResource其中的主要逻辑体现在getReader()方法中

在这里插入图片描述

这个方法内部才是真正的数据准备阶段

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
		Assert.notNull(encodedResource, "EncodedResource must not be null");
		if (logger.isTraceEnabled()) {
			logger.trace("Loading XML bean definitions from " + encodedResource);
		}
  	// 通过属性来记录已经加载的资源
		Set<EncodedResource> 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 {
		      // 从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());
				}
				// 真正进入了逻辑核心部分
				return doLoadBeanDefinitions(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 {
		//加载XML文件,并得到对应的Document。
			Document doc = doLoadDocument(inputSource, resource);
			//根据返回的Document注册Bean信息。
			int count = registerBeanDefinitions(doc, resource);
			if (logger.isDebugEnabled()) {
				logger.debug("Loaded " + count + " bean definitions from " + resource);
			}
			return count;
		}
		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);
		}
	}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值