Spring源码解析 第二章、容器的基本实现

2.5 容器的基础XmlBeanFactory

BeanFactory bf = new XmlBeanFactory(new ClassPathResource("beanFactoryTest.xml"))

2.5.1 配置文件的封装

Spring的配置文件是通过ClassPathResource进行封装的。在Java中,将不同来源的资源抽象成URL,注册不同的handler,来处理不同资源的业务逻辑,handler有不同的前缀,这个前缀用来识别不同的url,这个前缀也称作为协议。比如:file、http、jar。Spring对内部得到的资源实现了自己的抽象结构,用来检查资源是否存在、可读等等。Resourse接口来封装底层资源。

public interface InputStreamSource {
    InputStream getInputStream() throws IOException;
}

public interface Resource extends InputStreamSource {
    boolean exists();        //存在性
    default boolean isReadable() {   //可读性
        return exists();
    }
    default boolean isOpen() {   //是否处于打开状态
        return false;
    }
    default boolean isFile() {
        return false;
    }

   //类型转换
    URL getURL() throws IOException;
    URI getURI() throws IOException;
    File getFile() throws IOException;
    default ReadableByteChannel readableChannel() throws IOException {
        return Channels.newChannel(getInputStream());
    }
    long contentLength() throws IOException;
    long lastModified() throws IOException;

//基于当前资源创建一个新的资源
    Resource createRelative(String relativePath) throws IOException;
    String getFilename();

//  打印信息
    String getDescription();
}

 对于不同的来源都有不同的实现,相关类图如下:

 

在日常开发中,我们可以使用Spring提供的相关的类:Resource

Resource re = new ClassPathResource(“beanFactoryTest.xml”);

InputStream in = re.getInputStream();

 有个Resource接口我们就可以对所有资源进行统一的处理了,其子类可以为我们进行不同资源的实例化。

当我们将配置文件封装成Resource类型之后 ,通过XmlBeanFactory类中的若干个构造函数进行初始化,eg:

 上面代码中真正实现加载资源的代码是  this.reader.loadBeanDefinitions(resource)。但是加载之前还有一个调用父类构造函数的初始化过程,跟踪到父类的初始化过程代码如下

ignoreDependencyInterface的主要功能是忽略给定接口的自动装配功能。eg:当A中有B属性,那么当获取A的Bean的时候如果B属性没有进行初始化,那么spring将会自动初始化B,这也是Spring一个重要的特性。但是某些情况下,B不会被初始化,其中一种情况是B实现了BeanNameAware接口。Spring是这样介绍的:自动装配时忽略给定的依赖接口。典型的应用是通过其他方式解析Application上下文注册依赖,类似于BeanFactory通过BeanFactoryAware进行注入或者ApplicationContext通过ApplicationContextAware注入。

2.5.2 加载Bean

刚刚我们说到真正实现加载资源的代码是  this.reader.loadBeanDefinitions(resource)。那么现在我们进入到这个方法中看看里边是什么鬼。

public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
	return loadBeanDefinitions(new EncodedResource(resource));
}

进入到代码中看到的是new EncodedResource,我们单纯在类名上来看的话,这个类可能是进行编码处理的业务逻辑

public Reader getReader() throws IOException {
    	if (this.charset != null) {
		return new InputStreamReader(this.resource.getInputStream(), this.charset);
	}
	else if (this.encoding != null) {
		return new InputStreamReader(this.resource.getInputStream(), this.encoding);
	}
	else {
		return new InputStreamReader(this.resource.getInputStream());
	}
}

这是其中的主要业务逻辑是构造了一个有编码的InputStreamReader,构造好这个对象之后再次转到loadBeanDefinitions方法中,然而此时的业务逻辑如下

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中获取已经封装的对应并从中获取InputStream
			InputStream inputStream = encodedResource.getResource().getInputStream();
			try {
				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 {
			Document doc = doLoadDocument(inputSource, resource);
			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);
		}
	}

代码很长,但是都是抛出的异常信息。

Document doc = doLoadDocument(inputSource, resource);   //加载XML文件,并得到对应的Document
            int count = registerBeanDefinitions(doc, resource);                //根据返回的Document注册Bean信息
            if (logger.isDebugEnabled()) {
                logger.debug("Loaded " + count + " bean definitions from " + resource);
            }
            return count;

spring5.1.2和之前的有点差别,它在doLoadDocument中直接获取对XML文件的验证模式。

return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler
,getValidationModeForResource(resource), isNamespaceAware());

上述的代码才是真正的加载XML文件,返回一个Document真正的业务逻辑。

 

.....

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值