在使用 IoC 容器时,我们需要先配置容器,包括注册需要管理的对象、配置对象之间的依赖关系以及对象的生命周期等。

然后,IoC 容器会根据这些配置来动态地创建对象,并把它们注入到需要它们的位置上。

当我们使用 IoC 容器时,需要将对象的配置信息告诉 IoC 容器,这个过程叫做依赖注入(DI),而 IoC 容器就是实现依赖注入的工具。

因此,理解 IoC 容器就是理解它是如何管理对象,如何实现 DI 的过程。

以下几个大点,则是在实现方案时的流程与难点。

从XML读取Bean配置

首先是要读取xml,这部分内容其实就是 ClassPathXmlApplicationContext类的构造函数。

1
2
//1.加载 XML 配置文件
Resource res = new ClassPathXmlResource(fileName);

ClassPathXmlResource中,使用了SAXReader插件,可以更好的读取xml结构的文档。

1
2
3
4
5
6
7
8
9
10
11
12
public ClassPathXmlResource(String fileName) {
SAXReader saxReader = new SAXReader();
URL xmlPath = this.getClass().getClassLoader().getResource(fileName);
//将配置文件装载进来,生成一个迭代器,可以用于遍历
try {
this.document = saxReader.read(xmlPath);
this.rootElement = document.getRootElement();
this.elementIterator = this.rootElement.elementIterator();
} catch (Exception e) {
throw new RuntimeException(e);
}
}

然后再声明:

1
2
3
4
5
6
//2.创建一个 DefaultListableBeanFactory 实例
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
//3.创建一个 XmlBeanDefinitionReader 实例,并传入 BeanFactory
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(bf);
// 4.使用 XmlBeanDefinitionReader 加载 Bean 定义到 BeanFactory
reader.loadBeanDefinitions(res);

XmlBeanDefinitionReader中,会循环判断 while (res.hasNext())

是否有bean ? 若有,是 constructor-arg 还是 property类型?

依次将bean的 id 、 class,以及其中的 type 、name 、value 、ref(property类型)存储到 ConstructorArgumentValuesPropertyValues 中,最后汇总存入beanDefinition中。

这样,就实现了bean的配置的读取。主要是两步 :

  1. 读取xml ,使用ClassPathXmlResource,主要依赖SAXReader类
  2. 根据处理完的xml结果读取 bean配置,主要是遍历循环 Element element = (Element)res.next();中的元素,来将配置存入 beanDefinition

实现依赖注入

实现依赖注入这个时机,就是在createBean的时候。

传入的是BeanDefinition,通过doCreateBean()handleProperties()两个函数,分别 先后对Constructor方法和Property方法 进行依赖注入。

分两阶段的原因就是为了解决循环依赖问题。

依赖注入的具体方法,是通过反射:

  1. 获取bean类型 : clz = Class.forName(bd.getClassName());
  2. 获取构造器参数 : ConstructorArgumentValues argumentValues = bd.getConstructorArgumentValues(); ,遍历这些argumentValues,可以获得一个 paramTypes列表 和 paramValues列表 。
  3. 获取类的构造函数 : con = clz.getConstructor(paramTypes); 。通过调用 clz.getConstructor(paramTypes),你可以获得一个与这些参数类型匹配的构造函数。
  4. 创建类的实例:obj = con.newInstance(paramValues); con.newInstance() 方法允许你使用构造函数来创建类的实例
  5. 获取 property标签的参数名、类型、值。,得到paramValues列表。
  6. 通过拼接属性名生成相应的 setter 方法名 ,String methodName = "set" + pName.substring(0,1).toUpperCase() + pName.substring(1);
  7. 通过反射获取 setter 方法,method = clz.getMethod(methodName, paramTypes);
  8. 执行方法,实现依赖注入, method.invoke(obj, paramValues);
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
    private Object createBean(BeanDefinition bd) {
Class<?> clz = null;
Object obj = doCreateBean(bd);

this.earlySingletonObjects.put(bd.getId(), obj);

try {
clz = Class.forName(bd.getClassName());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}

populateBean(bd, clz, obj);

return obj;
}

private Object doCreateBean(BeanDefinition bd) {
Class<?> clz = null;
Object obj = null;
Constructor<?> con = null;

try {
clz = Class.forName(bd.getClassName());

//handle constructor
ConstructorArgumentValues argumentValues = bd.getConstructorArgumentValues();
if (!argumentValues.isEmpty()) {
Class<?>[] paramTypes = new Class<?>[argumentValues.getArgumentCount()];
Object[] paramValues = new Object[argumentValues.getArgumentCount()];
for (int i=0; i<argumentValues.getArgumentCount(); i++) {
ConstructorArgumentValue argumentValue = argumentValues.getIndexedArgumentValue(i);
if ("String".equals(argumentValue.getType()) || "java.lang.String".equals(argumentValue.getType())) {
paramTypes[i] = String.class;
paramValues[i] = argumentValue.getValue();
}
······
}
try {
con = clz.getConstructor(paramTypes);
obj = con.newInstance(paramValues);
}
}
else {
obj = clz.newInstance();
}

}
System.out.println(bd.getId() + " bean created. " + bd.getClassName() + " : " + obj.toString());

return obj;

}

/**
* 给bean输入数据(注入依赖),类似于handle constructor
* @param bd
* @param clz
* @param obj
*/
private void populateBean(BeanDefinition bd, Class<?> clz, Object obj) {
handleProperties(bd, clz, obj);
}
private void handleProperties(BeanDefinition bd, Class<?> clz, Object obj) {
//handle properties
System.out.println("handle properties for bean : " + bd.getId());
PropertyValues propertyValues = bd.getPropertyValues();
if (!propertyValues.isEmpty()) {
for (int i=0; i<propertyValues.size(); i++) {
PropertyValue propertyValue = propertyValues.getPropertyValueList().get(i);
String pName = propertyValue.getName();
String pType = propertyValue.getType();
Object pValue = propertyValue.getValue();
boolean isRef = propertyValue.getIsRef();
Class<?>[] paramTypes = new Class<?>[1];
Object[] paramValues = new Object[1];
//非引用的情况
if (!isRef) {
if ("String".equals(pType) || "java.lang.String".equals(pType)) {
paramTypes[0] = String.class;
}
······
else {
paramTypes[0] = String.class;
}

paramValues[0] = pValue;
}
//是引用的情况
else { //is ref, create the dependent beans
try {
paramTypes[0] = Class.forName(pType);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try {
paramValues[0] = getBean((String)pValue);
} catch (BeansException e) {
e.printStackTrace();
}
}
//通过拼接属性名生成相应的 setter 方法名
String methodName = "set" + pName.substring(0,1).toUpperCase() + pName.substring(1);

Method method = null;
try {
//通过反射获取 setter 方法
method = clz.getMethod(methodName, paramTypes);
} c
try {
//执行方法,实现依赖注入
method.invoke(obj, paramValues);
}
}
}

}

解决依赖循环

在某个 Bean 需要注入另一个 Bean 的时候,如果那个 Bean 还不存在,该怎么办?

Spring是三层缓存解决依赖循环,这里使用了两层 。

主要代码体现在AbstractBeanFactory类中,此类实现了循环依赖问题,体现在**getBean(), createBean()* *和* *doCreateBean()* *三个方法中。

  1. createBean()* 方法中调用了一个 doCreateBean(bd) *方法,专门负责创建早期的毛胚实例,毛坯中包含了这个bean的constructor参数。
  2. 毛胚实例创建好后会放在 earlySingletonObjects 结构中,然后 createBean() 方法再调用 handleProperties() 补齐这些 property *的值。
  3. getBean() 方法中,首先要判断有没有已经创建好的 *bean**,
  4. *** -*有的话直接取出来,如果没有就检查 earlySingletonObjects 中有没有相应的毛胚 Bean
  5. -在毛胚中有的话直接取出来,没有的话就去创建,
  6. *** -*并且会根据 Bean 之间的依赖关系把相关的 Bean 全部创建好。

所谓的三级缓存:

  1. singletonObjects:用于存储完全创建好的单例bean实例。
  2. earlySingletonObjects:用于存储早期创建但未完成初始化的单例bean实例。即毛坯
  3. singletonFactories:用于存储创建单例bean实例的工厂对象。(miniSpring没有这一层缓存,这一层体现在 new instance操作 obj = clz.newInstance();,只不过没有用factory封装。)

支持注解

首先要创造一个注解类,叫做 @Autowired,其逻辑如以下代码 :

  1. 判断这个bean是否有 @Autowired注解:boolean isAutowired = field.isAnnotationPresent(Autowired.class);
  2. 如果有的话,则通过 getBean() 方法从 Bean 工厂中获取对应的依赖对象,字段的名字被用作 Bean 的名称。
  3. 使用反射将依赖对象注入到字段中。首先将字段设置为可访问状态,然后使用 field.set(bean, autowiredObj) 将依赖对象设置到 Bean 中。
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
26
27
28
29
30
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
Object result = bean;

Class<?> clazz = bean.getClass();
//Field 类型表示一个类的字段(成员变量)。
Field[] fields = clazz.getDeclaredFields();
if(fields!=null){
for(Field field : fields){
boolean isAutowired = field.isAnnotationPresent(Autowired.class);
if(isAutowired){
String fieldName = field.getName();
Object autowiredObj = this.getBeanFactory().getBean(fieldName);
try {
field.setAccessible(true);
//set(Object obj, Object value): 设置指定对象上的字段值。需要提供一个对象作为参数,如果字段是静态的,可以传递 null。
field.set(bean, autowiredObj);
System.out.println("autowire " + fieldName + " for bean " + beanName);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}

}
}
}

return result;
}

构建体系

增强BeanFactory的扩展性

  • ListableBeanFactory
  • ConfigurableBeanFactory
  • ConfigurableListableBeanFactory:AutowireCapableBeanFactory、ListableBeanFactory、ConfigurableBeanFactory
  • 设计原则: interface segregation (接口隔离原则)

增加环境因素

  • 意义:使一些容器整体所需要的属性有个地方存储访问
  • core目录–>新建env目录 :
    • 增加PropertyResolver.java接口
    • 增加EnvironmentCapable.iava接口
    • 增加Environment.java接口

loC引擎: DefaultListableBeanFactory

  • 继承了其他BeanFactory类来实现bean的创建管理功能
  • ClassPathXmlApplicationContext运行时,注入DefaultListableBeanFactory

完善事件的发布与监听

  • ApplicationEvent
  • ApplicationListener
  • ApplicationEventPublisher
  • ContextRefreshEvent
  • 实现一个最简单的事件发布者SimpleApplicationEventPublisher