从手动实现Spring IOC 到IOC原理剖析

什么是IOC?

IOC (Inverse of Control)即控制反转。IOC是一种抽象的概念,也就是一种思想。

那么IOC到底如何进行理解呢?

简单的说,它就是一个容器,比如水杯是一种装水的容器,油箱是一种装油的容器,那么IOC就是一种装Bean的容器,它是用来存储管理Bean的,IOC容器将类与类之间的依赖从代码中脱离出来也就是解耦,进行负责依赖类之间的创建、拼接、管理、获取等工作。

IOC又是如何进行装bean的呢?

是通过注解,配置文件来管理对象的依赖关系的。

那么它的最终目的是什么呢?

是来实现依赖注入的功能,也就是我们经常说的DI。

那么控制反转又该如何理解呢?怎么进行了控制又是如何反转呢?

控制反转就是将所有权交给别人管理,不是我们找别人,而是别人找我。在spring中就是我们将创建对象的权力交给IOC,让它去控制。举个例子来说吧,正常情况下我们在创建对象的时候,我们需要进行new 操作,但是我们在使用spring的时候,我们并没有去手动创建,而是将对象进行声明为Bean,IOC来控制Bean(对象)的创建销毁。

手动实现IOC

我们从手动实现IOC来进行剖析spring中IOC的实现,来了解相关内容以及原理

IOC是如何进行加载并创建管理起来的呢?

正常情况下我们通过new 进行创建对象,可是在使用IOC的时候我们仅仅是写了一个bean的xml配置,以及对象,那么我们如何从xml中的信息去找到并创建它呢?
这里先进行写了一段xml与汽车Car对象

application.xml 这里仅仅配置了一个car,一个name属性

1
2
3
4
5
6
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="car" class="top.wsylp.www.po.Car">
<property name="name" value="奥迪" />
</bean>
</beans>

Car.java

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
package top.wsylp.www.po;

public class Car {
private String color;

private String name;

private int wheelNum;

private String board;

public String getColor() {
return color;
}

public void setColor(String color) {
this.color = color;
}

public int getWheelNum() {
return wheelNum;
}

public void setWheelNum(int wheelNum) {
this.wheelNum = wheelNum;
}

public String getBoard() {
return board;
}

public void setBoard(String board) {
this.board = board;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

}

看到这两个我们就会想,既然知道类的路径(全限定名),以及属性,我们如何将这个文件加载到虚拟机的内存中呢,既然jdk在可以进行加载,那么我们可不可以进行手动让java进行加载呢?
是的,可以的,那么就需要用到类加载器ClassLoader
好了既然已经可以进行加载了,那么还有一个问题,我们如何拿到这个类并把name=”奥迪”的这个属性进行赋值呢?
用set方法,对不起我们拿不到对象的所属权,因为我们不知道这个类是什么,我们仅仅知道这个对象,我们要拿着这个对象找到类,那么我们可以进行翻墙进行处理,jdk为我们提供了反射功能。
到了这个时候我们就知道了如何去获取到car对象了。

IOC的过程便一目了然了
1、加载xml并进行解析;
2、获取bean中的节点信息;
3、进行加载对象,并通过反射进行赋值;
4、将对象进行返回;

既然四个步骤已经清晰了,那么我们进行通过代码实现:
首先是解析xml
首先在我们写测试类的时候,我们进行看一下测试类,在spring中我们写的测试类跟下面的像

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package top.wsylp.www;

import top.wsylp.www.ioc.ClassPathXmlApplicationContext;
import top.wsylp.www.po.Car;

public class IocTest {



public static void main(String[] args) throws Exception{
ClassPathXmlApplicationContext context=new ClassPathXmlApplicationContext("application.xml");

Car car = (Car) context.getBean("car");
System.out.println(car.getName());


}
}

从上面的代码中我们可以发现我们需要两个方法,进行解析xml,以及获取对象

ApplicationContext接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package top.wsylp.www.ioc;

/**
*
* @author wsylp
*
*/
public interface ApplicationContext {

/**
* 获取bean方法
* @return
*/
Object getBean(String beanName);
}
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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
package top.wsylp.www.ioc;

import java.io.File;
import java.lang.reflect.Field;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Iterator;
import java.util.List;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

/**
*
* @author wsylp
*
*/
public class ClassPathXmlApplicationContext implements ApplicationContext {

private CacheManager cacheManager;

public ClassPathXmlApplicationContext(String xml) throws URISyntaxException, DocumentException {

cacheManager = new CacheManager();
// 解析xml
// 进行加载配置文件
URL resource = this.getClass().getClassLoader().getResource(xml);

File configFile = new File(resource.toURI());
if (configFile.exists()) {
System.out.println("文件存在");
}

SAXReader reader = new SAXReader();
Document document = reader.read(configFile);
Element rootElement = document.getRootElement();
List<Element> beanElement = rootElement.elements();
Iterator<Element> iterator = beanElement.iterator();

while (iterator.hasNext()) {
Element element = iterator.next();
// 声明bean,将xml中配置的bean信息存放到Bean对象中
Bean bean = new Bean();

// 将bean中的属性提炼出公共的
bean.setBeanName(element.attributeValue(ApplicationProperty.Bean));
bean.setClassName(element.attributeValue(ApplicationProperty.BeanClass));

// 获取property属性
List<Element> properties = element.elements(ApplicationProperty.BeanProperty);

Property property = new Property();
// 遍历属性
Iterator<Element> properItear = properties.iterator();
while (properItear.hasNext()) {
Element prop = properItear.next();
String propertyName = prop.attributeValue(ApplicationProperty.BeanPropName);
property.setPropertyName(propertyName);

if (prop.attribute(ApplicationProperty.BaseProp) != null) {
property.setPropertyValue(prop.attributeValue(ApplicationProperty.BaseProp));
property.setReference(false);
property.setAutoWired(false);
bean.addProperty(property);
continue;
}
if (prop.attribute(ApplicationProperty.ReferenceProp) != null) {
property.setPropertyValue(prop.attributeValue(ApplicationProperty.ReferenceProp));
property.setReference(true);
property.setAutoWired(false);
bean.addProperty(property);
continue;
}
bean.addProperty(property);

}

cacheManager.addBean(bean);
}

}

@Override
public Object getBean(String beanName) {
// 从Cachemanager中取bean
Bean bean = cacheManager.getBean(beanName);

// 首先对类进行加载,使用classLoader进行装载
ClassLoader loader = Thread.currentThread().getContextClassLoader();
Object obj = null;
try {
// 进行加载类,其中参数为全限定名
Class loadClass = loader.loadClass(bean.getClassName());

// 进行实例化
obj = loadClass.newInstance();

// 利用反射进行对属性进行赋值

// 遍历hashmap的值
for (Property pro : bean.getProperties()) {
String propertyName = pro.getPropertyName();
String propertyValue = pro.getPropertyValue();

Field field = loadClass.getDeclaredField(propertyName);

//取消java语言访问检查以访问private方法
field.setAccessible(true);
field.set(obj, propertyValue);

}

} catch (ClassNotFoundException e) {
System.out.println("该类不存在");
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
}

return obj;

}

}

上面在解析xml的时候将标签进行了声明,济宁香港抽取规范出来

ApplicationProperty

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
package top.wsylp.www.ioc;

/**
* xml属性
* @author wsylp
*
*/
public class ApplicationProperty {

/**
* 配置文件中Bean元素名称
* 加载XML时依靠这个判断是否为一个Bean
*/
public final static String Bean = "id";

/**
* 配置文件中Bean的属性元素名称
* 加载XML时依靠这个判断是否为一个属性
*/
public final static String BeanProperty = "property";

/**
* 配置文件中Bean属性
* name表示Bean中一个属性的名称
*/
public final static String BeanName = "name";

/**
* 配置文件中Bean的类
* BeanClass表示Bean所关联类
*/
public final static String BeanClass = "class";

/**
* 配置文件中Bean属性
* BeanPropName表示属性值名称
*/
public final static String BeanPropName = "name";

/**
* 配置文件中Bean属性
* BaseProperty表示属性值“非引用类型”
*/
public final static String BaseProp= "value";

/**
* 配置文件中Bean属性
* ReferenceProp表示属性是一个引用类型
*/
public final static String ReferenceProp = "ref";

/**
* 配置XML中的静态资源
* Import指向当前类加载器路径下的静态资源
*/
public final static String Import = "import";

/**
* 配置XML中的静态资源
* Import元素中的属性元素
*/
public final static String ImportProperty = "value";

/**
* XML中配置扫包
* 只支持扫描当前类加载器路径下的包
*/
public final static String ComponentScan = "component-scan";

/**
* XML中配置扫包
* ComponentScan元素中的属性
*/
public final static String ComponentScanProp = "value";

/**
* TypeHandler使用的属性
* 处理器优先去配置文件中查找该属性
*/
public final static String ResourceProp = "\\$\\{(.*)\\}";

/**
* TypeHandler使用的属性
* bool值的表示
*/
public final static String True = "true";

/**
* TypeHandler使用的属性
* bool值的表示
*/
public final static String False = "false";
}

同时在进行解析完成之后我们将xml中定义的信息及存放到Bean对象中,然后放在缓存中,这样的话在JAVA EE中进行解析xml一次就行了。

CacheManager

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

package top.wsylp.www.ioc;

import java.util.HashMap;
import java.util.Map;

/**
* 缓存管理
* @author wsylp
*
*/
public class CacheManager {

private static Map<String,Bean> beanCache;


{
beanCache = new HashMap<String, Bean>();

}

/**
* 判断是否已经存在
* @param bean
* @return
*/
public boolean containBean(String bean) {
return beanCache.containsKey(bean);
}

/**
* 将bean对象存放在缓存中
* @param bean
*/
public void addBean(Bean bean) {
if(beanCache.containsKey(bean.getBeanName())) {
System.out.println("Bean容器不可重复添加"+bean.getBeanName());
}
beanCache.put(bean.getBeanName(),bean);
}

/**
* 从缓存中获取bean
* @param beanName
* @return
*/
public Bean getBean(String beanName) {
if(!beanCache.containsKey(beanName)) {
System.out.println("Bean容器未找到"+beanName);
}
return beanCache.get(beanName);
}
}

存储bean配置文件的对象
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
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

package top.wsylp.www.ioc;

import java.util.ArrayList;
import java.util.List;

/**
* 将xml中的配置文件存到bean中
* @author wsylp
*
*/
public class Bean {

private String beanName;//xml中定义的唯一id
private String className;//对应的class标签

//存放bean的属性
private List<Property> properties;//属性标签
private boolean AutoWired;//自动注入
private String initMethod;//初始化方法
private String destoryMethod;//容器销毁时的方法

{
properties = new ArrayList<Property>();
AutoWired = false;
}

public boolean isAutoWired() {
return AutoWired;
}

public void setAutoWired(boolean autoWired) {
AutoWired = autoWired;
}

public String getBeanName() {
return beanName;
}

public void setBeanName(String beanName) {
this.beanName = beanName;
}

public List<Property> getProperties() {
return properties;
}

public void addProperty(Property property) {
properties.add(property);
}

public String getClassName() {
return className;
}

public void setClassName(String className) {
this.className = className;
}



public String getInitMethod() {
return initMethod;
}

public void setInitMethod(String initMethod) {
this.initMethod = initMethod;
}

public void setProperties(List<Property> properties) {
this.properties = properties;
}

public String getDestoryMethod() {
return destoryMethod;
}

public void setDestoryMethod(String destoryMethod) {
this.destoryMethod = destoryMethod;
}

@Override
public int hashCode() {
if(beanName == null) {
return super.hashCode();
}
return beanName.hashCode();
}

@Override
public boolean equals(Object obj) {
if(obj == this) {
return true;
}
if(obj instanceof Bean) {
Bean bean = (Bean)obj;
if(bean.getBeanName().equals(beanName)) {
return true;
}
}
return false;
}
}

Bean对象中的属性
Property

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
package top.wsylp.www.ioc;

/**
* bean配置文件中的属性
* @author wsylp
*
*/
public class Property {

private String propertyName;// 属性名称
private String propertyValue;// 属性值
private boolean isReference;// 是否引用
private boolean isAutoWired;// 是否自动注入

public String getPropertyValue() {
return propertyValue;
}

public void setPropertyValue(String propertyValue) {
this.propertyValue = propertyValue;
}

public boolean isReference() {
return isReference;
}

public void setReference(boolean reference) {
isReference = reference;
}

public boolean isAutoWired() {
return isAutoWired;
}

public void setAutoWired(boolean isAutoWired) {
this.isAutoWired = isAutoWired;
}

public String getPropertyName() {
return propertyName;
}

public void setPropertyName(String propertyName) {
this.propertyName = propertyName;
}
}

ok,现在最简单的IOC已经实现了,运行测试类进行测试吧
测试结果如下

1
2
文件存在
奥迪

虚拟机的类加载机制

虚拟机的类加载机制是什么?

高富帅的理解:虚拟机把描述类的数据从Class文件中加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的java类型。
屌丝的理解:现在给你一辆大奔,你把这个大奔进行车辆管理所登记拍照(加载到车辆管理所数据库中–对应内存),车辆管理所对你的大奔进行检验,发你行驶证(相当于解析),上牌照(初始化),然后这辆车就可以上路了。

本文标题:从手动实现Spring IOC 到IOC原理剖析

文章作者:wsylp

发布时间:2018年12月03日 - 21:12

最后更新:2020年01月02日 - 10:01

原始链接:http://wsylp.top/2018/12/03/从手动实现Spring-IOC-到IOC原理剖析/

许可协议: 本文为 wsylp 版权所有 转载请保留原文链接及作者。

-------------本文结束感谢阅读-------------