Spring-IOC原理(一)

前言

在Spring中最重要的就属IOC了,不论是在面试中,还是在一些中间件与Spring的继承中,都离不开IOC,下面就来瞧瞧IOC是个什么东西,他是怎么一个加载流程,先大致粗略的了解一下!

一、下载Spring源码

二、编译Spring源码

在编译的时候有些包是需要从外网下载的,有条件的可以科学上网,这样可以加快编译速度。如果不管怎么编译都是报错,或者下载包总是失败,可以留言我,我可以提供一点有限的帮助。

三、创建demo模块

创建demo模块

注意: 一定要选择Gradle,别选错了,因为从Spring5开始,使用的是Gradle来构建整个项目的

创建demo模块-2

这里因为我已经事先创建过了模块,所以上面会有一个报错。然后点Finish完成创建。

创建完毕之后,在初始化模块的目录结构,我的目录结构是如下结构:

demo模块目录结构

这里我创建了三个类ApplicationMain启动类、TestIOC接口、实现TestIOC接口的TestIOCImpl实现类。

TestIOC.java代码:

1
2
3
4
5
6
7
8
9
10
11
12
package com.lhb.service;
/**
* @Program: spring
* @Description:
* @Author: LHB
* @Version: v0.0.1
* @Time: 2021-07-26 14:52
**/
public interface TestIOC {
public void helloIOC(String say);
}

TestIOCImpl.java代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.lhb.service.impl;
import com.lhb.service.TestIOC;
import org.springframework.stereotype.Service;
/**
* @Program: spring
* @Description:
* @Author: LHB
* @Version: v0.0.1
* @Time: 2021-07-26 14:53
**/
@Service
public class TestIOCImpl implements TestIOC {
@Override
public void helloIOC(String say) {
System.out.println("====> " + say);
}
}

ApplicationMain.java启动类代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.lhb;
import com.lhb.service.impl.TestIOCImpl;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/**
* @Program: spring
* @Description:
* @Author: LHB
* @Version: v0.0.1 mode
* @Time: 2021-07-26 14:56
**/
@Configuration
@ComponentScan("com.lhb")
public class ApplicationMain {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ApplicationMain.class);
TestIOCImpl bean = applicationContext.getBean(TestIOCImpl.class);
bean.helloIOC("hello spring ioc");
}
}

四、IOC主流程分析

首先,肯定是要从整个程序的入口开始分析的,下面就从ApplicationMain中的main方法开始。从上图中我们可以看到在ApplicationMain类中的main方法中手动创建了一个AnnotationConfigApplicationContext

1
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ApplicationMain.class);

嗯??what.AnnotationConfigApplicationContext是一个什么东西。。为什么要创建这个东西;那么就带着这个疑问点到他的源码里看一看。Ctrl+鼠标左键走起。

AnnotationConfigApplicationContext.java

这两个名叫Juergen Hoeller和Chris Beams的作者给写了一大片注释,大概意思就是AnnotationConfigApplicationContext是整个引用的上下文,可以用他来获取容器中的Bean,和注册Bean等;AnnotationConfigApplicationContext实现会这继承了Spring框架中大部分的基础能力,一切Spring的动作,都是从这个类开始进行的,并且整个过程也都是这个类来进行接管的;所以它也是比较强大;总之大部分容器中的和Bean的操作都可以通过AnnotationConfigApplicationContext来操作。

AnnotationConfigApplicationContext关系图

从上面的AnnotationConfigApplicatioinContext类关系图中可以看出,这个类其实比较复杂的。好了这个类先聊到这里,接下来我们看到AnnotationConfigApplicationContext的构造方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
* Create a new AnnotationConfigApplicationContext, deriving bean definitions
* from the given component classes and automatically refreshing the context.
* @param componentClasses one or more component classes — for example,
* {@link Configuration @Configuration} classes
*/
public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
/** 这里会实例化BeanDefinitionReader和BeanDefinitionScanner,并注册一些框架初始化必要的Bean
* 调用无参构造函数,会先调用父类GenericApplicationContext的构造函数,父类构造函数中就是初始化
* DefaultListableBeanFactory,并且赋值给beanFactory。
* 在本类的构造函数中,初始化了一个读取器reader = new AnnotatedBeanDefinitionReader(this);
* 和一个扫描器scanner = new ClassPathBeanDefinitionScanner(this);
* 扫描器scanner的用出不是很大,它仅仅是我们外部手动调用.scan()方法时才会用到的,常规方式是不会用到scanner
*/
this();
/** 注册Bean
* 把传入的类进行注册,这里会有两种方式,一种是传入配置类,一种是传入bean。
* 这里spring会把传统的带有@Configuration注解的类称之为FULL配置类,不带@Configuration注解但是带有@Component
* @Import@ImportResource@Server@ComponentScan 等注解的类称之为Lite配置类
* 我们这里先把带有@Configuration注解的称之为传统配置类,不带该注解的称之为普通类
*/
register(componentClasses);
/** 刷新 */
refresh();
}

在构造函数中调用了三个方法,下面一一说一下这三个方法都干了什么

this()这个方法调用了本类的无参数构造函数,其实在调用AnnotationConfigApplicationContext的无参数构造方法之前,先调用的父类的构造方法。在这些方法中主要是做了一些初始化的操作,比如初始化我们的容器beanDefinitionMap,初始化资源读取器AnnotationBeanDefinitionReader和资源扫描器BeanDefinitionScanner

register(componentClasses) 这个方法很重要;在这个方法中会扫描所有的Bean,并且会把扫描出来的Bean定义进行注册;Spring还会在这里把带有@Configuration注解的类进行标记为Full配置类,不带@Configuration注解的类但是有@Component@ComponentScan@Import@ImportSource@Server等注解的类标记为Lite配置类,然后被标记为Full的配置类会进行CGLIB代理增强处理。

Spring的循环依赖也是在这个过程中解决的,后面会详细看这个方法

refresh() 这个方法很重要,几乎Spring所有的启动动作都会在这个方法来完成的。在这里会准备Bean工厂,为Bean工厂设置一些依赖到的接口,也会添加后置处理器,注册类的后置处理器,注册Bean工厂后置处理器,解析@Import@Component注解、注册Bean后置处理器、初始化事件、实例化Bean等操作、发布初始化完成事件等操作;这个方法后面也会详细看

下面通过一个图来回顾总结一下整个流程:

Spring-IOC原理

  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!

请我喝杯咖啡吧~

支付宝
微信