springIOC---1、手工简单实现基于xml的IOC框架

如何自己实现一个IOC容器

……这里我并不想多做关于spring的相关介绍,大家可自行去官网了解相关内容,我们直接进入正题,假如让你自己去做开发,并且spring开始收取高额的费用了,这里只是假设,你该如何去开发?思考一分钟……稍微有点框架基础的人应该都会说自己开发一个IOC框架进行容器管理不就得了,确实,容器管理是本文的重点,今天就来讲解一下第一种方式,通过xml文件的方式来实现IOC容器。

代码结构

……整体的代码结构如下图,下面就逐一解析
图p1

**代码结构**

入口类TestMain

1
2
3
4
5
6
7
8
9
10
11
12
package com.hk.springioc;

public class TestMain {
public static void main(String[] args) {
MyXmlClassPathApplicationContext fac = new MyXmlClassPathApplicationContext("myspring.xml");
Person bean1 = (Person) fac.getBean("person");
Person bean2 = (Person) fac.getBean("person");
bean1.printAll();
bean2.printAll();
System.out.print(bean1 == bean2 ? "同一个对象" : "非同一个对象");
}
}

整个程序的main方法中我们new了一个MyXmlClassPathApplicationContext这样的一个对象,这也是整个稍后IOC构建过程中的核心类,介绍该类之前,先说明两个接口
ApplicationContext用于IOC容器构建

1
2
3
4
5
package com.hk.springioc;

public interface ApplicationContext {
public void springIOC();
}

BeanFactory用于对象实例管理与获取

1
2
3
4
5
package com.hk.springioc;

public interface BeanFactory {
public Object getBean(String name);
}

MyXmlClassPathApplicationContext

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
package com.hk.springioc;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

public class MyXmlClassPathApplicationContext implements BeanFactory,ApplicationContext {

private ConfigManager configManager;

private Map<String,Bean> beanContainer;

private Map<String,Object> instanceContainer ;

public MyXmlClassPathApplicationContext(String path){
configManager = new ConfigManager();
instanceContainer = new HashMap<String,Object>();
beanContainer = configManager.parseXML(path);
springIOC();
}



//依賴注入的關鍵
@Override
public void springIOC() {
Set<Entry<String, Bean>> entrySet = beanContainer.entrySet();
for(Entry<String, Bean> entry : entrySet){
// String name = entry.getKey();
Bean bean = entry.getValue();
Object instance = createBean(bean);
// instanceContainer.put(name, instance);
}
}

private Object createBean(Bean bean) {
if(instanceContainer.get(bean.getName())!=null){
return instanceContainer.get(bean.getName());
}
String className = bean.getClassName();
List<Property> props = bean.getProps();
Object newInstance = null;
try {
Class<?> forName = Class.forName(className);
newInstance = forName.newInstance();
for(Property pro : props){
String name = pro.getName();
Field field = forName.getDeclaredField(name);
field.setAccessible(true);
if(pro.getRef()!=null){
String ref = pro.getRef();
Object existBean = getBean(ref);
if(existBean==null){
Object createBean = createBean(beanContainer.get(ref));
field.set(newInstance,createBean);
}else{
field.set(newInstance,existBean);
}
}else{
Class<?> type = field.getType();
if(type==int.class){
field.set(newInstance,Integer.parseInt(pro.getValue()));
}else{
field.set(newInstance,pro.getValue());
}

}
}
if(bean.getSingleton().equals("1")){
instanceContainer.put(bean.getName(), newInstance);
}

} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
}
return newInstance;
}



@Override
public Object getBean(String name) {
return instanceContainer.get(name);
}

}

解释一下这个类主要做了什么:
1、new了一个ConfigManager对象
2、构建了一个HashMap类型的实例化容器instanceContainer
3、用第一步的ConfigManager对象解析XML文件,获取一个Bean容器,这里的Bean就是XML文件中配置的相关bean信息,后面会有一个类进行封装
4、对Bean容器进行解析,构建最终的IOC实例容器,也就是第二步中的instanceContainer
下面就是xml文件以及对应封装类Bean

myspring.xml

配置文件结构如下

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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean name="person" class="com.hk.springioc.Person">
<property name="boy" ref="boy" />
<property name="girl" ref="girl" />
</bean>

<bean name="boy" class="com.hk.springioc.Boy">
<property name="age" value="2" />
<property name="name" value="lilei" />
</bean>

<bean name="girl" class="com.hk.springioc.Girl">
<property name="age" value="1" />
<property name="name" value="lily" />
</bean>
</beans>

封装Person信息,依赖注入Boy和Girl
package com.hk.springioc;

public class Person {
private Boy boy;
private Girl girl;
public Boy getBoy() {
return boy;
}
public void setBoy(Boy boy) {
this.boy = boy;
}
public Girl getGirl() {
return girl;
}
public void setGirl(Girl girl) {
this.girl = girl;
}

public Person(){

}
public void printAll(){
System.out.println(boy.getName()+":"+boy.getAge());
System.out.println(girl.getName()+":"+girl.getAge());
}
}

类封装girl信息
package com.hk.springioc;

public class Girl {
private String name;
private int age;

public String getName() {
return name;
}

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

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public Girl(){

}
public void printAll(){
System.out.println(name+":"+age);
}
}

封装boy的信息
package com.hk.springioc;

public class Boy {

private String name;
private int age;

public String getName() {
return name;
}

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

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public Boy(){

}
public void printAll(){
System.out.println(name+":"+age);
}
}

类封装xml中的bean标签信息
package com.hk.springioc;

import java.util.List;

public class Bean {
private String name;
private String className;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public String getSingleton() {
return singleton;
}
public void setSingleton(String singleton) {
this.singleton = singleton;
}
public List<Property> getProps() {
return props;
}
public void setProps(List<Property> props) {
this.props = props;
}
private String singleton = "1";
private List<Property> props;
}

类封装xml文件中的property信息

package com.hk.springioc;

public class Property {
private String name;
private String value;
private String ref;

public String getName() {
return name;
}

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

public String getValue() {
return value;
}

public void setValue(String value) {
this.value = value;
}

public String getRef() {
return ref;
}

public void setRef(String ref) {
this.ref = ref;
}

}

ConfigManager

我们可以使用dom4j或者SAXReader对xml文件进行解析,构建需要的Bean容器,这里需要重点说明一点!!!我们的xml配置文件中没有特殊说明的bean都是singleton,也只有单例的对象才需要IOC容器进行管理,非单例的对象IOC容器是不会管理的

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
package com.hk.springioc;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

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

/**
*
* @ClassName: ConfigManager
* @Description: TODO(这里用一句话描述这个类的作用)
* @author A18ccms a18ccms_gmail_com
* @date 2019年4月9日 下午5:33:23
* 解析xml組裝Bean容器
*
*/
public class ConfigManager {

public Map<String,Bean> parseXML(String path){
Map<String,Bean> map = new HashMap<String,Bean>();
InputStream is = this.getClass().getResourceAsStream(path);
SAXReader reader = new SAXReader();
try {
Document doc = reader.read(is);
List<Element> selectNodes = doc.selectNodes("//bean");
for(Element ele : selectNodes){
String nameValue = ele.attributeValue("name");
String classValue = ele.attributeValue("class");
String singtonValue = ele.attributeValue("singleton");
Bean bean = new Bean();
bean.setName(nameValue);
bean.setClassName(classValue);
if(singtonValue!=null){
bean.setSingleton(singtonValue);
}
List<Property> list = new ArrayList<Property>();
List<Element> pros = ele.elements("property");
for(Element pro : pros){
Property prop = new Property();
prop.setName(pro.attributeValue("name"));
prop.setValue(pro.attributeValue("value"));
prop.setRef(pro.attributeValue("ref"));
list.add(prop);
}
bean.setProps(list);
map.put(bean.getName(), bean);
}
} catch (DocumentException e) {
e.printStackTrace();
}


return map;
}

}

最后运行TestMain

查看最终运行结果:
图p2

**运行结果**

总结

……好了,写到这里我们就简单的实现了一个基于xml配置文件方式的IOC容器,原理其实很简单,只需要你了解reflect以及java基础,难的在于实现,spring框架通过各种设计模式实现了方便大家使用的最终发版,这里我不多做讲解,因为本人也在研究和学习当中,设计模式也是本人的缺陷,本篇文章重点只是向大家介绍一个最基本的实现原理,理解这个原理对于以后的开发会有很大的帮助,下一篇文章中我们再来讨论一下基于annotation的IOC容器构建…谢谢