您的位置:首页 > 编程语言 > Java开发

理解spring ioc 原理

2016-12-29 21:49 1046 查看
Ioc容器是Spring的核心,Spring的依赖反转由Ioc实现,同时,几乎其他所有的Spring特性都依赖Ioc容器。Ioc容器是Spring框架最核心的部分。

先说一段容器启动的Demo代码:

public class IocDemo {
static AnnotationConfigApplicationContext annotationConfigApplicationContext;
public static void main(String[] args) throws InterruptedException {
annotationConfigApplicationContext = new AnnotationConfigApplicationContext();
annotationConfigApplicationContext.scan("com.tubemogul.springsecuredemo.springDemo.bean");
annotationConfigApplicationContext.refresh();
TestService2 testService2 = annotationConfigApplicationContext.getBean(TestService2.class);
System.out.println(testService2.getClass().getName());
}
}


这里选择的容器是AnnotationConfigApplicationContext,基于注解配置的(个人认为比XML好,依赖配置和代码在一起,一可以减少配置量,二也更直观)。在获取容器对象前有3步:

1. new出AnnotationConfigApplicationContext对象

2. 调用scan函数,扫描bean配置。

3. refresh完成容器初始化。

一. 理解AnnotationConfigApplicationContext对象

首先理解AnnotationConfigApplicationContext对象,除了这个以外,类似的还有XmlConfigApplicationContext对象等。Spring容器有很多种,应对不同的需求场景,容器的功能按接口进行逻辑拆分。这是Ioc容器接口设计图:



理解下几个顶层接口,BeanFactory接口包含对bean的操作,比如获取bean,Message接口包含消息类操作,ReasourceLoader负责资源加载,ApplicationEventPublisher包含应用程序事件发布功能。

这里可看出ioc容器的2大系列,beanFactory系列和applicationContext系列的关系,实际applicationContext系列容器是beanFactory容器系列的功能增强版,除了容器管理功能外,还增加了消息,资源加载,应用程序事件发布等功能。

那么看看我用的实现类AnnotationConfigApplicationContext的继承关系:



重点理解BeanFactory的功能,沿着继承树查看,getBean()方法是在AbstractApplicationContext类中实现:



这里调用getBeanFactory()获取BeanFactory,真实的BeanFactory封装在GenericApplicationContext,本质上是一个DefaultListableBeanFactory:



进入DefaultListableBeanFactory查看,这里就是实际持有Bean和BeanDefinition的地方

二. 理解AnnotationConfigApplicationContext对配置的加载

追踪annotationConfigApplicationContext.scan(“com.tubemogul.springsecuredemo.springDemo.bean”);方法,我们会来到ClassPathBeanDefinitionScanner类中的doScan方法:



从注释和代码上理解,这个方法就是根据入参的包地址扫描配置,并装配成一个BeanDefinition的集合,最后注册在registry中。AnnotationConfigApplicationContext是从代码注解中扫描配置装配出BeanDefinition(同理XmlConfigApplicationContext就是从XML文件中扫描装配出BeanDefinition)。看下BeanDefinition接口:



基本上涵盖了生成一个Bean需要的函数,可以理解Spring容器生成Bean就是基于BeanDefinition来生成的。

再说说最后的注册BeanDefinition到register对象中,这个register对象实际上就是AnnotationConfigApplicationContext,这类是实现了AnnotationConfigRegistry接口的



追踪register方法,其实是在AnnotationConfigApplicationContext持有的AnnotatedBeanDefinitionReader对象中实现的,这应该算是设计模式中的桥接模式。

这个代码其实写得有点绕,看AnnotatedBeanDefinitionReader的初始化:



看到没有,这里又把AnnotationConfigApplicationContext当成register给传进去了,追踪到AnnotatedBeanDefinitionReader里的regiseterBean方法:



最终调用了registry.registerBeanDefinition()方法完成了注册BeanDefinition的过程:



理解registry之前,理解一下为什么Spring作者要弄一个AnnotatedBeanDefinitionReader出来,我理解就是针对AnnotationConfig的一些专用处理的封装,在给真正registry注册BeanDefition之前,抽象了一层Reader,适配不同的配置源。

registerBeanDefinition其实是在DefaultListableBeanFactory中实现的,最终BeanDefinition是在DefaultListableBeanFactory的beanDefinitionMap中保存。至此,Scan()函数的工作就完成了,其实就是读取配置,解析配置,转换为BeanDefinition并保存。

三. refresh()方法完成Ioc容器初始化

AbstractApplicationContext.refresh()方法是在AbstractApplicationContext类中实现,直接看源码:



步骤很多,根据函数名和注释大致可以理解每一步是做什么的

调用过refresh函数后,就可以从容器中获取对象了。

最后可以看出来,其实读取配置的过程就是装配BeanDefinition并保存,在getBean的时候才会根据BeanDefinition生成时间Bean对象,当然如果是单例对象就会被缓存起来,生成对象时会连带注入依赖对象等等操作。

功能有多复杂,代码就有多复杂,Spring ioc细节很多,也不可能一一搞清楚,只有先了解大致原理,在具体使用中在根据需要选择性的深入
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  spring