目录
  1. 1. 深入理解spring–手写简易的IOC容器
    1. 1.1. 容器要实现的功能
    2. 1.2. 所需技术
    3. 1.3. 所需 jar 包:
    4. 1.4. 项目结构图及介绍:
    5. 1.5. 代码部分
      1. 1.5.1. applicationContext.xml
      2. 1.5.2. Bean.java
      3. 1.5.3. Property.java
      4. 1.5.4. Man.java
      5. 1.5.5. User.java
      6. 1.5.6. People.java
      7. 1.5.7. BeanFactory.java
      8. 1.5.8. ApplicationContext.java
      9. 1.5.9. ClassPathXmlApplicationContext.java
      10. 1.5.10. ParseXML.java
      11. 1.5.11. Test.java
深入理解spring--动手搭建IOC容器

深入理解spring–手写简易的IOC容器

  • 使用过 spring 之后,发现很多东西都不用自己 new 很神奇,这些对象都是怎么来的呢?
  • IOC控制反转是怎么实现的呢?

spring IOC 容器可以在对象生成或初始化的时候就把属性注入到对象中,如果对象 A 的一个属性为对象 B ,还可以将对象 B 的引用注入到对象 A 的数据域中。如果对象 A 在初始化的时候对象 B 还没有初始化,而 A 又需要 B 作为自己的属性,这时 IOC 容器就会递归的先实例化对象 B ,这样也可以把对象之间的依赖清晰的建立起来。

IOC容器把对象的创建工作从具体的业务对象手中抢过来,这样也达到了解耦的目的,开发更加方便。

项目 github 地址(建议加星,当然我只是建议):https://github.com/Archivev/MyIOC

  • 简单的类结构图:

image-20200201091027360

  • 这里 ApplicationContext 里并没有任何方法,只是模仿 spring 继承了 BeanFactory,真正的 spring 结构要更加复杂,这里只是梳理一下大致关系

容器要实现的功能

  • 可以正确的解析xml文件,并将对象的实例创建出来
  • 借助 BeanUtils 可以将属性值注入到对象实例中
  • 可以设置 Scope 作用域为 singleton(单例模式)、prototype(多例模式)
  • 可以实现对象类型的注入 ref

所需技术

  • dom4j 用来解析 xml 配置文件
  • BeanUtils 用来帮助我们对对象注入属性
  • Java反射机制

所需 jar 包:

项目结构图及介绍:

image-20200201093042996

代码部分

applicationContext.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="utf-8"?>
<beans>
<bean id="people" class="com.Archiver.entity.People" >
<property name="name" value="张三"/>
<property name="sex" value="男"/>
</bean>
<bean id="user" class="com.Archiver.entity.User">
<property name="people" ref="people"/>
<property name="userId" value="11"/>
</bean>
<bean id="man" class="com.Archiver.entity.Man">
<property name="age" value="123"/>
<property name="people" ref="people"/>
</bean>
</beans>

Bean.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
45
46
47
48
49
50
51
52
public class Bean {

private String name;

private String className;

private String scope="singleton";

private List<Property> properties = new ArrayList<>();

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 getScope() {
return scope;
}

public void setScope(String scope) {
this.scope = scope;
}

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

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

@Override
public String toString() {
return "Bean{" +
"name='" + name + '\'' +
", className='" + className + '\'' +
", scope='" + scope + '\'' +
", properties=" + properties +
'}';
}
}

Property.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
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;
}

@Override
public String toString() {
return "Property{" +
"name='" + name + '\'' +
", value='" + value + '\'' +
", ref='" + ref + '\'' +
'}';
}
}

Man.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
public class Man {
private People people;
private String age;

public People getPeople() {
return people;
}

public void setPeople(People people) {
this.people = people;
}

public String getAge() {
return age;
}

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

@Override
public String toString() {
return "Man{" +
"people=" + people +
", age='" + age + '\'' +
'}';
}
}

User.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
public class User {

private String userId;

private People people;

public String getUserId() {
return userId;
}

public void setUserId(String userId) {
this.userId = userId;
}

public People getPeople() {
return people;
}

public void setPeople(People people) {
this.people = people;
}

@Override
public String toString() {
return "User{" +
"userId='" + userId + '\'' +
", people=" + people +
'}';
}
}

People.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
public class People {

private String name;

private String sex;

public String getName() {
return name;
}

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

public String getSex() {
return sex;
}

public void setSex(String sex) {
this.sex = sex;
}

@Override
public String toString() {
return "People{" +
"name='" + name + '\'' +
", sex='" + sex + '\'' +
'}';
}
}

BeanFactory.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
public interface BeanFactory {

/**
* 获取容器中的bean实例
* @return
*/
Object getBean(String name);

/**
* 是否是单例模式
* @return
*/
boolean isSingleton(String name);

/**
* 是否是多例模式
* @return
*/
boolean isPrototype(String name);

/**
*
* 是否包含该bean
* @return
*/
boolean containsBean(String name);
}

ApplicationContext.java

1
2
3
4
public interface ApplicationContext extends BeanFactory {


}

ClassPathXmlApplicationContext.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
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
public class ClassPathXmlApplicationContext implements ApplicationContext{

//获取解析到信息的map
private Map<String, Bean> map;

//作为IOC容器使用,放置反射生成的对象
private Map<String,Object> context = new HashMap<>();

public ClassPathXmlApplicationContext(String pathName){
//读取配置文件得到初试化的Bean的信息
map = ParseXML.getConfig(pathName);

//通过entrySet方法,返回一个储存map映射关系的set集合,分别取 key 和 value
for (Map.Entry<String,Bean> e: map.entrySet()) {
String beanName = e.getKey();
Bean bean = e.getValue();

Object exsitBean = context.get(beanName);
//如果容器中没有而且作用域为单例时则创建对象
if (exsitBean == null && "singleton".equals(bean.getScope())){
//根据bean信息创建一个实例
exsitBean = createBean(bean);
//并将实例放入容器中
context.put(beanName,exsitBean);
}
}
}

public Object createBean(Bean bean){
Class clazz = null;
try{
clazz = Class.forName(bean.getClassName());
}catch (ClassNotFoundException e){
e.printStackTrace();
throw new RuntimeException("没有找到该类");
}
Object object = null;
try {
object = clazz.newInstance();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("没有提供无参构造器");
}
//BeanUtils注入属性的map
Map<String,String> pmap = new HashMap<>();

for (Property p: bean.getProperties()) {
String name = p.getName();
String value = p.getValue();
String ref = p.getRef();
if (value != null){
pmap.put(name,value);

//利用Apache的BeanUtils注入属性
try{
BeanUtils.populate(object,pmap);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("请检查你的" + name + "属性");
}
}
if (ref != null){
Object exsitBean = context.get(ref);
/**
* 首先寻找容器中是否有 ref 的对象
* 如果有则不用再次创建,如果没有则递归的先 ref 的对象创建
*/
if (exsitBean == null){
//从xml解析的map中取出bean并创建
exsitBean = createBean(map.get(ref));
}
try {
BeanUtils.setProperty(object,name,exsitBean);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("您的bean的属性" + name + "未设置set方法");
}
}
}
return object;
}

@Override
public Object getBean(String name) {
Object o = context.get(name);
//如果为空则证明容器内没有,且为多例模式直接创建
//也就是说单例模式下,初始化容器时直接创建,多例等到调用时在创建
if (o == null) {
o = createBean(map.get(name));
}
return o;
}

@Override
public boolean isSingleton(String name) {
Bean bean = map.get(name);
return Objects.equals(bean.getScope(),"singleton");
}

@Override
public boolean isPrototype(String name) {
Bean bean = map.get(name);
return Objects.equals(bean.getScope(),"prototype");
}

@Override
public boolean containsBean(String name) {
Object o = map.get(name);
if (o != null){
return true;
}else {
return false;
}
}
}

ParseXML.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
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
public class ParseXML {

//模拟IOC容器
/**
* map<String,Bean>结构
* String 用来模拟 bean 中的 id 值
* Bean 模拟实际 IOC 容器中的 BeanDefinition
*/
private static Map<String,Bean> map = new HashMap<>();

/**
* 解析xml配置文件中的bean并注入到容器中
* @param pathName
* @return
*/
public static Map<String, Bean> getConfig(String pathName) {

//新建一个dom4j的解析器
SAXReader saxReader = new SAXReader();
Document document = null;
try {
//根据路径解析xml文件
document = saxReader.read(pathName);
}catch (Exception e){
e.printStackTrace();
throw new RuntimeException("请检查您的xml配置文件是否正确");
}
//根据Xpath寻找bean节点
String Xpath = "//bean";

//寻找根节点下所有bean结构
List<Element> beans = document.selectNodes(Xpath);

for (Element element: beans) {

//将解析到的内容封装到 bean 对象中
Bean bean = new Bean();
String name = element.attributeValue("id");
String className = element.attributeValue("class");
String scope = element.attributeValue("scope");
bean.setName(name);
bean.setClassName(className);

//如果没有写则默认单例
if (scope!=null){
bean.setScope(scope);
}

//继续遍历bean节点下的property节点
List<Element> properties = element.elements("property");

//如果有该节点则解析内容
if (properties != null) {
for (Element e:properties) {
Property property = new Property();
String pname = e.attributeValue("name");
String value = e.attributeValue("value");
String ref = e.attributeValue("ref");
property.setName(pname);
property.setValue(value);
property.setRef(ref);
/**
* 获取list<Property>后向内添加元素
* 将解析到的属性封装到property中,并添加到bean中
*/
bean.getProperties().add(property);
}
}
//封装到map中
map.put(name,bean);
}

return map;
}
}

Test.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Test {

public static void main(String[] args) {
ApplicationContext ct = new ClassPathXmlApplicationContext("resources/applicationContext.xml");
People people = (People) ct.getBean("people");
User user = (User) ct.getBean("user");
Man man = (Man) ct.getBean("man");
System.out.println(ct.containsBean("man"));
System.out.println(ct.getBean("people"));
System.out.println(ct.getBean("people"));
System.out.println(ct.isSingleton("man"));
System.out.println(ct.isPrototype("man"));
System.out.println(people);
System.out.println(user);
System.out.println(man);
}
}
文章作者: Archiver
文章链接: https://www.kaiming66.com/2020/01/31/Java/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3spring--%E5%8A%A8%E6%89%8B%E6%90%AD%E5%BB%BAIOC%E5%AE%B9%E5%99%A8/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Archiver`s Blog
打赏
  • 微信
  • 支付寶

评论