spring IoC容器初始化简单分析

2023-11-18

首先分享一篇文章,对于IoC思想的理解

主题:我对IoC/DI的理解http://www.iteye.com/topic/1122310

    我认为,里面最重要的一段话如下:

理解IoC容器问题关键:控制的哪些方面被反转了?

1、谁控制谁?为什么叫反转? ------ IoC容器控制,而以前是应用程序控制,所以叫反转

2、控制什么?               ------ 控制应用程序所需要的资源(对象、文件……)

3、为什么控制?             ------ 解耦组件之间的关系

4、控制的哪些方面被反转了? ------ 程序的控制权发生了反转:从应用程序转移到了IoC容器

下面是我从11月下旬开始,看《Spring 技术内幕》和结合spring源码的一些简单分析,时间紧,就比较粗糙。

1.spring IoC容器核心数据结构

看了看,我认为BeanDefinition、Resource、BeanFactory、ApplicationContext是IoC容器核心结构。

1.1BeanDefinition

作用:持有bean数据结构,是注入的bean在IoC容器中的抽象

方法列表如下:

getBeanClassName;
getFactoryBeanName;
getScope;
isLazyInit;
isSingleton;
isPrototype;
isAbstract;
isPrimary;
isAutowireCandidate;
getDescription;

与BeanDefinition相关的类:

AbstractBeanDefinition、BeanDefinitionReader

AbstractBeanDefinition是BeanDefinition的一个完整实现

BeanDefinitionReader用于读取bean的信息的接口,AbstractBeanDefinitionReader实现了该接口,XmlBeanDefinitionReader继承了AbstractBeanDefinitionReader。

1.2Resource

对于资源文件的抽象,最终实现了InputStream getInputStream() throwsIOException这个方法

方法列表如下:

exists();
isReadable();
getURL();
getURI();
getFile();
contentLength();
lastModified();
getFilename();
getDescription();

1.3 BeanFactory

获取bean的工厂,极其重要,最基本的IoC容器

getBean();
containsBean();
isSingleton();
isPrototype();
isTypeMatch();
getType();
getAliases();

1.4 ApplicationContext

高级IoC容器,除了基本的IoC容器功能外,支持不同信息源、访问资源、支持事件发布等功能。

继承了一下接口

ListableBeanFactory、HierarchicalBeanFactory、MessageSource、ApplicationEventPublisher、ResourcePatternResolver

 

五个接口

接口ListableBeanFactory继承了BeanFactory,在此基础之上,添加了containsBeanDefinition、getBeanDefinitionCount、getBeanDefinitionNames等方法。

接口HierarchicalBeanFactory继承了BeanFactory,在此基础之上,添加了getParentBeanFactory、containsLocalBean这两个方法。

MessageSource用于获取国际化信息。所谓国际化信息,假设我们正在开发一个支持多国语言的Web应用程序,要求系统能够根据客户端的系统的语言类型返回对应的界面:英文的操作系统返回英文界面,而中文的操作系统则返回中文界面——这便是典型的i18n国际化问题。对于有国际化要求的应用系统,我们不能简单地采用硬编码的方式编写用户界面信息、报错信息等内容,而必须为这些需要国际化的信息进行特殊处理。简单来说,就是为每种语言提供一套相应的资源文件,并以规范化命名的方式保存在特定的目录中,由系统自动根据客户端语言选择适合的资源文件。

ApplicationEventPublisher为spring的事件发布者接口。因为ApplicationContext实现了该接口,因此spring的ApplicationContext实例具有发布事件的功能。

ResourcePatternResolver:资源装载器,装置资源使用。

ApplicationContext对外提供了getId()、getDisplayName()等方法。


2 IoC容器的初始化

IoC容器的初始化就是含有BeanDefinition信息的Resource的定位、载入、解析、注册四个过程,最终我们配置的bean,以beanDefinition的数据结构存在于IoC容器即内存中。这里并不涉及bean的依赖注入,不产生任何bean。

2.1BeanDefinition的Resource的定位

在AbstractBeanDefinitionReader.loadBeanDefinitions()方法中,调用了defaultResourceLoader的getResource()方法,这里即为resouce的定位,如下

public Resource getResource(String location) {
		Assert.notNull(location, "Location must not be null");
		if (location.startsWith(CLASSPATH_URL_PREFIX)) {
			return new
ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
		}
		else {
			try {
				// Try to parse the location as a URL...
				URL url = new URL(location);
				return new UrlResource(url);
			}
			catch (MalformedURLException ex) {
				// No URL -> resolve as resource path.
				return getResourceByPath(location);
			}
		}
	}

getResourceByPath的实现如下:

protected Resource getResourceByPath(String path) {
		return new ClassPathContextResource(path, getClassLoader());
	}

2.2 beanDefinition的载入

beanDefinition的载入在XmlBeanDefinitionReader中的loadBeanDefinitions()方法实现:

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
		Assert.notNull(encodedResource, "EncodedResource must not be null");
		if (logger.isInfoEnabled()) {
			logger.info("Loading XML bean definitions from " + encodedResource.getResource());
		}
		Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
		if (currentResources == null) {
			currentResources = new HashSet<EncodedResource>(4);
			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 = new InputSource(inputStream);
				if (encodedResource.getEncoding() != null) {
					inputSource.setEncoding(encodedResource.getEncoding());
				}
				return 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();
			}
		}
	}

其中,doLoadBeanDefinitions()方法在该类中实现如下

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
			throws BeanDefinitionStoreException {
		try {
			int validationMode = getValidationModeForResource(resource);
			Document doc = this.documentLoader.loadDocument(
					inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());
			return registerBeanDefinitions(doc, resource);
		}
		catch(){
		//异常处理
}
}

这里异常抛出了SAXParseException,可以知道,Spring解析xml的工具是SAX。

 

2.3 beanDefinition的解析

beanDefinition的解析是在BeanDefinitionParserDelegate类的parseBeanDefinitionElement(Elementele, BeanDefinition containingBean)方法中完成。

该方法首先调用了


String id = ele.getAttribute(ID_ATTRIBUTE);
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);

获取bean的id和bean的name。

接着,调用


AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean)

对bean元素进行详细解析,调用

parseBeanDefinitionAttributes()解析bean的属性;

parsePropertyElements(ele, bd)解析bean的property。

bean属性就是注入scope、isLazyInit、autowire、sigleton等属性,都是按照类似方法来做:

if (ele.hasAttribute(SCOPE_ATTRIBUTE)) {
			// Spring 2.x "scope" attribute
		beanDefinition.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));
}

调用beanDefinition的set方法来实现。

解析propterty方法具体在parsePropertyElements()方法中,主要:

1.  bean定义中如果有同名的propterty,那么只解析第一个property,对于后续的同名propterty不做任何处理。

2. 需要判断是ref还是value。

2.4 BeanDefinition的注册

BeanDefinition注册的意思就是让IoC知道BeanDefinition是怎么样存在的,怎么样才可以拿到BeanDefinition。在DefaultListenerableBeanFactory中,通过一个hashMap<String,BeanDefinition>来操作各个BeanDefinition。

在DefaultListenableBeanFactory中的publicvoid registerBeanDefinition(String beanName,BeanDefinition beanDefinition)throwsBeanDefinitionStoreException

完成了这个步骤。


Object oldBeanDefinition = this.beanDefinitionMap.get(beanName);
			if (oldBeanDefinition != null) {
				if (!this.allowBeanDefinitionOverriding) {
					throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
							"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
							"': There is already [" + oldBeanDefinition + "] bound.");
				}
				else {
					if (this.logger.isInfoEnabled()) {
						this.logger.info("Overriding bean definition for bean '" + beanName +
								"': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");
					}
				}
			}
			else {
				this.beanDefinitionNames.add(beanName);
				this.frozenBeanDefinitionNames = null;
			}
			this.beanDefinitionMap.put(beanName, beanDefinition);

最后就通过beanDefinitionMap来操作beanDefinition了。

2.5 IoC容器的启动

FileSystemXmlApplicationContext的构造函数如下,启动了IoC容器。

public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
			throws BeansException {
		super(parent);
		setConfigLocations(configLocations);
		if (refresh) {
			refresh();
		}
	}

refresh()方法在FileSystemXmlApplicationContext的父类AbstractApplicationContext中实现。在refresh()方法中,最终调用了refreshBeanFactory()方法。

 

完整IoC容器启动分析(以FileSystemXmlApplicationContext为例)

1.  FileSystemXmlApplicationContext构造方法调用refresh()方法,这里是载入beanDefinition的入口

2. refresh()方法在FileSystemXmlApplicationContext的父类

AbstractApplicationContext中的实现,调用refreshBeanFactory()方法;

3. refreshBeanFactory()方法在AbstractApplicationContext的子类

AbstractRefreshableApplicationContext中实现,

调用了loadBeanDefinition()方法,启动对beanDefinition的载入;

4. loadBeanDefinition()在AbstractXmlApplicationContext中实现,调用了XmlBeanDefinitionReader的loadBeanDefinitions()方法。

5. XmlBeanDefinitionReader的loadBeanDefinitions()实现了对于承载beanDefnition定义的xml文件的读入,以I/O的方式。

6. 读入后,对beanDefinition进行解析。Bean解析采用SAX工具,先按照XML文件格式解析,再按照spring bean也有的定义解析,在BeanDefinitionParserDelegate.parseBeanDefinitionElement()实现。

7. 最后对beanDefinition信息进行注册。就是将每个beanDefinition以key  =beanName,value = beanDefinition放入一个hashMap中,在DefaultListenableFactoty.RegisterBeanDefinition()中实现。

 

经过IoC容器的初始化后,IoC容器持有beanDefintion,为依赖注入bean即调用getBean()方法奠定了基础。



本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

spring IoC容器初始化简单分析 的相关文章

随机推荐

  • Android 上传头像功能第三方框架Boxing与TakePhoto使用总结

    刚做了个人中心的上传头像功能 就来总结一下做的过程 一开始就直接去github上找一个第三方框架接入 省点事 先是听了朋友的推荐去看了一下bilibili开源的一款Boxing的框架 但是使用起来发现不太符合我的需求 最后还是使用TakeP
  • 【学习排序】 Learning to Rank 中Listwise关于ListNet算法讲解及实现

    前一篇文章 Learning to Rank中Pointwise关于PRank算法源码实现 讲述了基于点的学习排序PRank算法的实现 该篇文章主要讲述Listwise Approach和基于神经网络的ListNet算法及Java实现 包括
  • inno setup打包软件学习

    目录 一 打包结果 二 示例打包脚本 三 错误解决 3 1 另一个程序正在使用此文件 进程无法访问 3 2 桌面图标无法修改 四 参考资料 一 打包结果 测试程序来自 泽森科工 zenustech com 二 示例打包脚本 使用打包软件下载
  • SVN服务器权限设置

    1 authz文件内容 groups admin xiaoming team1 zhangsan admin rw admin具有svnRepos根目录下所有文件的读写权限 组别为team1的人员具有projectname目录下所有文件的读
  • Java并发编程学习1-并发简介

    Java并发编程学习系列 Java并发编程学习 简介 线程的优势 发挥多处理器的强大能力 建模的简单性 异步事件的简化处理 响应更灵敏的用户界面 线程的风险 安全性问题 活跃性问题 性能问题 结语 简介 在早期的计算机中不包含操作系统 它们
  • 钩子函数介绍和总结

    Author 快来救救我 ps 快乐废宅 文章目录 前言 一 钩子函数 Hook 是什么 理解 特点 举例 钩子类型 钩子程序组成部分 钩子操作流程 钩子函数的回调函数 最后 三 参考文章 前言 我看了其他博客有些对钩子函数讲述的篇幅要不太
  • IBM Worklight开发环境安装

    1 安装jdk1 6以上版本 2 下载安装mysql http ftp iij ad jp pub db mysql Downloads MySQL 5 1 mysql noinstall 5 1 62 win32 zip 3 下载mysq
  • 成为黑客第一步:从这五本书开始

    如果您想学习黑客并成为道德黑客 那么这些最好的黑客书籍将带你走上正轨 学习黑客 如果你是一个完整的初学者 并不是一件容易的事 虽然网上有很多资源 但是黑客资源往往很少 因此阅读书籍通常很有帮助 黑客可能会被不道德地使用 还可能对个人和公司造
  • 当HDF5需要重新创建大量group和dataset但是又提示原来的group已经存在怎么办

    描述 我在创建tff数据集的时候 程序里面有循环写到hdf5需要根据某类名字创建group dataset 但是我有的时候会发现这段代码有问题 我需要修改一下循环里面的某些东西然后再重新run 这个时候会提示创建的group或者datase
  • C++ 去掉字符串前后的空格

    基本思路 先去掉字符串头部的空格 再去掉字符串尾部的空格 实现方式一 include
  • ICLR 2023高分论文!ToMe:你的视觉Transformer可以更快!

    点击下方卡片 关注 CVer 公众号 AI CV重磅干货 第一时间送达 点击进入 gt CV微信技术交流群 转载自 极市平台 作者 科技猛兽 导读 这篇文章提出了一种无需训练即可加速 ViT 模型 提高吞吐量的方法 Token Mergin
  • C语言static和extern关键字

    1 static static修饰的变量 自始至终只有一块空间 当前文件才能使用 生命周期是从定义开始直到程序结束 全局变量的定义是在 c文件中 但是声明全局变量是在 h中 static 静态型 用它定义的变量自动初始化为0值或空值 常用于
  • Django开发过程中的一些bug及解决方法

    在使用django开发一个交易平台的过程中 遇到各种各样的问题 其中有比较复杂的问题也有疏忽导致的比较简单的bug 记录下来 希望有所帮助 错误 A model can t have more than one AutoField Asse
  • Unity3D方向键控制人物移动的代码

    代码 var v Input GetAxis Vertical var h Input GetAxis Horizontal transform Translate transform forward Time deltaTime move
  • 有人用ChatGPT月入十万了!我们整理了70+款可以免费使用的AI工具

    策划丨KK 制图丨邬海雯 ChatGPT风靡全球 人人可用 小红书上有关ChatGPT的笔记已有10w 篇 相关话题浏览量也达到了1 12亿次 其中讨论最为热烈的 要数 ChatGPT使用教程 当然 类似的话题还包括 教你如何使用Midjo
  • docker部署mall商城

    文章目录 一 学习网址 二 mall的docker部署 1 docker环境安装 2 部署harbor 3 Mysql安装 4 部署redis 5 nginx安装 6 RabbitMQ安装 7 Elasticsearch安装 8 Logst
  • [Python人工智能] 二十.基于Keras+RNN的文本分类vs基于传统机器学习的文本分类

    从本专栏开始 作者正式研究Python深度学习 神经网络及人工智能相关知识 前一篇文章分享了循环神经网络RNN的原理知识 并采用Keras实现手写数字识别的RNN分类案例及可视化呈现 这篇文章作者将带领大家用Keras实现RNN和LSTM的
  • LeetCode 125. Valid Palindrome

    class Solution object def isPalindrome self s type s str rtype bool intable string punctuation outtable len intable s s
  • 【Python】用Python写MT4的自动交易脚本

    原理 使用MQL4原生库调用ZERO MQ作为消息服务端 使用其它语言 如Python作为客户端调用接口 Github地址 https github com ns2250225 py mt4
  • spring IoC容器初始化简单分析

    首先分享一篇文章 对于IoC思想的理解 主题 我对IoC DI的理解 http www iteye com topic 1122310 我认为 里面最重要的一段话如下 理解IoC容器问题关键 控制的哪些方面被反转了 1 谁控制谁 为什么叫反