BeanFactory bf = new XmlBeanFactory(new ClassPathResource("beanFactoryTest.xml"))
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注入。
刚刚我们说到真正实现加载资源的代码是 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 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真正的业务逻辑。
.....