Spring框架在开发中还是在面试中都占有很重要的地位,AOP,IOC两个概念也是Spring的核心。之前一直想学习一下IOC的实现原理,刚好趁着周六周天就学习了一下IOC的一个基础实现XmlBeanFactory,了解的从解析xml到注册到IOC容器的过程收货满满。写了这边博客与大家分享一下,刚好也是交流一下技术,如果文章中有不对的地方,大家可以交流探讨。
XmlBeanFactory继承关系图
代码分析
- XmlBeanFactory
// DefaultListableBeanFactory 是一个基础IOC容器
public class XmlBeanFactory extends DefaultListableBeanFactory {
// 从xml文件中对definition进行加载解析从bean并进行注册(由于容器本身没资源加载
// 和解析的功能所以通过XmlBeanDefinitionReader 对象来实现)
private final XmlBeanDefinitionReader reader;
// 构造器(资源信息)
public XmlBeanFactory(Resource resource) throws BeansException {
this(resource, (BeanFactory)null);
}
// 构造器(资源信息,父bean工厂)
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
// 初始化父类
super(parentBeanFactory);
// 创建读取,解析,注册xml中bean(当前对象引用)的对象
this.reader = new XmlBeanDefinitionReader(this);
// 调用loadBeanDefinitions方法初始化ioc容器对
// xml中的definition进行加载,解析,注册到ioc容器中
this.reader.loadBeanDefinitions(resource);
}
}
从代码中可以发现对这些XML文件定义信息的处理并不是由XmlBeanFactory直接完成的。在XmlBeanFactory中初始化了一个XmlBeanDefinitionReader对象,有了这个Reader对象,那些以XML方式定义的BeanDefinition就有了处理的地方。
构造XmlBeanFactory这个IOC容器时,需要指定BeanDefinition的信息来源,而这个信息来源需要封装成Spring中的Resource类类给出。Resource是Spring用来封装I/O操作的类。 比如,我们的BeanDefinition信息以XML文件形式存在的,那么可以使用像“ClassPathResource res= new ClassPathResource (bean.xml) ”;这样具体的ClassPathResource 来构造需要的Resource,然后将Resource作为构造参数传递给XmlBeanFactory构造函数。这样,Ioc容器就可以方便的定位到需要的BeanDefinition信息来对Bean完成容器的初始化和依赖注入。
- XmlBeanDefinitionReader
1.loadBeanDefinitions方法
// 加载 Bean (资源路径信息)
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
// EncodedResource(资源编码)包含的资源路径信息
return this.loadBeanDefinitions(new EncodedResource(resource));
}
2.loadBeanDefinitions方法
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
// 判断EncodedResource 是否为null
Assert.notNull(encodedResource, "EncodedResource must not be null");
if (this.logger.isTraceEnabled()) {
// 打印 加载xml中bean的资源路径信息
this.logger.trace("Loading XML bean definitions from " + encodedResource);
}
// 获取当前线程中正在加载的资源
Set<EncodedResource> currentResources = (Set)this.resourcesCurrentlyBeingLoaded.get();
// 判断当前将要加载是否已经存在
if (!currentResources.add(encodedResource)) {
// 抛出检测到“+encodedResource+”的循环加载 - 检查您的导入定义
throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!");
} else {
// 定义int类型的 var6
int var6;
try {
// 获取加载资源输入流
InputStream inputStream = encodedResource.getResource().getInputStream();
// 定义Throwable类型的var4
Throwable var4 = null;
try {
// 创建资源输入对象
InputSource inputSource = new InputSource(inputStream);
// 判断资源编码是否为null
if (encodedResource.getEncoding() != null) {
// 设置资源输入时的编码
inputSource.setEncoding(encodedResource.getEncoding());
}
// 调用doLoadBeanDefinitions(资源输入对象,资源信息)
var6 = this.doLoadBeanDefinitions(inputSource, encodedResource.getResource());
} catch (Throwable var24) {
var4 = var24;
throw var24;
} finally {
if (inputStream != null) {
if (var4 != null) {
try {
inputStream.close();
} catch (Throwable var23) {
var4.addSuppressed(var23);
}
} else {
inputStream.close();
}
}
}
} catch (IOException var26) {
throw new BeanDefinitionStoreException("IOException parsing XML document from " + encodedResource.getResource(), var26);
} finally {
currentResources.remove(encodedResource);
if (currentResources.isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
}
}
return var6;
}
}
3.doLoadBeanDefinitions方法
// 资源输入对象,资源信息
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {
try {
// 得到一个xml的Document 对象
Document doc = this.doLoadDocument(inputSource, resource);
// 注册 Bean(xml的Document 对象,资源信息)
int count = this.registerBeanDefinitions(doc, resource);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Loaded " + count + " bean definitions from " + resource);
}
return count;
} catch (BeanDefinitionStoreException var5) {
throw var5;
} catch (SAXParseException var6) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(), "Line " + var6.getLineNumber() + " in XML document from " + resource + " is invalid", var6);
} catch (SAXException var7) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(), "XML document from " + resource + " is invalid", var7);
} catch (ParserConfigurationException var8) {
throw new BeanDefinitionStoreException(resource.getDescription(), "Parser configuration exception parsing XML from " + resource, var8);
} catch (IOException var9) {
throw new BeanDefinitionStoreException(resource.getDescription(), "IOException parsing XML document from " + resource, var9);
} catch (Throwable var10) {
throw new BeanDefinitionStoreException(resource.getDescription(), "Unexpected exception parsing XML document from " + resource, var10);
}
}
4.doLoadDocument方法
// 资源输入对象,资源信息
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
// 资源输入对象,实体解析器,错误处理程序,资源验证,命名空间感知
return this.documentLoader.loadDocument(inputSource, this.getEntityResolver(), this.errorHandler, this.getValidationModeForResource(resource), this.isNamespaceAware());
}
5.registerBeanDefinitions方法
// 注册 Bean(xml的Document 对象,资源信息)
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
// 创建从Document中读取bean的阅读器
BeanDefinitionDocumentReader documentReader = this.createBeanDefinitionDocumentReader();
// DefaultListableBeanFactory 容器实现了BeanDefinitionRegistry接口
// 说明BeanDefinitionRegistry容器本身也具备了bean注册的功能
// 这里获取的是DefaultListableBeanFactory 容器中存放bean的Map的大小
int countBefore = this.getRegistry().getBeanDefinitionCount();
// 开始想IOC容器中进行bean的注册
// 底层实现是使用DefaultListableBeanFactory容器本身进行bean的注册
// DefaultListableanBeanFactory中有个方法
//public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
// 是进行bean注册的具体可以去看源码
documentReader.registerBeanDefinitions(doc, this.createReaderContext(resource));
// 获取Map大小
return this.getRegistry().getBeanDefinitionCount() - countBefore;
}
6.createBeanDefinitionDocumentReader方法
protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
return (BeanDefinitionDocumentReader)BeanUtils.instantiateClass(this.documentReaderClass);
}
- DocumentLoader(DocumentLoader是一个接口默认的实现只有一个DefaultDocumentLoader)
1.loadDocument方法
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver, ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
// 得到DocumentBuilderFactory工厂
DocumentBuilderFactory factory = this.createDocumentBuilderFactory(validationMode, namespaceAware);
if (logger.isTraceEnabled()) {
logger.trace("Using JAXP provider [" + factory.getClass().getName() + "]");
}
// 创建文档生成器(工厂,实体解析器,错误处理程序)
DocumentBuilder builder = this.createDocumentBuilder(factory, entityResolver, errorHandler);
// DocumentBuilder 的parse方法得到一个Xml的Documnet对象
return builder.parse(inputSource);
}
2.createDocumentBuilderFactory方法
// 资源验证,命名空间感知
protected DocumentBuilderFactory createDocumentBuilderFactory(int validationMode, boolean namespaceAware) throws ParserConfigurationException {
// 创建文档生成器工厂
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
// 设置工厂命名感知器
factory.setNamespaceAware(namespaceAware);
// 资源认证不为0
if (validationMode != 0) {
// 认证成功
factory.setValidating(true);
// 认证结果为3
if (validationMode == 3) {
// 设置工厂命名感知器为true
factory.setNamespaceAware(true);
// 工厂设置属性
try {
factory.setAttribute("http://java.sun.com/xml/jaxp/properties/schemaLanguage", "http://www.w3.org/2001/XMLSchema");
} catch (IllegalArgumentException var6) {
ParserConfigurationException pcex = new ParserConfigurationException("Unable to validate using XSD: Your JAXP provider [" + factory + "] does not support XML Schema. Are you running on Java 1.4 with Apache Crimson? Upgrade to Apache Xerces (or Java 1.5) for full XSD support.");
pcex.initCause(var6);
throw pcex;
}
}
}
// 返回工厂
return factory;
}
3.createDocumentBuilder方法
protected DocumentBuilder createDocumentBuilder(DocumentBuilderFactory factory, @Nullable EntityResolver entityResolver, @Nullable ErrorHandler errorHandler) throws ParserConfigurationException {
// 由工厂得到一个DocumentBuilder
DocumentBuilder docBuilder = factory.newDocumentBuilder();
// 判断实体解析器不为null
if (entityResolver != null) {
// 设置DocumentBuilder 的实体解析器
docBuilder.setEntityResolver(entityResolver);
}
// 错误处理器不为null
if (errorHandler != null) {
// DocumentBuilder 的错误处理器
docBuilder.setErrorHandler(errorHandler);
}
// 返回DocumentBuilder
return docBuilder;
}