前言

ioc容器其实就是一个创建对象的工厂。那么,要实现一个ioc容器,首先要知道它创建对象的步骤流程是什么。然后根据这些步骤,手写出一个ioc容器。

一、spring ioc容器创建对象的步骤是什么

1.找到被@ComponentScan注解修饰的配置类,获取到要扫描的包路径名
2.找到该包名下,所有被@Component注解修饰的类,遍历这些类
3.如果该类实现了后处理器,实例化该类的一个对象,并将该对象放入后处理器列表中。
4.获取到该类的名字,如果为空就取默认名。
5.将该类的信息(单例还是多例,class信息),用bean定义对象封装起来。
6.将bean定义对象放入beandefinitionmap中。
7.循环遍历beandefinitionmap,如果bean定义对象是单例的,创建一个bean对象,并将该对象放入单例池中。
8.创建bean对象的过程,首先获取到bean定义对象中定义的bean class信息,通过该class的构造方法,实例化一个对象,可以理解为早期的bean。
9.然后对该bean进行依赖注入,判断该bean的哪些属性被Autowired属性修饰过。
10 Aware回调,判断该类是否实现了aware接口,实现了的话则运行重写后的方法。
11.遍历后处理器列表,运行后处理器列表中所有初始化前方法。
12.判断该类是否实现了初始化接口,实现了则运行对象的方法。
13.遍历后处理器列表,运行后处理器列表所有初始化后方法,如果初始化后方法中使用了AOP,则获取到对应的代理对象。并将代理对象放入单例池中。
上述就是springioc创建对象的全部步骤,我做个简单概括,先创建一个普通对象,然后对该对象进行依赖注入,接着先后运行初始化前、初始化、初始化后方法,将最终获得的成体bean放入单例池中。

二、实现ioc容器需要做的前期准备

下面我们就根据上述步骤实现一个ioc容器

1.需要的注解

@Autowired注解,用于依赖注入。

@Rentention注解和@Target注解分别的作用是可以通过反射获取该注解和该注解只能修饰方法。

怎么获取容器ID 获取ioc容器_java


@ComponentScan注解,告诉ioc容器要扫哪个包下的文件

怎么获取容器ID 获取ioc容器_怎么获取容器ID_02


@Component注解,告诉ioc容器哪些类需要实例化成bean对象

怎么获取容器ID 获取ioc容器_java_03


@Scope注解,记录该bean是单例还是多例

怎么获取容器ID 获取ioc容器_mybatis_04

2.需要用到的接口

BeanNameAware接口,用于aware回调

怎么获取容器ID 获取ioc容器_初始化_05


BeanPostProcessor接口,用于初始化前和初始化后

怎么获取容器ID 获取ioc容器_java_06

InitializingBean接口,用于初始化

怎么获取容器ID 获取ioc容器_怎么获取容器ID_07

3.需要用到的类

BeanDefinition,bean定义对象,用于封装bean信息

怎么获取容器ID 获取ioc容器_spring_08

三、实现ioc容器

做完了上述前期准备工作,接下来开始实现ioc容器。ioc容器其实就是一个类。我们给它取名叫TestApplicationContext。

1.TestApplicationContext的成员变量

首先要有一个class类型的属性,作用是获取到配置类,即被ComponentScan修饰的那个类。其次需要两个map,一个存放bean定义对象,另一个存放bean对象,就是所谓的单例池。还需要有一个列表,用于存放后处理器。

怎么获取容器ID 获取ioc容器_初始化_09

2.TestApplicationContext的方法

需要一个有参构造方法,穿入配置类,并在构造方法中实现步骤1-7。其次需要一个创建bean对象的方法,用于实现步骤8到13。最后需要一个获取到bean对象的方法。有了这三个方法,一个ioc容器就实现了。

3.TestApplicationContext方法详解

接下来会根据第一大段中每一步的内容进行代码的编写。

3.1 找到被@ComponentScan注解修饰的配置类,获取到要扫描的包路径名

怎么获取容器ID 获取ioc容器_spring_10

3.2 遍历包路径下所有类,如果该类被Component注解修饰,且实现了后处理器接口,将该类实例后的对象放入后处理器列表中

怎么获取容器ID 获取ioc容器_怎么获取容器ID_11

3.3 遍历包路径下所有类,如果该类被Component注解修饰,将该类对应的bean定义对象放入bean定义列表中。

怎么获取容器ID 获取ioc容器_mybatis_12

3.4 循环遍历beandefinitionmap,如果bean定义对象是单例的,创建一个bean对象,并将该对象放入单例池中。

怎么获取容器ID 获取ioc容器_怎么获取容器ID_13

3.5 上一步中提到了创建一个bean对象,那么bean对象具体是怎么创建的。首先获取到bean定义对象中定义的bean class信息,通过该class的构造方法,实例化一个对象

怎么获取容器ID 获取ioc容器_怎么获取容器ID_14

3.6 对于被Autowired修饰过的属性进行依赖注入。

怎么获取容器ID 获取ioc容器_mybatis_15

3.7 依次运行aware回调、初始化前、初始化、初始化后。

怎么获取容器ID 获取ioc容器_spring_16

3.8 最后测试一下ioc容器。

怎么获取容器ID 获取ioc容器_初始化_17


怎么获取容器ID 获取ioc容器_初始化_18


测试成功。

总结

以上便是模拟一个ioc容器的全部代码。由于步骤较多,我简要概括一下。首先根据配置类生成bean定义对象,然后根据bean定义对象生成初始对象。初始对象经过aware回调、初始化前、初始化、初始化后生成的最终对象放入单例池中。之后再需要用到该对象时,直接从单例池中取即可。如果该对象不是单例的,是原型的,则每次使用都需要按上述流程创建一个新的对象,效率较低。故spring默认对象是单例模式。