目录

  1. Spring的整体架构
  2. 容器的基本实现与XML文件的加载

一、Spring的整体架构

Spring是一个分层架构,主要包含以下部分

  1. Core Container
  2. Data Access
  3. Web
  4. Aop
  5. Test

1、Core Container

核心容器,包含Core、Beans、Context和Expression Language(EL表达式)模块。Core和Beans是基础部分,提供IoC(控制反转)和DI(依赖注入),提供对工厂模式的经典实现来消除对程序单例模式的需求,并通过xml的配置和代码解耦

  • Core:包含基本的核心工具类,其他组件都需要用到,可以看做是Utils工具类
  • Beans:包含访问配置文件、创建、管理bean以及进行DI
  • Context:主要是在Core和Beans的进一步封装,继承Beans的特性并提供大量拓展如国际化、事件传播、资源加载等。ApplicationContext接口是Context模块的关键
  • EL:用于在运行是查询、操作对象

2、Data Access

数据访问相关,包含JDBC、ORM、OXM、JMS 和 Transaction模块

  • JDBC:为不同的数据库连接访问提供抽象层
  • ORM:为流行的对象-关系映射API如JPA、JDO、Hibernate、iBatis等提供一个交互层
  • JMS:包含一些制造和消费消息的一些特性

3、Web

4、AOP

5、Test

二、容器的基本实现

因为Spring可以使用xml完成容器和Bean的相关配置,先看最基本的获取XmlBeanFactory类型的实例

// XmlBeanFactory 继承 BeanFactory
BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("beanFactory.xml"));

bean是Spring中最核心的东西,beans包下的两个核心类

  1. DefaultListableBeanFactory:bean加载的核心部分,是bean注册及加载bean的默认实现
  2. XmlBeanDefinitionReader:xml配置文件的读取

1、核心的两个类

Spring版本5.2.12下BeanFactory接口下继承体系

DefaultListableBeanFactory


XmlBeanFactory实现了DefaultListableBeanFactory,在此基础上增加了XmlBeanDefinitionReader类型的成员变量reader

用于对xml配置文件读取

XmlBeanDefinitionReader

1、先看new ClassPathResource("beanFactory.xml")的构造,ClassPathResource的getInputStream()方法是利用ClassLoader的getResourceAsStream(path)得到InputStream进而转化为Resource

// package org.springframework.core.io;
// public class ClassPathResource extends AbstractFileResolvingResource public InputStream getInputStream() throws IOException {InputStream is;if (this.clazz != null) {is = this.clazz.getResourceAsStream(this.path);} else if (this.classLoader != null) {is = this.classLoader.getResourceAsStream(this.path);} else {is = ClassLoader.getSystemResourceAsStream(this.path);}if (is == null) {throw new FileNotFoundException(this.getDescription() + " cannot be opened because it does not exist");} else {return is;}}

2、XmlBeanFactory的实例化详细流程

1、回到XmlBeanFactory的构造方法new XmlBeanFactory(new ClassPathResource("beanFactory.xml"));

/** @deprecated */
// 因为使用的是SpringBoot源码分析,因此已经被标注过时
@Deprecated
public class XmlBeanFactory extends DefaultListableBeanFactory {private final XmlBeanDefinitionReader reader;public XmlBeanFactory(Resource resource) throws BeansException {this(resource, (BeanFactory)null);}public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {// 1、调用父类 AbstractAutowireCapableBeanFactory 的构造方法super(parentBeanFactory);// 2、 创建reader实例this.reader = new XmlBeanDefinitionReader(this);// 3、开始解析Resourcethis.reader.loadBeanDefinitions(resource);}
}

1.1、调用父类 AbstractAutowireCapableBeanFactory 的构造方法

 public AbstractAutowireCapableBeanFactory() {this.instantiationStrategy = new CglibSubclassingInstantiationStrategy();this.parameterNameDiscoverer = new DefaultParameterNameDiscoverer();this.allowCircularReferences = true;this.allowRawInjectionDespiteWrapping = false;this.ignoredDependencyTypes = new HashSet();this.ignoredDependencyInterfaces = new HashSet();this.currentlyCreatedBean = new NamedThreadLocal("Currently created bean");this.factoryBeanInstanceCache = new ConcurrentHashMap();this.factoryMethodCandidateCache = new ConcurrentHashMap();this.filteredPropertyDescriptorsCache = new ConcurrentHashMap();// 忽略给定接口的自动转配,Bean、BeanFactory、BeanClassloader可以通过实现下面3个接口的时候进行相应Bean的注入//当其他Bean的属性包含上面情况的Bean的时候,上面情况的Bean不会因为依赖注入被自动初始化this.ignoreDependencyInterface(BeanNameAware.class);this.ignoreDependencyInterface(BeanFactoryAware.class);this.ignoreDependencyInterface(BeanClassLoaderAware.class);}

1.2、创建XmlBeanDefinitionReader类型的reader实例

1.3、关键逻辑就在this.reader.loadBeanDefinitions(resource);

loadBeanDefinitions(resource)方法

进入XmlBeanDefinitionReader先进行加载XML文档和解析Bean前的准备

  1. 封装资源文件:首先对参数Resource使用EncodedResource类进行封装
  2. 获取输入流:从Resource中获取对应的InputStream封装为InputSource
  3. 通过构造的Resource和InputSource调用doLoadBeanDefinitions(inputSource, encodedResource.getResource());

doLoadBeanDefinitions方法主要做了三件事

  1. 获取对XML文件的验证模式:XML文件主要有DTD和XSD两种模式,getValidationMode(resource)—>detectValidationMode(resource)—>XmlvalidationModeDetector#validationModeDetector(resource)方法
  2. 加载XML文件,得到对应的Document:委托给DoucumentLoader的实现子类DefaultDocumentLoader
  3. 根据返回的Document注册Bean信息:委托给BeanDefinitionDocumentReader的实现子类DefaultBeanDefinitionDocumentReader
doLoadBeanDefinitions方法
// XmlBeanDefinitionReader#doLoadBeanDefinitions()
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {try {// 1、获取DocumentDocument doc = this.doLoadDocument(inputSource, resource);// 2、调用 registerBeanDefinitions 注册BeanDefinitionsint 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);}}

// 1、获取Document

// 1、委托DocumentLoader转化Document,实际实现子类是DefaultDocumentLoader// DefaultDocumentLoader#loadDocument()//EntityResolver 作用:提供一个本地查找DTD的方法,避免网络查找。主要还是用作验证
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver, ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {DocumentBuilderFactory factory = this.createDocumentBuilderFactory(validationMode, namespaceAware);if (logger.isTraceEnabled()) {logger.trace("Using JAXP provider [" + factory.getClass().getName() + "]");}DocumentBuilder builder = this.createDocumentBuilder(factory, entityResolver, errorHandler);return builder.parse(inputSource);}

// 2、调用 registerBeanDefinitions 注册BeanDefinitions

// 2、 BeanDefinitionDocumentReader 拿着Document进行BeanDefinitions注册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;}// 实际调用的是DefaultBeanDefinitionDocumentReader#registerBeanDefinitions()public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {this.readerContext = readerContext;this.doRegisterBeanDefinitions(doc.getDocumentElement());}protected void doRegisterBeanDefinitions(Element root) {// 专门处理解析BeanDefinitionParserDelegate parent = this.delegate;this.delegate = this.createDelegate(this.getReaderContext(), root, parent);if (this.delegate.isDefaultNamespace(root)) {// 处理profile属性,如一个xml可以设置激活dev、prod等配置环境String profileSpec = root.getAttribute("profile");if (StringUtils.hasText(profileSpec)) {String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, ",; ");if (!this.getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {if (this.logger.isDebugEnabled()) {this.logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + this.getReaderContext().getResource());}return;}}}// 解析前处理,留给子类实现this.preProcessXml(root);// 解析this.parseBeanDefinitions(root, this.delegate);// 解析后处理,留给子类实现this.postProcessXml(root);this.delegate = parent;}

// 3、解析 parseBeanDefinitions(root, this.delegate);

 protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {// 对beans的处理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)) {// 对bean的处理:对于跟标签和子标签是默认命名空间,如<bean>this.parseDefaultElement(ele, delegate);} else {// 对bean的处理:对自定义命名空间的标签解析,如<tx:annotation-driven/>delegate.parseCustomElement(ele);}}}} else {delegate.parseCustomElement(root);}}

下一部分:对XML文件标签的解析

《Spring源码深度解析 郝佳 第2版》容器的基本实现与XML文件的加载相关推荐

  1. 《Spring源码深度解析 郝佳 第2版》AOP

    往期博客 <Spring源码深度解析 郝佳 第2版>容器的基本实现与XML文件的加载 <Spring源码深度解析 郝佳 第2版>XML标签的解析 <Spring源码深度解 ...

  2. 《Spring源码深度解析 郝佳 第2版》ApplicationContext

    往期博客: <Spring源码深度解析 郝佳 第2版>容器的基本实现与XML文件的加载 <Spring源码深度解析 郝佳 第2版>XML标签的解析 <Spring源码深度 ...

  3. 《Spring源码深度解析 郝佳 第2版》SpringBoot体系分析、Starter的原理

    往期博客 <Spring源码深度解析 郝佳 第2版>容器的基本实现与XML文件的加载 <Spring源码深度解析 郝佳 第2版>XML标签的解析 <Spring源码深度解 ...

  4. 《Spring源码深度解析 郝佳 第2版》事务

    往期博客 <Spring源码深度解析 郝佳 第2版>容器的基本实现与XML文件的加载 <Spring源码深度解析 郝佳 第2版>XML标签的解析 <Spring源码深度解 ...

  5. 《Spring源码深度解析 郝佳 第2版》JDBC、MyBatis原理

    往期博客 <Spring源码深度解析 郝佳 第2版>容器的基本实现与XML文件的加载 <Spring源码深度解析 郝佳 第2版>XML标签的解析 <Spring源码深度解 ...

  6. 《Spring源码深度解析 郝佳 第2版》XML标签的解析

    目录 往期博客<Spring源码深度解析 郝佳 第2版>容器的基本实现与XML文件的加载 分析了xml文件的加载,接下来就是标签的解析,入口函数有两个 默认标签的解析 自定义标签的解析 一 ...

  7. 《Spring源码深度解析 郝佳 第2版》bean的加载、循环依赖的解决

    往期博客: <Spring源码深度解析 郝佳 第2版>容器的基本实现与XML文件的加载 <Spring源码深度解析 郝佳 第2版>XML标签的解析 往期博客完成了xml文件加载 ...

  8. Spring源码深度解析(郝佳)-学习-第二章-容器的基本实现

    DefaultListableBeanFactory XmlBeanFactory继承自DefaultListableBeanFactory,而DefaultListableBeanFactory是整 ...

  9. Spring源码深度解析(郝佳)-学习-源码解析-基于注解bean定义(一)

    我们在之前的博客 Spring源码深度解析(郝佳)-学习-ASM 类字节码解析 简单的对字节码结构进行了分析,今天我们站在前面的基础上对Spring中类注解的读取,并创建BeanDefinition做 ...

最新文章

  1. SpringBoot conditional注解和自定义conditional注解使用
  2. webpack入门(二)what is webpack
  3. Python基础语法总结,Python初学者必备
  4. Metasploit save命令技巧
  5. web报表开发技术专题八:总结
  6. -9 逆序输出一个整数的各位数字_【Java编程基本功】(八)逆序输出、是否为回文数,判断星期几,升序排列...
  7. ubuntu安装scala开发环境
  8. 剑指offer面试题54. 二叉搜索树的第k大节点(逆中序遍历)
  9. UE4 如何导入外部插件包
  10. 分享:git push 时报错 Permission to username/My_python.git denied to deploy key 解决方法
  11. 黑客主要学习python的什么_黑客最常用的黑客语言——Python!
  12. MATLAB2014a的安装
  13. 计算机重启很慢,教您win7电脑关机很慢的解决方法
  14. 90后凤凰男:寒门难出贵子
  15. centos7里xxx.jar解压和压缩
  16. 联想笔记本突然没声音了,但是音量却是打开得,F1亮着
  17. WIN11+CUAD11.2+vs2019+tensorTR8.6+Yolov3/4/5模型加速
  18. 抖音seo源码,抖音seo矩阵系统源码技术搭建
  19. 《ROS理论与实践》学习笔记(九)机器人自主导航
  20. week9-东东学打牌

热门文章

  1. 大学python考试试题_大学Python程序题题库
  2. buffer busy waits等待事件案例-vage
  3. 查找组成一个偶数最接近的两个素数
  4. PAM AppArmor非默认目录构建和安装
  5. hexo 大厂都在用的主题,非常好用的主题
  6. 拨码开关记录(没用)
  7. CloudFront-转发到其他域名
  8. 光纤耦合透镜参数优化
  9. 理论:如何调整四轴4个电机的转速,使飞行器朝不同方向运动
  10. 北京黑马面授java基础_北京顺义黑马JavaEE基础100期(20190324面授)——开班贴