hdml指的是什么接口
284
2023-05-16
详解Spring简单容器中的Bean基本加载过程
本篇将对定义在 XMl 文件中的 bean,从静态的的定义到变成可以使用的对象的过程,即 bean 的加载和获取的过程进行一个整体的了解,不去深究,点到为止,只求对 Spring IOC 的实现过程有一个整体的感知,具体实现细节留到后面用针对性的篇章进行讲解。
首先我们来引入一个 Spring 入门使用示例,假设我们现在定义了一个类 org.zhenchao.framework.MyBean ,我们希望利用 Spring 来管理类对象,这里我们利用 Spring 经典的 XMl 配置文件形式进行配置:
xmlns:xsi="http://w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://springframework.org/schema/beans http://springframework.org/schema/beans/spring-beans.xsd"> 我们将上面的配置文件命名为 spring-core.xml,则对象的最原始的获取和使用示例如下: // 1. 定义资源 Resource resource = new ClassPathResource("spring-core.xml"); // 2. 利用XmlBeanFactory解析并注册bean定义 XmlBeanFactory beanFactoryhttp:// = new XmlBeanFactory(resource); // 3. 从IOC容器加载获取bean MyBean myBean = (MyBean) beanFactory.getBean("myBean"); // 4. 使用bean myBean.sayHello(); 上面 demo 虽然简单,但麻雀虽小,五脏俱全,完整的让 Spring 执行了一遍配置文件加载,并获取 bean 的过程。虽然从 Spring 3.1 开始 XmlBeanFactory 已经被置为 Deprecated ,但是 Spring 并没有定义出更加高级的基于 XML 加载 bean 的 BeanFactory,而是推荐采用更加原生的方式,即组合使用 DefaultListableBeanFactory 和 XmlBeanDefinitionReader 来完成上诉过程: Resource resource = new ClassPathResource("swOIbWGqCjwpring-core.xml"); DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory); reader.loadBeanDefinitions(resource); MyBean myBean = (MyBean) beanFactory.getBean("myBean"); myBean.sayHello(); 后面的分析你将会看到 XmlBeanFactory 实际上是对 DefaultListableBeanFactory 和 XmlBeanDefinitionReader 组合使用方式的封装,所以这里我们仍然将继续分析基于 XmlBeanFactory 加载 bean 的过程。 一. Bean的解析和注册 Bean的加载过程,主要是对配置文件的解析,并注册 bean 的过程,上图是加载过程的时序图,当我们 new XmlBeanFactory(resource) 的时候,已经完成将配置文件包装成了 Spring 定义的资源,并触发解析和注册。 new XmlBeanFactory(resource) 调用的是下面的构造方法: publicXmlBeanFactory(Resource resource)throwsBeansException{ this(resource, null); } 这个构造方法本质上还是继续调用了: publicXmlBeanFactory(Resource resource, BeanFactory parentBeanFactory)throwsBeansException{ super(parentBeanFactory); // 加载xml资源 this.reader.loadBeanDefinitions(resource); } 在这个构造方法里面先是调用了父类构造函数,即 org.springframework.beans.factory.support.DefaultListableBeanFactory 类,这是一个非常核心的类,它包含了基本 IOC 容器所具有的重要功能,是一个 IOC 容器的基本实现。然后是调用了 this.reader.loadBeanDefinitions(resource) ,从这里开始加载配置文件。 Spring 在设计采用了许多程序设计的基本原则,比如迪米特法则、开闭原则,以及接口隔离原则等等,这样的设计为后续的扩展提供了灵活性,也增强了模块的复用性,这也是我看 Spring 源码的动力之一,希望通过阅读学习的过程来提升自己接口设计的能力。Spring 使用了专门的资源加载器对资源进行加载,这里的 reader 就是 org.springframework.beans.factory.xml.XmlBeanDefinitionReader 对象,专门用来加载基于 XML 文件配置的 bean。这里的加载过程为: 利用 EncodedResource 二次包装资源文件 获取资源输入流,并构造 InputSource 对象 获取 XML 文件的实体解析器和验证模式 加载 XML 文件,获取对应的 Document 对象 由 Document 对象解析并注册 bean 1.利用 EncodedResource 二次包装资源文件 采用 org.springframework.core.io.support.EncodedResource 对resource 进行二次封装. 2.获取资源输入流,并构造 InputSource 对象 对资源进行编码封装之后,开始真正进入 this.loadBeanDefinitions(new EncodedResource(resource)) 的过程,该方法源码如下: publicintloadBeanDefinitions(EncodedResource encodedResource)throwsBeanDefinitionStoreException{ Assert.notNull(encodedResource, "EncodedResource must not be null"); if (logger.isInfoEnabled()) { logger.info("Loading XML bean definitions from " + encodedResource.getResource()); } // 标记正在加载的资源,防止循环引用 Set if (currentResources == null) { currentResources = new HashSet this.resourcesCurrentlyBeingLoaded.set(currentResources); } if (!currentResources.add(encodedResource)) { throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!"); } try { // 获取资源的输入流 InputStream inputStream = encodedResource.getResource().getInputStream(); try { // 构造InputSource对象 InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } // 真正开始从XML文件中加载Bean定义 return this.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(); } } } 需要知晓的是 org.xml.sax.InputSource 不是 Spring 中定义的类,这个类来自 jdk,是 java 对 XML 实体提供的原生支持。这个方法主要还是做了一些准备工作,按照 Spring 方法的命名相关,真正干活的方法一般都是以 “do” 开头的,这里的 this.doLoadBeanDefinitions(inputSource, encodedResource.getResource()) 就是真正开始加载 XMl 的入口,该方法源码如下: protectedintdoLoadBeanDefinitions(InputSource inputSource, Resource resource)throwsBeanDefinitionStoreException{ try { // 1. 加载xml文件,获取到对应的Document(包含获取xml文件的实体解析器和验证模式) Document doc = this.doLoadDocument(inputSource, resource); // 2. 解析Document对象,并注册bean return this.registerBeanDefinitions(doc, resource); } catch (BeanDefinitionStoreException ex) { // 这里是连环catch,省略 } } 方面里面的逻辑还是很清晰的,第一步获取 org.w3c.dom.Document 对象,第二步由该对象解析得到 BeanDefinition 对象,并注册到 IOC 容器中。 3.获取 XML 文件的实体解析器和验证模式 this.doLoadDocument(inputSource, resource) 包含了获取实体解析器、验证模式,以及 Document 对象的逻辑,源码如下: protectedDocumentdoLoadDocument(InputSource inputSource, Resource resource)throwsException{ return this.documentLoader.loadDocument( inputSource, this.getEntityResolver(), // 获取实体解析器 this.errorHandler, this.getValidationModeForResource(resource), // 获取验证模式 this.isNamespaceAware()); } XML 是半结构化数据,XML 的验证模式用于保证结构的正确性,常见的验证模式有 DTD 和 XSD 两种,获取验证模式的源码如下: protectedintgetValidationModeForResource(Resource resource){ int validationModeToUse = this.getValidationMode(); if (validationModeToUse != VALIDATION_AUTO) { // 手动指定了验证模式 return validationModeToUse; } // 没有指定验证模式,则自动检测 int detectedMode = this.detectValidationMode(resource); if (detectedMode != VALIDATION_AUTO) { return detectedMode; } // 检测验证模式失败,默认采用XSD验证 return VALIDATION_XSD; } 上面源码描述了获取验证模式的执行流程,如果没有手动指定,那么 Spring 会去自动检测。对于 XML 文件的解析,SAX 首先会读取 XML 文件头声明,以获取对应验证文件地址,并下载对应的文件,如果网络不正常,则会影响下载过程,这个时候可以通过注册一个实体解析来实现寻找验证文件的过程。 4.加载 XML 文件,获取对应的 Document 对象 获取对应的验证模式和解析器,解析去就可以加载 Document 对象了,这里本质上调用的是 org.springframework.beans.factory.xml.DefaultDocumentLoader 的 loadDocument() 方法,源码如下: publicDocumentloadDocument(InputSource inputSource, EntityResolver entityResolver, ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception { DocumentBuilderFactory factory = this.createDocumentBuilderFactory(validationMode, namespaceAware); if (logger.isDebugEnabled()) { logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]"); } DocumentBuilder builder = this.createDocumentBuilder(factory, entityResolver, errorHandler); return builder.parse(inputSource); } 整个过程类似于我们平常解析 XML 文件的流程。 5.由 Document 对象解析并注册 bean 完成了对 XML 文件的到 Document 对象的解析,我们终于可以解析 Document 对象,并注册 bean 了,这一过程发生在 this.registerBeanDefinitions(doc, resource) 中,源码如下: publicintregisterBeanDefinitions(Document doc, Resource resource)throwsBeanDefinitionStoreException{ // 使用DefaultBeanDefinitionDocumentReader构造 BeanDefinitionDocumentReader documentReader = this.createBeanDefinitionDocumentReader(); // 记录之前已经注册的BeanDefinition个数 int countBefore = this.getRegistry().getBeanDefinitionCount(); // 加载并注册bean documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); // 返回本次加载的bean的数量 return getRegistry().getBeanDefinitionCount() - countBefore; } 这里方法的作用是创建对应的 BeanDefinitionDocumentReader,并计算返回了过程中新注册的 bean 的数量,而具体的注册过程,则是由 BeanDefinitionDocumentReader 来完成的,具体的实现位于子类 DefaultBeanDefinitionDocumentReader 中: publicvoidregisterBeanDefinitions(Document doc, XmlReaderContext readerContext){ this.readerContext = readerContext; logger.debug("Loading bean definitions"); // 获取文档的root结点 Element root = doc.getDocumentElement(); this.doRegisterBeanDefinitions(root); } 还是按照 Spring 命名习惯,doRegisterBeanDefinitions 才是真正干活的地方,这也是真正开始解析配置的核心所在: protectedvoiddoRegisterBeanDefinitions(Element root){ BeanDefinitionParserDelegate parent = this.delegate; this.delegate = this.createDelegate(getReaderContext(), root, parent); if (this.delegate.isDefaultNamespace(root)) { // 处理profile标签(其作用类比pom.xml中的profile) String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); if (StringUtils.hasText(profileSpec)) { String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); if (!this.getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) { if (logger.isInfoEnabled()) { logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + getReaderContext().getResource()); } return; } } } // 解析预处理,留给子类实现 this.preProcessXml(root); // 解析并注册BeanDefinition this.parseBeanDefinitions(root, this.delegate); // 解析后处理,留给子类实现 this.postProcessXml(root); this.delegate = parent; } 方法中显示处理了 标签,这个属性在 Spring 中不是很常用,不过在 maven 的 pom.xml 中则很常见,意义也是相同的,就是配置多套环境,从而在部署的时候可以根据具体环境来选择使用哪一套配置。方法中会先去检测是否配置了 profile,如果配置了就需要从上下文环境中确认当前激活了哪一套 profile。 方法在解析并注册 BeanDefinition 前后各设置一个模板方法,留给子类扩展实现,而在 this.parseBeanDefinitions(root, this.delegate) 中执行解析和注册逻辑: protectedvoidparseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate){ if (delegate.isDefaultNamespace(root)) { // 解析默认标签 NodeList nl = root.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (node instanceof Element) { Element ele = (Element) node; if (delegate.isDefaultNamespace(ele)) { // 解析默认标签 this.parseDefaultElement(ele, delegate); } else { // 解析自定义标签 delegate.parseCustomElement(ele); } } } } else { // 解析自定义标签 delegate.parseCustomElement(root); } } 方法中判断当前标签是默认标签还是自定义标签,并按照不同的策略去解析,这是一个复杂的过程,后面用文章进行针对性讲解,这里不在往下细究。 到这里我们已经完成了静态配置到动态 BeanDefinition 的解析,这个时候 bean 的定义已经处于内存中,解析去将是探究如何获取并使用 bean 的过程。 二. Bean的获取 在完成了 Bean 的加载过程之后,我们可以调用 beanFactory.getBean("myBean") 方法来获取目标对象,这里本质上调用的是 org.springframework.beans.factory.support.AbstractBeanFactory 的 getBean() 方法,源码如下: publicObjectgetBean(String name)throwsBeansException{ return this.doGetBean(name, null, null, false); } 这里调用 this.doGetBean(name, null, null, false) 来实现具体逻辑,也符合我们的预期,该方法可以看做是获取 bean 的整体框架,一个函数完成了整个过程的模块调度,还是挺复杂的: protected final String name, final Class /* * 转化对应的beanName * * 传入的参数可能是alias,也可能是FactoryBean,所以需要进行解析,主要包含以下内容: * 1. 去除FactoryBean的修饰符“&” * 2. 取指定alias对应的最终的name */ final String beanName = this.transformedBeanName(name); Object bean; /* * 检查缓存或者实例工厂中是否有对应的实例 * * 为什么会一开始就进行检查? * 因为在创建单例bean的时候会存在依赖注入的情况,而在创建依赖的时候为了避免循环依赖 * Spring创建bean的原则是不等bean创建完成就会将创建bean的ObjectFactory提前曝光,即将对应的ObjectFactory加入到缓存 * 一旦下一个bean创建需要依赖上一个bean,则直接使用ObjectFactory */ Object sharedInstance = this.getSingleton(beanName); // 获取单例 if (sharedInstance != null && args == null) { // 实例已经存在 if (logger.isDebugEnabled()) { if (this.isSingletonCurrentlyInCreation(beanName)) { logger.debug("Returning eagerly cached instance of singleton bean '" + beanName + "' that is not fully initialized yet - a consequence of a circular reference"); } else { logger.debug("Returning cached instance of singleton bean '" + beanName + "'"); } } // 返回对应的实例 bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, null); } else { // 单例实例不存在 if (this.isPrototypeCurrentlyInCreation(beanName)) { /* * 只有在单例模式下才会尝试解决循环依赖问题 * 对于原型模式,如果存在循环依赖,也就是满足this.isPrototypeCurrentlyInCreation(beanName),抛出异常 */ throw new BeanCurrentlyInCreationException(beanName); } BeanFactory parentBeanFactory = this.getParentBeanFactory(); if (parentBeanFactory != null && !this.containsBeanDefinition(beanName)) { // 如果在beanDefinitionMap中(即所有已经加载的类中)不包含目标bean,则尝试从parentBeanFactory中检测 String nameToLookup = this.originalBeanName(name); if (args != null) { // 递归到BeanFactory中寻找 return (T) parentBeanFactory.getBean(nameToLookup, args); } else { return parentBeanFactory.getBean(nameToLookup, requiredType); } } // 如果不仅仅是做类型检查,则创建bean if (!typeCheckOnly) { this.markBeanAsCreated(beanName); } try { /* * 将存储XML配置的GenericBeanDefinition转换成RootBeanDefinition * 如果指定了beanName是子bean的话,同时会合并父类的相关属性 */ final RootBeanDefinition mbd = this.getMergedLocalBeanDefinition(beanName); this.checkMergedBeanDefinition(mbd, beanName, args); // 获取当前bean依赖的bean String[] dependsOn = mbd.getDependsOn(); if (dependsOn != null) { // 存在依赖,递归实例化依赖的bean for (String dep : dependsOn) { if (this.isDependent(beanName, dep)) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'"); } // 缓存依赖调用 this.registerDependentBean(dep, beanName); this.getBean(dep); } } // 实例化依赖的bean后,实例化mbd自身 if (mbd.isSingleton()) { // scope == singleton sharedInstance = this.getSingleton(beanName, new ObjectFactory @Override publicObjectgetObject()throwsBeansException{ try { return createBean(beanName, mbd, args); } catch (BeansException ex) { // Explicitly remove instance from singleton cache: It might have been put there // eagerly by the creation process, to allow for circular reference resolution. // Also remove any beans that received a temporary reference to the bean. destroySingleton(beanName); throw ex; } } }); bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } else if (mbd.isPrototype()) { // scope == prototype Object prototypeInstance; try { this.beforePrototypeCreation(beanName); prototypeInstance = this.createBean(beanName, mbd, args); } finally { this.afterPrototypeCreation(beanName); } // 返回对应的实例 bean = this.getObjectForBeanInstance(prototypeInstance, name, beanName, mbd); } else { // 其它scope String scopeName = mbd.getScope(); final Scope scope = this.scopes.get(scopeName); if (scope == null) { throw new IllegalStateException("No Scope registered for scope namehttp:// '" + scopeName + "'"); } try { Object scopedInstance = scope.get(beanName, new ObjectFactory @Override publicObjectgetObject()throwsBeansException{ beforePrototypeCreation(beanName); try { return createBean(beanName, mbd, args); } finally { afterPrototypeCreation(beanName); } } }); // 返回对应的实例 bean = this.getObjectForBeanInstance(scopedInstance, name, beanName, mbd); } catch (IllegalStateException ex) { throw new BeanCreationException(beanName, "Scope '" + scopeName + "' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton", ex); } } } catch (BeansException ex) { cleanupAfterBeanCreationFailure(beanName); throw ex; } } // 检查需要的类型是否符合bean的实际类型,对应getBean时指定的requireType if (requiredType != null && bean != null && !requiredType.isAssignableFrom(bean.getClass())) { try { return this.getTypeConverter().convertIfNecessary(bean, requiredType); } catch (TypeMismatchException ex) { if (logger.isDebugEnabled()) { logger.debug("Failed to convert bean '" + name + "' to required type '" + ClassUtils.getQualifiedName(requiredType) + "'", ex); } throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass()); } } return (T) bean; }
xmlns:xsi="http://w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://springframework.org/schema/beans http://springframework.org/schema/beans/spring-beans.xsd">
我们将上面的配置文件命名为 spring-core.xml,则对象的最原始的获取和使用示例如下:
// 1. 定义资源
Resource resource = new ClassPathResource("spring-core.xml");
// 2. 利用XmlBeanFactory解析并注册bean定义
XmlBeanFactory beanFactoryhttp:// = new XmlBeanFactory(resource);
// 3. 从IOC容器加载获取bean
MyBean myBean = (MyBean) beanFactory.getBean("myBean");
// 4. 使用bean
myBean.sayHello();
上面 demo 虽然简单,但麻雀虽小,五脏俱全,完整的让 Spring 执行了一遍配置文件加载,并获取 bean 的过程。虽然从 Spring 3.1 开始 XmlBeanFactory 已经被置为 Deprecated ,但是 Spring 并没有定义出更加高级的基于 XML 加载 bean 的 BeanFactory,而是推荐采用更加原生的方式,即组合使用 DefaultListableBeanFactory 和 XmlBeanDefinitionReader 来完成上诉过程:
Resource resource = new ClassPathResource("swOIbWGqCjwpring-core.xml");
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
reader.loadBeanDefinitions(resource);
MyBean myBean = (MyBean) beanFactory.getBean("myBean");
myBean.sayHello();
后面的分析你将会看到 XmlBeanFactory 实际上是对 DefaultListableBeanFactory 和 XmlBeanDefinitionReader 组合使用方式的封装,所以这里我们仍然将继续分析基于 XmlBeanFactory 加载 bean 的过程。
一. Bean的解析和注册
Bean的加载过程,主要是对配置文件的解析,并注册 bean 的过程,上图是加载过程的时序图,当我们 new XmlBeanFactory(resource) 的时候,已经完成将配置文件包装成了 Spring 定义的资源,并触发解析和注册。 new XmlBeanFactory(resource) 调用的是下面的构造方法:
publicXmlBeanFactory(Resource resource)throwsBeansException{
this(resource, null);
}
这个构造方法本质上还是继续调用了:
publicXmlBeanFactory(Resource resource, BeanFactory parentBeanFactory)throwsBeansException{
super(parentBeanFactory);
// 加载xml资源
this.reader.loadBeanDefinitions(resource);
}
在这个构造方法里面先是调用了父类构造函数,即 org.springframework.beans.factory.support.DefaultListableBeanFactory 类,这是一个非常核心的类,它包含了基本 IOC 容器所具有的重要功能,是一个 IOC 容器的基本实现。然后是调用了 this.reader.loadBeanDefinitions(resource) ,从这里开始加载配置文件。
Spring 在设计采用了许多程序设计的基本原则,比如迪米特法则、开闭原则,以及接口隔离原则等等,这样的设计为后续的扩展提供了灵活性,也增强了模块的复用性,这也是我看 Spring 源码的动力之一,希望通过阅读学习的过程来提升自己接口设计的能力。Spring 使用了专门的资源加载器对资源进行加载,这里的 reader 就是 org.springframework.beans.factory.xml.XmlBeanDefinitionReader 对象,专门用来加载基于 XML 文件配置的 bean。这里的加载过程为:
利用 EncodedResource 二次包装资源文件
获取资源输入流,并构造 InputSource 对象
获取 XML 文件的实体解析器和验证模式
加载 XML 文件,获取对应的 Document 对象
由 Document 对象解析并注册 bean
1.利用 EncodedResource 二次包装资源文件
采用 org.springframework.core.io.support.EncodedResource 对resource 进行二次封装.
2.获取资源输入流,并构造 InputSource 对象
对资源进行编码封装之后,开始真正进入 this.loadBeanDefinitions(new EncodedResource(resource)) 的过程,该方法源码如下:
publicintloadBeanDefinitions(EncodedResource encodedResource)throwsBeanDefinitionStoreException{
Assert.notNull(encodedResource, "EncodedResource must not be null");
if (logger.isInfoEnabled()) {
logger.info("Loading XML bean definitions from " + encodedResource.getResource());
}
// 标记正在加载的资源,防止循环引用
Set
if (currentResources == null) {
currentResources = new HashSet
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
if (!currentResources.add(encodedResource)) {
throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!");
}
try {
// 获取资源的输入流
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
// 构造InputSource对象
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
// 真正开始从XML文件中加载Bean定义
return this.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();
}
}
}
需要知晓的是 org.xml.sax.InputSource 不是 Spring 中定义的类,这个类来自 jdk,是 java 对 XML 实体提供的原生支持。这个方法主要还是做了一些准备工作,按照 Spring 方法的命名相关,真正干活的方法一般都是以 “do” 开头的,这里的 this.doLoadBeanDefinitions(inputSource, encodedResource.getResource()) 就是真正开始加载 XMl 的入口,该方法源码如下:
protectedintdoLoadBeanDefinitions(InputSource inputSource, Resource resource)throwsBeanDefinitionStoreException{
try {
// 1. 加载xml文件,获取到对应的Document(包含获取xml文件的实体解析器和验证模式)
Document doc = this.doLoadDocument(inputSource, resource);
// 2. 解析Document对象,并注册bean
return this.registerBeanDefinitions(doc, resource);
} catch (BeanDefinitionStoreException ex) {
// 这里是连环catch,省略
}
}
方面里面的逻辑还是很清晰的,第一步获取 org.w3c.dom.Document 对象,第二步由该对象解析得到 BeanDefinition 对象,并注册到 IOC 容器中。
3.获取 XML 文件的实体解析器和验证模式
this.doLoadDocument(inputSource, resource) 包含了获取实体解析器、验证模式,以及 Document 对象的逻辑,源码如下:
protectedDocumentdoLoadDocument(InputSource inputSource, Resource resource)throwsException{
return this.documentLoader.loadDocument(
inputSource,
this.getEntityResolver(), // 获取实体解析器
this.errorHandler,
this.getValidationModeForResource(resource), // 获取验证模式
this.isNamespaceAware());
}
XML 是半结构化数据,XML 的验证模式用于保证结构的正确性,常见的验证模式有 DTD 和 XSD 两种,获取验证模式的源码如下:
protectedintgetValidationModeForResource(Resource resource){
int validationModeToUse = this.getValidationMode();
if (validationModeToUse != VALIDATION_AUTO) {
// 手动指定了验证模式
return validationModeToUse;
}
// 没有指定验证模式,则自动检测
int detectedMode = this.detectValidationMode(resource);
if (detectedMode != VALIDATION_AUTO) {
return detectedMode;
}
// 检测验证模式失败,默认采用XSD验证
return VALIDATION_XSD;
}
上面源码描述了获取验证模式的执行流程,如果没有手动指定,那么 Spring 会去自动检测。对于 XML 文件的解析,SAX 首先会读取 XML 文件头声明,以获取对应验证文件地址,并下载对应的文件,如果网络不正常,则会影响下载过程,这个时候可以通过注册一个实体解析来实现寻找验证文件的过程。
4.加载 XML 文件,获取对应的 Document 对象
获取对应的验证模式和解析器,解析去就可以加载 Document 对象了,这里本质上调用的是 org.springframework.beans.factory.xml.DefaultDocumentLoader 的 loadDocument() 方法,源码如下:
publicDocumentloadDocument(InputSource inputSource, EntityResolver entityResolver,
ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
DocumentBuilderFactory factory = this.createDocumentBuilderFactory(validationMode, namespaceAware);
if (logger.isDebugEnabled()) {
logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
}
DocumentBuilder builder = this.createDocumentBuilder(factory, entityResolver, errorHandler);
return builder.parse(inputSource);
}
整个过程类似于我们平常解析 XML 文件的流程。
5.由 Document 对象解析并注册 bean
完成了对 XML 文件的到 Document 对象的解析,我们终于可以解析 Document 对象,并注册 bean 了,这一过程发生在 this.registerBeanDefinitions(doc, resource) 中,源码如下:
publicintregisterBeanDefinitions(Document doc, Resource resource)throwsBeanDefinitionStoreException{
// 使用DefaultBeanDefinitionDocumentReader构造
BeanDefinitionDocumentReader documentReader = this.createBeanDefinitionDocumentReader();
// 记录之前已经注册的BeanDefinition个数
int countBefore = this.getRegistry().getBeanDefinitionCount();
// 加载并注册bean
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
// 返回本次加载的bean的数量
return getRegistry().getBeanDefinitionCount() - countBefore;
}
这里方法的作用是创建对应的 BeanDefinitionDocumentReader,并计算返回了过程中新注册的 bean 的数量,而具体的注册过程,则是由 BeanDefinitionDocumentReader 来完成的,具体的实现位于子类 DefaultBeanDefinitionDocumentReader 中:
publicvoidregisterBeanDefinitions(Document doc, XmlReaderContext readerContext){
this.readerContext = readerContext;
logger.debug("Loading bean definitions");
// 获取文档的root结点
Element root = doc.getDocumentElement();
this.doRegisterBeanDefinitions(root);
}
还是按照 Spring 命名习惯,doRegisterBeanDefinitions 才是真正干活的地方,这也是真正开始解析配置的核心所在:
protectedvoiddoRegisterBeanDefinitions(Element root){
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = this.createDelegate(getReaderContext(), root, parent);
if (this.delegate.isDefaultNamespace(root)) {
// 处理profile标签(其作用类比pom.xml中的profile)
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
String[] specifiedProfiles =
StringUtils.tokenizeToStringArray(profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
if (!this.getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
if (logger.isInfoEnabled()) {
logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + getReaderContext().getResource());
}
return;
}
}
}
// 解析预处理,留给子类实现
this.preProcessXml(root);
// 解析并注册BeanDefinition
this.parseBeanDefinitions(root, this.delegate);
// 解析后处理,留给子类实现
this.postProcessXml(root);
this.delegate = parent;
}
方法中显示处理了 标签,这个属性在 Spring 中不是很常用,不过在 maven 的 pom.xml 中则很常见,意义也是相同的,就是配置多套环境,从而在部署的时候可以根据具体环境来选择使用哪一套配置。方法中会先去检测是否配置了 profile,如果配置了就需要从上下文环境中确认当前激活了哪一套 profile。
方法在解析并注册 BeanDefinition 前后各设置一个模板方法,留给子类扩展实现,而在 this.parseBeanDefinitions(root, this.delegate) 中执行解析和注册逻辑:
protectedvoidparseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate){
if (delegate.isDefaultNamespace(root)) {
// 解析默认标签
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
if (delegate.isDefaultNamespace(ele)) {
// 解析默认标签
this.parseDefaultElement(ele, delegate);
} else {
// 解析自定义标签
delegate.parseCustomElement(ele);
}
}
}
} else {
// 解析自定义标签
delegate.parseCustomElement(root);
}
}
方法中判断当前标签是默认标签还是自定义标签,并按照不同的策略去解析,这是一个复杂的过程,后面用文章进行针对性讲解,这里不在往下细究。
到这里我们已经完成了静态配置到动态 BeanDefinition 的解析,这个时候 bean 的定义已经处于内存中,解析去将是探究如何获取并使用 bean 的过程。
二. Bean的获取
在完成了 Bean 的加载过程之后,我们可以调用 beanFactory.getBean("myBean") 方法来获取目标对象,这里本质上调用的是 org.springframework.beans.factory.support.AbstractBeanFactory 的 getBean() 方法,源码如下:
publicObjectgetBean(String name)throwsBeansException{
return this.doGetBean(name, null, null, false);
}
这里调用 this.doGetBean(name, null, null, false) 来实现具体逻辑,也符合我们的预期,该方法可以看做是获取 bean 的整体框架,一个函数完成了整个过程的模块调度,还是挺复杂的:
protected
final String name, final Class
/*
* 转化对应的beanName
*
* 传入的参数可能是alias,也可能是FactoryBean,所以需要进行解析,主要包含以下内容:
* 1. 去除FactoryBean的修饰符“&”
* 2. 取指定alias对应的最终的name
*/
final String beanName = this.transformedBeanName(name);
Object bean;
/*
* 检查缓存或者实例工厂中是否有对应的实例
*
* 为什么会一开始就进行检查?
* 因为在创建单例bean的时候会存在依赖注入的情况,而在创建依赖的时候为了避免循环依赖
* Spring创建bean的原则是不等bean创建完成就会将创建bean的ObjectFactory提前曝光,即将对应的ObjectFactory加入到缓存
* 一旦下一个bean创建需要依赖上一个bean,则直接使用ObjectFactory
*/
Object sharedInstance = this.getSingleton(beanName); // 获取单例
if (sharedInstance != null && args == null) {
// 实例已经存在
if (logger.isDebugEnabled()) {
if (this.isSingletonCurrentlyInCreation(beanName)) {
logger.debug("Returning eagerly cached instance of singleton bean '" + beanName + "' that is not fully initialized yet - a consequence of a circular reference");
} else {
logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
}
}
// 返回对应的实例
bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, null);
} else {
// 单例实例不存在
if (this.isPrototypeCurrentlyInCreation(beanName)) {
/*
* 只有在单例模式下才会尝试解决循环依赖问题
* 对于原型模式,如果存在循环依赖,也就是满足this.isPrototypeCurrentlyInCreation(beanName),抛出异常
*/
throw new BeanCurrentlyInCreationException(beanName);
}
BeanFactory parentBeanFactory = this.getParentBeanFactory();
if (parentBeanFactory != null && !this.containsBeanDefinition(beanName)) {
// 如果在beanDefinitionMap中(即所有已经加载的类中)不包含目标bean,则尝试从parentBeanFactory中检测
String nameToLookup = this.originalBeanName(name);
if (args != null) {
// 递归到BeanFactory中寻找
return (T) parentBeanFactory.getBean(nameToLookup, args);
} else {
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
}
// 如果不仅仅是做类型检查,则创建bean
if (!typeCheckOnly) {
this.markBeanAsCreated(beanName);
}
try {
/*
* 将存储XML配置的GenericBeanDefinition转换成RootBeanDefinition
* 如果指定了beanName是子bean的话,同时会合并父类的相关属性
*/
final RootBeanDefinition mbd = this.getMergedLocalBeanDefinition(beanName);
this.checkMergedBeanDefinition(mbd, beanName, args);
// 获取当前bean依赖的bean
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
// 存在依赖,递归实例化依赖的bean
for (String dep : dependsOn) {
if (this.isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
// 缓存依赖调用
this.registerDependentBean(dep, beanName);
this.getBean(dep);
}
}
// 实例化依赖的bean后,实例化mbd自身
if (mbd.isSingleton()) {
// scope == singleton
sharedInstance = this.getSingleton(beanName, new ObjectFactory
@Override
publicObjectgetObject()throwsBeansException{
try {
return createBean(beanName, mbd, args);
} catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
}
});
bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
} else if (mbd.isPrototype()) {
// scope == prototype
Object prototypeInstance;
try {
this.beforePrototypeCreation(beanName);
prototypeInstance = this.createBean(beanName, mbd, args);
} finally {
this.afterPrototypeCreation(beanName);
}
// 返回对应的实例
bean = this.getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
} else {
// 其它scope
String scopeName = mbd.getScope();
final Scope scope = this.scopes.get(scopeName);
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope namehttp:// '" + scopeName + "'");
}
try {
Object scopedInstance = scope.get(beanName, new ObjectFactory
@Override
publicObjectgetObject()throwsBeansException{
beforePrototypeCreation(beanName);
try {
return createBean(beanName, mbd, args);
} finally {
afterPrototypeCreation(beanName);
}
}
});
// 返回对应的实例
bean = this.getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
} catch (IllegalStateException ex) {
throw new BeanCreationException(beanName, "Scope '" + scopeName + "' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton", ex);
}
}
} catch (BeansException ex) {
cleanupAfterBeanCreationFailure(beanName);
throw ex;
}
}
// 检查需要的类型是否符合bean的实际类型,对应getBean时指定的requireType
if (requiredType != null && bean != null && !requiredType.isAssignableFrom(bean.getClass())) {
try {
return this.getTypeConverter().convertIfNecessary(bean, requiredType);
} catch (TypeMismatchException ex) {
if (logger.isDebugEnabled()) {
logger.debug("Failed to convert bean '" + name + "' to required type '" + ClassUtils.getQualifiedName(requiredType) + "'", ex);
}
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
}
return (T) bean;
}
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~