0%

手写Spring框架IoC

  • 手写Spring框架IoC
    • Spring框架核心接口实现ApplicationContext
    • Spring框架核心接口实现类ClassPathXmlApplicationContext

十一、手写Spring框架IoC

  • Spring IoC 的实现原理:工厂模式 + 解析 XML + 反射机制。

10.1 环境准备

  1. 创建模块 myspring

    ![](../../../../../Running Noob/计算机/Typora笔记/笔记-git仓库/Java-SSM-notebook/img/Spring/myspring1.png)

  2. 配置 pom.xml 文件,引入以下依赖:

    • dom4j依赖、jaxen依赖、junit依赖。
    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
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.f</groupId>
    <artifactId>myspring</artifactId>
    <version>1.0.0</version>
    <packaging>jar</packaging>

    <dependencies>
    <!--dom4j解析XML文件-->
    <dependency>
    <groupId>org.dom4j</groupId>
    <artifactId>dom4j</artifactId>
    <version>2.1.3</version>
    </dependency>
    <!--jaxen用以使用xpath-->
    <dependency>
    <groupId>jaxen</groupId>
    <artifactId>jaxen</artifactId>
    <version>1.2.0</version>
    </dependency>
    <!--单元测试-->
    <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.13.2</version>
    <scope>test</scope>
    </dependency>
    </dependencies>

    <properties>
    <maven.compiler.source>17</maven.compiler.source>
    <maven.compiler.target>17</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    </project>
  3. 创建 JavaBean 文件。

    • 注意:这些 JavaBean 是使用框架的用户写的,与框架自身代码无关。
    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
    package com.f.myspring.bean;

    /**
    * @author fzy
    * @date 2024/1/21 20:36
    */
    public class User {
    private String name;
    private int age;

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

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

    @Override
    public String toString() {
    return "User{" +
    "name='" + name + '\'' +
    ", age=" + age +
    '}';
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    package com.f.myspring.bean;

    /**
    * @author fzy
    * @date 2024/1/21 20:36
    */
    public class UserDao {
    public void insert() {
    System.out.println("Mysql数据库正在保存用户信息...");
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    package com.f.myspring.bean;

    /**
    * @author fzy
    * @date 2024/1/21 20:37
    */
    public class UserService {
    private UserDao userDao;

    public void setUserDao(UserDao userDao) {
    this.userDao = userDao;
    }

    public void save() {
    userDao.insert();
    }
    }
  4. 创建配置文件。

    • 注意:这个配置文件是使用框架的用户写的,与框架自身代码无关。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <?xml version="1.0" encoding="UTF-8"?>
    <!--这个配置文件是使用myspring框架的开发人员提供的-->
    <beans>
    <bean id="user" class="com.f.myspring.bean.User">
    <property name="name" value="jack"/>
    <property name="age" value="30"/>
    </bean>
    <bean id="userService" class="com.f.myspring.bean.UserService">
    <property name="userDao" ref="userDao"/>
    </bean>
    <bean id="userDao" class="com.f.myspring.bean.UserDao"/>
    </beans>

10.2 Spring框架核心接口实现ApplicationContext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package org.myspringframework.core;

/**
* myspring框架应用上下文接口
*
* @author fzy
* @date 2024/1/21 20:45
*/
public interface ApplicationContext {
/**
* 根据Bean的名称获取对应的Bean对象
*
* @param beanName myspring配置文件中Bean标签的id
* @return 对应的单例Bean对象
*/
public Object getBean(String beanName);
}

10.3 Spring框架核心接口实现类ClassPathXmlApplicationContext

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
package org.myspringframework.core;

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

import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
* @author fzy
* @date 2024/1/21 20:48
*/
public class ClassPathXmlApplicationContext implements ApplicationContext {
// 缓存。用于存放Bean对象的map集合,使用Bean的名称可以获取相应的Bean对象
private Map<String, Object> singletonObjects = new HashMap<>();

/**
* 解析myspring的配置文件,并初始化所有的Bean对象
* 解析myspring.xml文件,然后实例化Bean,将Bean存放到singletonObjects集合当中
*
* @param configLocation spring配置文件的路径
*/
public ClassPathXmlApplicationContext(String configLocation) {
try {
// 这是dom4j解析XML文件的核心对象
SAXReader saxReader = new SAXReader();
InputStream in = ClassLoader.getSystemClassLoader().getResourceAsStream(configLocation);
// 读文件
Document document = saxReader.read(in);
// 获取所有的bean标签
List<Node> nodes = document.selectNodes("//bean");
// 遍历bean标签,这次是实例化Bean,并加入Map集合
nodes.forEach(node -> {
try {
// 向下转型的目的是为了使用Element接口里面更加丰富的方法
Element beanElt = (Element) node;
String id = beanElt.attributeValue("id");
String className = beanElt.attributeValue("class");
// 通过反射机制创建对象,将其放到Map集合中,提前曝光
// 获取Class
Class<?> clazz = Class.forName(className);
// 获取无参数构造方法
Constructor<?> defaultCons = clazz.getDeclaredConstructor();
// 调用无参数构造方法实例化Bean
Object bean = defaultCons.newInstance();
// 将Bean曝光,加入Map集合
singletonObjects.put(id, bean);
} catch (Exception e) {
e.printStackTrace();
}
});
// 再次把所有的bean标签遍历一次,这次主要是给对象的属性赋值
nodes.forEach(node -> {
try {
Element beanElt = (Element) node;
String id = beanElt.attributeValue("id");
String className = beanElt.attributeValue("class");
// 获取Class
Class<?> clazz = Class.forName(className);
// 获取该bean标签下所有属性的property标签
List<Element> propertyElts = beanElt.elements("property");
// 遍历所有的property标签
propertyElts.forEach(propertyElt -> {
try {
// 获取属性名
String propertyName = propertyElt.attributeValue("name");
// 获取set方法名
String setMethodName = "set" + propertyName.toUpperCase().charAt(0) + propertyName.substring(1);
// 获取属性
Field field = clazz.getDeclaredField(propertyName);
// 获取set方法
Method setMethod = clazz.getDeclaredMethod(setMethodName, field.getType());
// 获取具体的值
String value = propertyElt.attributeValue("value");
Object actualVal = null;
String ref = propertyElt.attributeValue("ref");
if (value != null) { // 说明这个值是简单类型
// 获取属性类型名
String propertyTypeSimpleName = field.getType().getSimpleName();
switch (propertyTypeSimpleName) {
case "byte":
actualVal = Byte.parseByte(value);
break;
case "short":
actualVal = Short.parseShort(value);
break;
case "int":
actualVal = Integer.parseInt(value);
break;
case "long":
actualVal = Long.parseLong(value);
break;
case "float":
actualVal = Float.parseFloat(value);
break;
case "double":
actualVal = Double.parseDouble(value);
break;
case "boolean":
actualVal = Boolean.parseBoolean(value);
break;
case "char":
actualVal = value.charAt(0);
break;
case "Byte":
actualVal = Byte.valueOf(value);
break;
case "Short":
actualVal = Short.valueOf(value);
break;
case "Integer":
actualVal = Integer.valueOf(value);
break;
case "Long":
actualVal = Long.valueOf(value);
break;
case "Float":
actualVal = Float.valueOf(value);
break;
case "Double":
actualVal = Double.valueOf(value);
break;
case "Boolean":
actualVal = Boolean.valueOf(value);
break;
case "Character":
actualVal = Character.valueOf(value.charAt(0));
break;
case "String":
actualVal = value;
}
// 调用set方法(set方法没有返回值)
setMethod.invoke(singletonObjects.get(id), actualVal);
}
if (ref != null) { // 说明这个值是非简单类型
// 调用set方法(set方法没有返回值)
setMethod.invoke(singletonObjects.get(id), singletonObjects.get(ref));
}
} catch (Exception e) {
e.printStackTrace();
}
});
} catch (Exception e) {
e.printStackTrace();
}
});
} catch (Exception e) {
e.printStackTrace();
}
}

@Override
public Object getBean(String beanName) {
return singletonObjects.get(beanName);
}
}

10.4 MySpring框架测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.f.myspring.test;

import com.f.myspring.bean.User;
import com.f.myspring.bean.UserService;
import org.junit.Test;
import org.myspringframework.core.ApplicationContext;
import org.myspringframework.core.ClassPathXmlApplicationContext;

/**
* @author fzy
* @date 2024/1/21 21:19
*/
public class MySpringTest {
@Test
public void testMySpring() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("myspring.xml");
User user = (User) applicationContext.getBean("user");
System.out.println(user);
UserService userService = (UserService) applicationContext.getBean("userService");
userService.save();
}
}
---------------The End---------------