该系列文章是本人在学习 Spring 的过程当中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合个人源码注释 Spring 源码分析 GitHub 地址 进行阅读html
Spring 版本:5.1.14.RELEASEjava
开始阅读这一系列文章以前,建议先查看《深刻了解 Spring IoC(面试题)》这一篇文章git
该系列其余文章请查看:《死磕 Spring 之 IoC 篇 - 文章导读》github
上一篇文章 《Bean 的“前身”》 对 BeanDefinition 进行了介绍,Bean 是根据 BeanDefinition 配置元信息对象生成的。咱们在 Spring 中一般以这两种方式定义一个 Bean:面向资源(XML、Properties)、面向注解,那么 Spring 是如何将这两种方式定义的信息转换成 BeanDefinition 对象的,接下来会先分析面向资源(XML、Properties)这种方式 Spring 是如何处理的面试
下来熟悉一段代码:正则表达式
dependency-lookup-context.xml
:spring
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <!-- <context:component-scan base-package="org.geekbang.thinking.in.spring.ioc.overview" /> --> <bean id="user" class="org.geekbang.thinking.in.spring.ioc.overview.domain.User"> <property name="id" value="1"/> <property name="name" value="小马哥"/> </bean> </beans>
// 建立 BeanFactory 容器 DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory); // XML 配置文件 ClassPath 路径 String location = "classpath:/META-INF/dependency-lookup-context.xml"; // 加载配置 int beanDefinitionsCount = reader.loadBeanDefinitions(location); System.out.println("Bean 定义加载的数量:" + beanDefinitionsCount); // 依赖查找 System.out.println(beanFactory.getBean("user"));;
这段代码是 Spring 中编程式使用 IoC 容器,咱们能够看到 IoC 容器的使用过程大体以下:编程
1
步建立的 BeanFactory上面的第 3
步会解析 Resource 资源,将 XML 文件中定义的 Bean 解析成 BeanDefinition 配置元信息对象,并往 BeanDefinitionRegistry 注册中心注册,此时并无生成对应的 Bean 对象,须要经过依赖查找获取到 Bean。固然,咱们在实际场景中通常不会这样使用 Spring,这些工做都会有 Spring 来完成。接下来咱们一块儿来看看 Sping 是如何加载 XML 文件的数组
org.springframework.beans.factory.support.BeanDefinitionReader
接口的类图以下所示:dom
总览:
org.springframework.beans.factory.support.BeanDefinitionReader
接口,BeanDefinition 读取器
org.springframework.beans.factory.support.AbstractBeanDefinitionReader
抽象类,提供通用的实现,具体的资源加载逻辑在由子类实现
org.springframework.beans.factory.xml.XmlBeanDefinitionReader
,XML 文件资源解析器,解析出 BeanDefinition 配置元信息对象并注册
org.springframework.beans.factory.support.PropertiesBeanDefinitionReader
,Properties 文件资源解析器
org.springframework.beans.factory.support.BeanDefinitionReader
接口,BeanDefinition 读取器,定义了加载资源的方法,代码以下:
public interface BeanDefinitionReader { /** 返回 BeanDefinition 注册中心 */ BeanDefinitionRegistry getRegistry(); /** 返回 Resource 资源加载器,默认为 PathMatchingResourcePatternResolver */ @Nullable ResourceLoader getResourceLoader(); /** 返回类加载器 */ @Nullable ClassLoader getBeanClassLoader(); /** 返回 Bean 的名称生成器,默认为 DefaultBeanNameGenerator */ BeanNameGenerator getBeanNameGenerator(); /** 从 Resource 资源中加载 BeanDefinition 并返回数量 */ int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException; int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException; int loadBeanDefinitions(String location) throws BeanDefinitionStoreException; int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException; }
org.springframework.beans.factory.support.AbstractBeanDefinitionReader
抽象类,实现了 BeanDefinitionReader 和 EnvironmentCapable 接口,代码以下:
public abstract class AbstractBeanDefinitionReader implements BeanDefinitionReader, EnvironmentCapable { private final BeanDefinitionRegistry registry; @Nullable private ResourceLoader resourceLoader; @Nullable private ClassLoader beanClassLoader; private Environment environment; private BeanNameGenerator beanNameGenerator = new DefaultBeanNameGenerator(); protected AbstractBeanDefinitionReader(BeanDefinitionRegistry registry) { Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); this.registry = registry; // Determine ResourceLoader to use. if (this.registry instanceof ResourceLoader) { this.resourceLoader = (ResourceLoader) this.registry; } else { this.resourceLoader = new PathMatchingResourcePatternResolver(); } // Inherit Environment if possible if (this.registry instanceof EnvironmentCapable) { this.environment = ((EnvironmentCapable) this.registry).getEnvironment(); } else { this.environment = new StandardEnvironment(); } } @Override public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException { Assert.notNull(resources, "Resource array must not be null"); int count = 0; for (Resource resource : resources) { count += loadBeanDefinitions(resource); } return count; } @Override public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException { return loadBeanDefinitions(location, null); } public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException { // 得到 ResourceLoader 对象 ResourceLoader resourceLoader = getResourceLoader(); if (resourceLoader == null) { throw new BeanDefinitionStoreException( "Cannot load bean definitions from location [" + location + "]: no ResourceLoader available"); } if (resourceLoader instanceof ResourcePatternResolver) { // Resource pattern matching available. try { // 得到 Resource 数组,由于 Pattern 模式匹配下,可能有多个 Resource 。例如说,Ant 风格的 location Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location); // 加载 BeanDefinition 们 int count = loadBeanDefinitions(resources); if (actualResources != null) { // 添加到 actualResources 中 Collections.addAll(actualResources, resources); } if (logger.isTraceEnabled()) { logger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]"); } return count; } catch (IOException ex) { throw new BeanDefinitionStoreException( "Could not resolve bean definition resource pattern [" + location + "]", ex); } } else { // Can only load single resources by absolute URL. // 得到 Resource 对象 Resource resource = resourceLoader.getResource(location); // 加载 BeanDefinition 们 int count = loadBeanDefinitions(resource); if (actualResources != null) { // 添加到 actualResources 中 actualResources.add(resource); } if (logger.isTraceEnabled()) { logger.trace("Loaded " + count + " bean definitions from location [" + location + "]"); } return count; } } @Override public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException { Assert.notNull(locations, "Location array must not be null"); int count = 0; for (String location : locations) { count += loadBeanDefinitions(location); } return count; } // ... 省略相关代码 }
在实现的方法中,最终都会调用 int loadBeanDefinitions(Resource resource)
这个方法,该方法在子类中实现
org.springframework.beans.factory.xml.XmlBeanDefinitionReader
,XML 文件资源解析器,解析出 BeanDefinition 配置元信息对象并注册
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader { /** * 禁用验证模式 */ public static final int VALIDATION_NONE = XmlValidationModeDetector.VALIDATION_NONE; /** * 自动获取验证模式 */ public static final int VALIDATION_AUTO = XmlValidationModeDetector.VALIDATION_AUTO; /** * DTD 验证模式 */ public static final int VALIDATION_DTD = XmlValidationModeDetector.VALIDATION_DTD; /** * XSD 验证模式 */ public static final int VALIDATION_XSD = XmlValidationModeDetector.VALIDATION_XSD; /** Constants instance for this class. */ private static final Constants constants = new Constants(XmlBeanDefinitionReader.class); /** * 验证模式,默认为自动模式。 */ private int validationMode = VALIDATION_AUTO; private boolean namespaceAware = false; private Class<? extends BeanDefinitionDocumentReader> documentReaderClass = DefaultBeanDefinitionDocumentReader.class; /** * 解析过程当中异常处理器 */ private ProblemReporter problemReporter = new FailFastProblemReporter(); private ReaderEventListener eventListener = new EmptyReaderEventListener(); private SourceExtractor sourceExtractor = new NullSourceExtractor(); @Nullable private NamespaceHandlerResolver namespaceHandlerResolver; private DocumentLoader documentLoader = new DefaultDocumentLoader(); @Nullable private EntityResolver entityResolver; private ErrorHandler errorHandler = new SimpleSaxErrorHandler(logger); /** * XML 验证模式探测器 */ private final XmlValidationModeDetector validationModeDetector = new XmlValidationModeDetector(); /** * 当前线程,正在加载的 EncodedResource 集合。 */ private final ThreadLocal<Set<EncodedResource>> resourcesCurrentlyBeingLoaded = new NamedThreadLocal<>( "XML bean definition resources currently being loaded"); /** * Create new XmlBeanDefinitionReader for the given bean factory. * @param registry the BeanFactory to load bean definitions into, * in the form of a BeanDefinitionRegistry */ public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) { super(registry); } }
loadBeanDefinitions(Resource resource)
方法,解析 Resource 资源的入口,方法以下:
@Override public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException { return loadBeanDefinitions(new EncodedResource(resource)); } 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); } // <1> 获取当前线程正在加载的 Resource 资源集合,添加当前 Resource,防止重复加载 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 { // <2> 从 Resource 资源获取 InputStream 流对象(支持编码) InputStream inputStream = encodedResource.getResource().getInputStream(); try { InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } // <3> 【核心】执行加载 Resource 资源过程,解析出 BeanDefinition 进行注册 return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); } finally { // 关闭流 inputStream.close(); } } catch (IOException ex) { throw new BeanDefinitionStoreException( "IOException parsing XML document from " + encodedResource.getResource(), ex); } finally { // <4> 从当前线程移除当前加载的 Resource 对象 currentResources.remove(encodedResource); if (currentResources.isEmpty()) { this.resourcesCurrentlyBeingLoaded.remove(); } } }
将 Resource 封装成 EncodedResource 对象,目的是让资源对象可设置编码
doLoadBeanDefinitions(InputSource inputSource, Resource resource)
方法,执行加载 Resource 资源过程,解析出 BeanDefinition 进行注册doLoadBeanDefinitions(InputSource inputSource, Resource resource)
方法,执行加载 Resource 资源过程,解析出 BeanDefinition 进行注册,方法以下:
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { // <1> 获取 XML Document 实例 Document doc = doLoadDocument(inputSource, resource); // <2> 根据 Document 实例,解析出 BeanDefinition 们并注册,返回注册数量 int count = registerBeanDefinitions(doc, resource); if (logger.isDebugEnabled()) { logger.debug("Loaded " + count + " bean definitions from " + resource); } return count; } // 省略 catch 各类异常 }
doLoadDocument(InputSource inputSource, Resource resource)
方法,获取 XML Document 实例registerBeanDefinitions(Document doc, Resource resource)
方法,根据 Document 实例,解析出 BeanDefinition 们并注册,返回注册数量doLoadDocument(InputSource inputSource, Resource resource)
方法,获取 Resource 资源对应的 XML Document 实例,方法以下:
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception { // <3> 经过 DefaultDocumentLoader 根据 Resource 获取一个 Document 对象 return this.documentLoader.loadDocument(inputSource, getEntityResolver(), // <1> 获取 `org.xml.sax.EntityResolver` 实体解析器,ResourceEntityResolver this.errorHandler, getValidationModeForResource(resource), isNamespaceAware()); // <2> 获取 XML 文件验证模式,保证 XML 文件的正确性 }
org.xml.sax.EntityResolver
实体解析器,ResourceEntityResolver,根据 publicId 和 systemId 获取对应的 DTD 或 XSD 文件,用于对 XML 文件进行验证,这个类比较关键,在后续文章会讲到DOCTYPE
内容则为 DTD 模式,不然为 XSD 模式factory
,开启校验factory
建立 DocumentBuilder 对象 builder
,设置 EntityResolver(第 1
步建立的)、ErrorHandler 属性builder
对 inputSource
(Resource 资源)进行解析,返回一个 Document 对象上述过程目的就是获取到 Resource 资源对应的 Document 对象,须要通过校验和解析两个过程
registerBeanDefinitions(Document doc, Resource resource)
方法,根据 Document 实例,解析出 BeanDefinition 们并注册,返回注册数量,方法以下:
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { // <1> 建立 BeanDefinitionDocumentReader 对象 BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); // <2> 获取已注册的 BeanDefinition 数量 int countBefore = getRegistry().getBeanDefinitionCount(); // <3> 建立 XmlReaderContext 对象(读取 Resource 资源的上下文对象) // <4> 根据 Document、XmlReaderContext 解析出全部的 BeanDefinition 并注册 documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); // <5> 计算新注册的 BeanDefinition 数量 return getRegistry().getBeanDefinitionCount() - countBefore; }
documentReader
DefaultBeanDefinitionDocumentReader#registerBeanDefinitions(Document doc, XmlReaderContext readerContext)
方法DTD(Document Type Definition),即文档类型定义,为 XML 文件的验证机制,属于 XML 文件中组成的一部分。DTD 是一种保证 XML 文档格式正确的有效验证方式,它定义了相关 XML 文档的元素、属性、排列方式、元素的内容类型以及元素的层次结构。其实 DTD 就至关于 XML 中的 “词汇”和“语法”,咱们能够经过比较 XML 文件和 DTD 文件 来看文档是否符合规范,元素和标签使用是否正确。
DTD 在必定的阶段推进了 XML 的发展,可是它自己存在着一些缺陷:
XSD(XML Schemas Definition),即 XML Schema 语言,针对 DTD 的缺陷由 W3C 在 2001 年推出。XML Schema 自己就是一个 XML 文档,使用的是 XML 语法,所以能够很方便的解析 XSD 文档。相对于 DTD,XSD 具备以下优点:
咱们在 Spring 中一般以这两种方式定义一个 Bean:面向资源(XML、Properties)、面向注解,对于第一种方式若是定义的是一个 XML 文件,Spring 会经过 XmlBeanDefinitionReader 加载该 XML 文件,获取该 Resource 资源的 org.w3c.dom.Document
对象,这个过程会通过校验、解析两个步骤