这次稍微了解一下spring创建容器的其中一中方法——XmlBeanFactory(虽然这个方法已经过时了,从源码中可以看出,但是还是觉得有必要了解了解)。
以下是创建容器的代码:
BeanFactory bf = new XmlBeanFactory(new ClassPathResource("applicationContextIOC.xml"));
该行代码是Spring加载一个对象的代码,该代码执行的第一步是调用ClasspathResource的构造函数来构造Resource资源文件的实例对象,后续的资源处理就可以使用Resource提供的各种服务,当有了Resource文件之后可以进行XmlBeanFactory的初始化。
spring对其内部使用到的资源实现了自己的抽象结构,Resource接口来封装底层资源:
public interface Resource extends InputStreamSource {
boolean exists();//存在性
boolean isReadable();//可读性
boolean isOpen();//是否处于打开状态
URL getURL() throws IOException;//获取UrL
URI getURI() throws IOException; //获取UrI
File getFile() throws IOException;//获取File
long contentLength() throws IOException;//获取contentLength(内容长度)
long lastModified() throws IOException;//获取lastModified(最后修改)
Resource createRelative(String var1) throws IOException;//基于当前资源创建相对资源的方法,
String getFilename();//获取文件名(不带路径信息的文件名)
String getDescription()//在错误处理中打印信息;
}
在日常开发中,资源文件的加载也是经常用到的,可以直接使用spring提供的类来操作,比如希望加载文件时可以使用如下代码:
Resource resource = new ClassPathResource(“beans.xml”);
InputStream inputStream = resource.getInputStream();
有了Resource就可以对所有资源进行统一整理,实现方式也非常简单。
当通过Resource相关类完成对配置文件的封装后,文件的读取工作就交给XmlBeanDefinitionReader来处理了。
了解完spring中将配置文件封装为Resource类型的实例方法后,我们就可以开始正式探寻xmlBeanfactory的初始化过程了
public XmlBeanFactory(Resource resource) throws BeansException {
this(resource, null);
}
该构造方法内部再次就进行了调用内部构造函数:
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
super(parentBeanFactory);
this.reader.loadBeanDefinitions(resource);
上面的代码中this.reader.loadBeanDefinitions(resource);才是资源加载的真正实现,XmlBeanDefinitionReader加载数据就是在该方法中完成的。
加载Bean
调用了this.reader.loadBeanDefinitions(resource)之后的处理流程:
- 封装资源文件,进入XmlBeanDefinitionReader后首先对Resource使用EncodedResource类进行封装。
- 获取输入流,从Resource中获取对应的InputStream并构造InputSource。
- 通过构造InputSource实例和Resource实例继续调用doLoadBeanDefinition。
我们来看下loadBeanDefinitions的具体实现过程:
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return loadBeanDefinitions(new EncodedResource(resource));
}
EncodedResource的作用主要是用于对资源文件的编码进行处理的。其中的主要逻辑在getReader()中。
构造好了encodedResource对象之后,再次转入了可复用方法loadBeanDefinitions(new EncodedResource(resource))。
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Assert.notNull(encodedResource, "EncodedResource must not be null");//判断encodeResource是否为空
if (this.logger.isInfoEnabled()) {
this.logger.info("Loading XML bean definitions from " + encodedResource.getResource());
}
//通过属性来记录已经加载的资源
Set<EncodedResource> currentResources = (Set)this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
currentResources = new HashSet(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
if (!((Set)currentResources).add(encodedResource)) {
throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!");
} else {
int var5;
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());
}
//调用核心的方法
var5 = this.doLoadBeanDefinitions(inputSource, encodedResource.getResource());
} finally {
//关闭资源
inputStream.close();
}
} catch (IOException var15) {
throw new BeanDefinitionStoreException("IOException parsing XML document from " + encodedResource.getResource(), var15);
} finally {
((Set)currentResources).remove(encodedResource);
if (((Set)currentResources).isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
}
}
return var5;
}
}
/**
即上面的代码首先对传入的resource参数做了封装,是为了考虑Resource可能存在的编码要求的情况,然后通过SAX读取XML文件的方式准备了InputSource对象,最后将准备的数据通过参数传入到核心doLoadBeanDefinitions(InputSource,encodedResource.getResources())。
*/
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {
try {
Document doc = this.doLoadDocument(inputSource, resource);
return this.registerBeanDefinitions(doc, resource);
} catch (BeanDefinitionStoreException var4) {
throw var4;
} catch (SAXParseException var5) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(), "Line " + var5.getLineNumber() + " in XML document from " + resource + " is invalid", var5);
} catch (SAXException var6) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(), "XML document from " + resource + " is invalid", var6);
} catch (ParserConfigurationException var7) {
throw new BeanDefinitionStoreException(resource.getDescription(), "Parser configuration exception parsing XML from " + resource, var7);
} catch (IOException var8) {
throw new BeanDefinitionStoreException(resource.getDescription(), "IOException parsing XML document from " + resource, var8);
} catch (Throwable var9) {
throw new BeanDefinitionStoreException(resource.getDescription(), "Unexpected exception parsing XML document from " + resource, var9);
}
}
protected Document doLoadDocument (InputSource inputSource, Resource resource) throws Exception {
return this.documentLoader.loadDocument(inputSource, this.getEntityResolver(), this.errorHandler, this.getValidationModeForResource(resource), this.isNamespaceAware());
}
protected int getValidationModeForResource(Resource resource) {
int validationModeToUse = this.getValidationMode();
if (validationModeToUse != 1) {
return validationModeToUse;
} else {
int detectedMode = this.detectValidationMode(resource);
return detectedMode != 1 ? detectedMode : 3;
}
}
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = this.createBeanDefinitionDocumentReader();
int countBefore = this.getRegistry().getBeanDefinitionCount();
documentReader.registerBeanDefinitions(doc, this.createReaderContext(resource));
return this.getRegistry().getBeanDefinitionCount() - countBefore;
}
protected int doLoadBeanDefinitions (InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {
try {
Document doc = this.doLoadDocument(inputSource, resource);
return this.registerBeanDefinitions(doc, resource);
} catch (BeanDefinitionStoreException var4) {
throw var4;
} catch (SAXParseException var5) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(), "Line " + var5.getLineNumber() + " in XML document from " + resource + " is invalid", var5);
} catch (SAXException var6) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(), "XML document from " + resource + " is invalid", var6);
} catch (ParserConfigurationException var7) {
throw new BeanDefinitionStoreException(resource.getDescription(), "Parser configuration exception parsing XML from " + resource, var7);
} catch (IOException var8) {
throw new BeanDefinitionStoreException(resource.getDescription(), "IOException parsing XML document from " + resource, var8);
} catch (Throwable var9) {
throw new BeanDefinitionStoreException(resource.getDescription(), "Unexpected exception parsing XML document from " + resource, var9);
}
}
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
return this.documentLoader.loadDocument(inputSource, this.getEntityResolver(), this.errorHandler, this.getValidationModeForResource(resource), this.isNamespaceAware());
}
protected int getValidationModeForResource(Resource resource) {
int validationModeToUse = this.getValidationMode();
if (validationModeToUse != 1) {
return validationModeToUse;
} else {
int detectedMode = this.detectValidationMode(resource);
return detectedMode != 1 ? detectedMode : 3;
}
}
public int registerBeanDefinitions (Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = this.createBeanDefinitionDocumentReader();
int countBefore = this.getRegistry().getBeanDefinitionCount();
documentReader.registerBeanDefinitions(doc, this.createReaderContext(resource));
return this.getRegistry().getBeanDefinitionCount() - countBefore;
}
上面这一长串代码实际上只做了三件事:
- 获取对xml文件的验证模式
- 加载xml文件并得到对应的Document
- 根据Document注册Bean信息