Spring IoC 底层实现
核心技术点: XML 解析 + 反射
具体的思路:
1、根据需求编写 xml 文件,配置需要创建的 bean
2、编写程序读取 xml 文件,获取 bean 相关信息,类、属性、id。
3、根据第 2 步获取的信息,结合反射机制动态创建对象,同时完成属性的赋值。
4、将创建好的 bean 存入 Map 集合,设置 key - value 映射,key 就是 bean 中 id 值, value 就是 bean 对象。
5、提供方法从 Map 中通过 id 获取对应的 value。
1 前提准备
1、创建一个 maven 项目
2、导入相关依赖,lombok,log4j(需要用 log4j 解析 XML 文件)
<dependencies>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
<scope>provided</scope>
</dependency>
<!-- dom4j -->
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
</dependencies>
2 创建一个实体类
Car
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Car {
private Integer num;
private String brand;
private Double price;
}
3 根据需求编写 xml 文件,配置需要创建的 bean
在 resources 资源目录下创建 spring-ioc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="car" class="cc.lucien.entity.Car">
<property name="num" value="1"></property>
<property name="brand" value="奥迪"></property>
<property name="price" value="30.5"></property>
</bean>
<bean id="car2" class="cc.lucien.entity.Car">
<property name="num" value="2"></property>
<property name="brand" value="奥拓"></property>
</bean>
</beans>
4 编写程序读取 xml 文件,获取 bean 相关信息,类、属性、id。
模仿 spring,Spring 是有一个 ClassPathXMLApplicationContext 类来读取配置文件,我们创建一个属于自己的 MyClassPathXMLApplicationContext
类。
MyClassPathXMLApplicationContext.java
代码上面都有注释,仔细阅读
public class MyClassPathXmlApplicationContext {
public MyClassPathXmlApplicationContext(String path) {
// 解析 XML
parseXML(path);
}
public void parseXML(String path) {
SAXReader saxReader = new SAXReader();
try {
Document document = saxReader.read("src/main/resources/" + path);
Element root = document.getRootElement();
Iterator<Element> rootIter = root.elementIterator();
while (rootIter.hasNext()) {
// 遍历父节点下的子节点,获取子节点
Element bean = rootIter.next();
// 拿到子节点的属性值
String idStr = bean.attributeValue("id");
String className = bean.attributeValue("class");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
5 根据第 2 步获取的信息,结合反射机制动态创建对象,同时完成属性的赋值。
public class MyClassPathXmlApplicationContext {
public MyClassPathXmlApplicationContext(String path) {
// 解析 XML
parseXML(path);
}
public void parseXML(String path) {
SAXReader saxReader = new SAXReader();
try {
Document document = saxReader.read("src/main/resources/" + path);
Element root = document.getRootElement();
Iterator<Element> rootIter = root.elementIterator();
while (rootIter.hasNext()) {
// 遍历父节点下的子节点,获取子节点
Element bean = rootIter.next();
// 拿到子节点的属性值
String idStr = bean.attributeValue("id");
String className = bean.attributeValue("class");
// 反射动态创建对象
Class clazz = Class.forName(className);
// 获取无参构造方法
Constructor constructor = clazz.getConstructor();
// 通过无参构造创建对象
Object object = constructor.newInstance();
// 给属性赋值
Iterator<Element> beanIter = bean.elementIterator();
while (beanIter.hasNext()) {
// 获取子节点
Element property = beanIter.next();
// 获取子节点的属性值
String propertyName = property.attributeValue("name");
String propertyValue = property.attributeValue("value");
// 获取 setter 方法
// num --> setNum, brand --> setBrand
String methodName = "set" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
// 获取属性
Field field = clazz.getDeclaredField(propertyName);
// 获取属性的类型,也是setter方法中传参的类型
Class<?> type = field.getType();
Method method = clazz.getMethod(methodName, type);
// 类型转换
Object value = propertyValue;
switch (type.getName()) {
case "java.lang.Integer":
value = Integer.parseInt(propertyValue);
break;
case "java.lang.Double":
value = Double.parseDouble(propertyValue);
break;
}
// 调用方法
method.invoke(object, value);
}
}
} catch (DocumentException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
}
6 将创建好的 bean 存入 Map 集合,设置 key - value 映射,key 就是 bean 中 id 值, value 就是 bean 对象。
public class MyClassPathXmlApplicationContext {
private Map<String, Object> iocMap;
public MyClassPathXmlApplicationContext(String path) {
iocMap = new HashMap<>();
// 解析 XML
parseXML(path);
}
public void parseXML(String path) {
SAXReader saxReader = new SAXReader();
try {
Document document = saxReader.read("src/main/resources/" + path);
Element root = document.getRootElement();
Iterator<Element> rootIter = root.elementIterator();
while (rootIter.hasNext()) {
// 遍历父节点下的子节点,获取子节点
Element bean = rootIter.next();
// 拿到子节点的属性值
String idStr = bean.attributeValue("id");
String className = bean.attributeValue("class");
// 反射动态创建对象
Class clazz = Class.forName(className);
// 获取无参构造方法
Constructor constructor = clazz.getConstructor();
// 通过无参构造创建对象
Object object = constructor.newInstance();
// 给属性赋值
Iterator<Element> beanIter = bean.elementIterator();
while (beanIter.hasNext()) {
// 获取子节点
Element property = beanIter.next();
// 获取子节点的属性值
String propertyName = property.attributeValue("name");
String propertyValue = property.attributeValue("value");
// 获取 setter 方法
// num --> setNum, brand --> setBrand
String methodName = "set" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
// 获取属性
Field field = clazz.getDeclaredField(propertyName);
// 获取属性的类型,也是setter方法中传参的类型
Class<?> type = field.getType();
Method method = clazz.getMethod(methodName, type);
// 类型转换
Object value = propertyValue;
switch (type.getName()) {
case "java.lang.Integer":
value = Integer.parseInt(propertyValue);
break;
case "java.lang.Double":
value = Double.parseDouble(propertyValue);
break;
}
// 调用方法
method.invoke(object, value);
}
// 存放到 map 集合中
iocMap.put(idStr, object);
}
} catch (DocumentException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
}
7 设计一个 getBean 方法用来从 map 中通过 key 来取 value
public class MyClassPathXmlApplicationContext {
private Map<String, Object> iocMap;
public MyClassPathXmlApplicationContext(String path) {
iocMap = new HashMap<>();
// 解析 XML
parseXML(path);
}
public void parseXML(String path) {
// 省略....
}
public Object getBean(String name) {
return iocMap.get(name);
}
}
8 创建测试类,进行测试
Test.java
public class Test {
public static void main(String[] args) {
MyClassPathXmlApplicationContext context = new MyClassPathXmlApplicationContext("spring-ioc.xml");
Car car = (Car) context.getBean("car");
Car car2 = (Car) context.getBean("car2");
System.out.println(car);
System.out.println(car2);
}
}
运行结果:
Car(num=1, brand=奥迪, price=30.5)
Car(num=2, brand=奥拓, price=null)
9 gitee 源码地址
点击跳转:gitee 地址
完结,散花…