详实明了的IOC容器的介绍,启动流程以及Bean的实例化和依赖注入

文章目录

    前言
    项目环境
    核心要点
    IOC容器的启动过程
        1. 资源定位,找到配置文件
        2.BeanDefinition的载入和解析,将配置文件解析成BeanDefiniton
        3. BeanDefinition的注册,将BeanDefinition向Map中注册`beanDefinitionMap`
    Bean的实例化和依赖注入
        Bean的实例化
    流程分析
        实例化Bean
        Bean的依赖注入(属性注入)
    总结

前言

今天我们来认识一下Spring IOC容器,本文主要介绍SpringIOC容器的核心要点以及其启动流程和实例化流程。
项目环境

Springframework 4.3.12
核心要点

Spring IOC是什么?他有什么作用呢?我们通过了解学习,Spring IOC是一个容器,用于生成和管理Bean的实例,以及实例之间的依赖关系,然后注入相关的依赖。这里我们可以把IOC容器想象成一个餐馆。我们去餐馆点菜的话,不需要关心菜的生成过程,不需要关心菜的原材料从哪里来。我们只需要最终做好的菜。这里的菜就是我们的需要的Bean。不同的菜对应不同的Bean。没有IOC 容器的情况下,如果需要一个Bean的话,就需要自己来new一个对象的实例,比如A类依赖了B类,那么就需要在A类中new一个B类的实例对象,这就好像我们要自己在家动手做菜一样。有了IOC容器之后,如果A类依赖B类,只需要通过IOC容器帮我们创建A类的实例和B类的实例,然后IOC容器会将B类的实例注入到A类中。这就很像餐馆把菜做好之后送给我们一样。既然IOC容器这么6,那么他是如何实现这一切的呢?
还是回到餐馆那个例子,做菜的话就需要与原材料和菜谱,同样的IOC容器想要管理各个业务对象以及他们之间的依赖关系,就需要通过某种途径来记录和管理这些信息,而BeanDefinition对象就承担了这个责任。IOC容器中每一个Bean都会有一个对应的BeanDefinition实例,该实例负责保存bean对象的所有必要信息,包括Bean对象的class类型,是否是抽象类,构造方法和参数,以及其他属性等,这里的BeanDefinition就相当于原材料。而BeanDefinitionRegistry对象和BeanFactory对象就相当于菜谱,告诉我们如何将原材料加工成相应的菜肴。
下面我们就来看看这些比较核心的类和接口。
在这里插入图片描述
在这里插入图片描述

认识上面的几个核心接口和类,对我们下面看Bean的启动过程和实例化过程有很大的帮助。

需要说明的是,在Spring中,ApplicationContext是IOC容器的承载体,而BeanFactory是操作这个容器的工具,两者关系紧密,相互协作,refresh方法实现了ApplicationContext和BeanFactory相互协作的过程,不同之处主要在于子类 AbstractRefreshableApplicationContext 和 GenericApplicationContext 中实现,两者使用的 BeanFactory 都为 DefaultListableBeanFactory,它构建在BeanFactory之 上,属于更⾼级的容器,除了具有BeanFactory的所有能⼒之外,还提供对事件监听机制以及国际化的⽀持等。它管理的bean,在容器启动 时全部完成初始化和依赖注⼊操作。
IOC容器的启动过程

介绍完了IOC容器的核心类和要点,接下来我们看看IOC容器的启动过程,其启动过程主要有如下三个步骤:

  1. 资源定位,找到配置文件

这里定位资源有两种方式,一种是通过ClassPathXmlApplicationContext类来解析Spring的配置文件的形式,就是通过配置文件来定义Bean的情况,另外,一种情况就是通过注解的方式来定义Bean的情况,这种情况是通过AnnotationConfigApplicationContext类解析的,主要是扫描项目的classPath下定义的注解。下面我们首先介绍下通过ClassPathXmlApplicationContext。这个类的核心作用是作为一个解析Xml的入口,其调用链是: ClassPathXmlApplicationContext类的构造器
------>AbstractApplicationContext类的refresh方法
----->调用AbstractRefreshableApplicationContext类的refreshBeanFactory方法
---->XmlWebApplicationContext类的loadBeanDefinitions方法
----> AbstractBeanDefinitionReader类的loadBeanDefinitions方法
---->XmlBeanDefinitionReader类的loadBeanDefinitions方法
---->XmlBeanDefinitionReader类的doLoadBeanDefinitions方法
---->XmlBeanDefinitionReader类的registerBeanDefinitions方法
---->DefaultBeanDefinitionDocumentReader类的registerBeanDefinitions方法
调用层次很深,我们就直接跳到核心的方法来看。下面我们就来看看registerBeanDefinitions方法

@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
	this.readerContext = readerContext;
	logger.debug("Loading bean definitions");
	//读取XML文件
	Element root = doc.getDocumentElement();
	//载入并注册BeanDefinition
	doRegisterBeanDefinitions(root);
}

然后,registerBeanDefinitions方法只是读取到根节点root之后,就另外一个核心方法doRegisterBeanDefinitions方法,然后,doRegisterBeanDefinitions方法又把逻辑转给了parseBeanDefinitions方法,这个parseBeanDefinitions方法首先获取所有的子节点 ,然后遍历解析子节点,载入BeanDefinition又交给了parseDefaultElement方法和parseCustomElement方法。

protected void parseBeanDefinitions(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)) {
					//解析节点
					parseDefaultElement(ele, delegate);
				}
				else {
					delegate.parseCustomElement(ele);
				}
			}
		}
	}
	else {
		delegate.parseCustomElement(root);
	}
}

2.BeanDefinition的载入和解析,将配置文件解析成BeanDefiniton

说完了配置文件的解析之后,接下来,我们来看看BeanDefinition的载入和解析。我们直接找到parseDefaultElement方法。

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
	//省略部分非核心代码
	//如果节点是bean节点,说明是一个Bean
	else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
		processBeanDefinition(ele, delegate);
	}
}

这个方法按照节点名,调用不同的处理方法,在此处我们只看节点为bean时调用的方法processBeanDefinition方法。

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
	BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
	if (bdHolder != null) {
		bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
		try {
			// Register the final decorated instance.(注册BeanDefinition)
			BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
		}
		catch (BeanDefinitionStoreException ex) {
			getReaderContext().error("Failed to register bean definition with name '" +
					bdHolder.getBeanName() + "'", ele, ex);
		}
		// Send registration event.
		getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
	}
}

我们重点看BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());这个方法,这个方法才是真正的将传入BeanDefinitionRegistry类,载入并解析BeanDefinition,然后对BeanDefinition进行注册。
3. BeanDefinition的注册,将BeanDefinition向Map中注册beanDefinitionMap

接下来就到了我们的重头戏,注册BeanDefinition到beanDefinitionMap中,其中key就是Bean的id,其中beanDefinitionMap是一个定义在DefaultListableBeanFactory类中全局的线程安全的map,用于存放解析到的BeanDefinition。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值