简单实现Spring的IOC容器
在学习Spring的时候,对IOC的实现比较感兴趣,在没有阅读源码前手动实现一个简单的IOC容器。
本文就通过dom4j解析xml文件,反射实例化对象,将对象存入hashmap中来简单实现一个IOC容器。
该容器只能解析通过属性注入的方式来实例化对象的xml文件(通过构造器方式注入来实例化对象的方法逻辑上差不多,都是使用反射,可以在本文代码的基础上进行调整实现)
导入dom4j
<!-- https://mvnrepository.com/artifact/dom4j/dom4j -->
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
代码实现
解析xml文件,将实例化的对象存入HashMap中
package com.etime.common;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
/**
* @BelongsProject: spring
* @BelongsPackage: com.etime.common
* @Author: zhanghang
* @CreateTime: 2022-10-18 21:08
* @Description: 基础Spring容器,可以实现通过配置Spring的xml文件来实例化单例的pojo对象
* 需要的技术点:解析xml文件,通过类名反射获取实例化对象,xml文件中的配置给对象的属性赋值,
* 将赋值结束的对象存入hashmap,key为xml文件中的bean标签的id,value为对象
* @Version: 1.0
*/
public class BaseIOC {
private Map<String,Object> targetMap = new HashMap<>();
private static final BaseIOC baseIOC = new BaseIOC();
private BaseIOC() {
}
/**
* @description: 获取单例对象
* @author: zhanghang
* @date: 2022/10/18 23:52
* @param: []
* @return: com.etime.common.BaseIOC
**/
public static BaseIOC getInstance(){
return baseIOC;
}
/**
* @description: 解析xml文件,同bean节点及其property获取bean对象,将其存储于map中
* @author: zhanghang
* @date: 2022/10/18 23:00
* @param: [xmlPath]
* @return: void
**/
public void parseXml(String xmlPath) throws Exception {
//1.创建SAXReader对象,解析器
SAXReader reader = new SAXReader();
//2.创建对应的Document对象,加载xml
Document document = null;
document = reader.read(new File(xmlPath));
//3.获取根节点
Element rootElement = document.getRootElement();
//4.获取根节点下的所有子节点
Iterator rootIterator = rootElement.elementIterator();
//5.遍历所有子节点
while (rootIterator.hasNext()){
//获取所有一级子节点的名称
Element element = (Element) rootIterator.next();
String name = element.getName();
//若一级子节点的名称为bean
Object o = null;
String idValue = null;
if ("bean".equals(name)){
//获取标签为bean的id属性值
Attribute idAttribute = element.attribute("id");
idValue = idAttribute.getValue();
// System.out.println(idValue);
//获取标签为bean的class属性值,
Attribute classAttribute = element.attribute("class");
String classValue = classAttribute.getValue();
// System.out.println(classValue);
//通过属性值利用反射创建对象
Class<?> beanClass = Class.forName(classValue);
o = beanClass.newInstance();
//获取标签为bean的所有子节点
Iterator iterator = element.elementIterator();
//遍历标签为bean的所有子节点
while (iterator.hasNext()){
Element childElement = (Element) iterator.next();
//获取bean中子节点的名称
String childElementName = childElement.getName();
//子节点为property的可以通过属性注入
if ("property".equals(childElementName)){
//获取property标签的元素的属性值,name和value
Attribute nameAttribute = childElement.attribute("name");
String parameterName = nameAttribute.getValue();
Attribute valueAttribute = childElement.attribute("value");
String parameterValue = valueAttribute.getValue();
// System.out.println(parameterName);
// System.out.println(parameterValue);
//利用反射为实例化好的对象赋属性值
Field[] declaredFields = beanClass.getDeclaredFields();
for (Field field : declaredFields){
if (field.getName().equals(parameterName)){
Class<?> type = field.getType();
Method method = beanClass.getMethod(getSetterName(parameterName), type);
method.invoke(o,parameterValue);
}
}
}
}
}
if (idValue!=null && o != null){
targetMap.put(idValue,o);
}
}
}
/**
* @description: 获取bean对象
* @author: zhanghang
* @date: 2022/10/18 23:52
* @param: [beanId]
* @return: java.lang.Object
**/
public Object getBean(String beanId){
if (targetMap!=null && targetMap.size()!=0){
return targetMap.get(beanId);
}
return null;
}
/**
* @description: 通过属性名生成Setter方法名
* @author: zhanghang
* @date: 2022/10/18 23:16
* @param: [parameterName]
* @return: java.lang.String
**/
private static String getSetterName(String parameterName){
char[] chars = parameterName.toCharArray();
char c = Character.toUpperCase(chars[0]);
chars[0] = c;
String result = String.valueOf(chars);
result = "set" + result;
return result;
}
}
简单IOC容器接口
package com.etime.common;
/**
* @BelongsProject: spring
* @BelongsPackage: com.etime.common
* @Author: zhanghang
* @CreateTime: 2022-10-18 22:49
* @Description: 简单的IOC容器
* @Version: 1.0
*/
public interface SimpleApplicationContext {
public Object getBean(String beanId);
}
简单IOC容器接口实现类
package com.etime.common;
import java.lang.reflect.InvocationTargetException;
/**
* @BelongsProject: spring
* @BelongsPackage: com.etime.common
* @Author: zhanghang
* @CreateTime: 2022-10-18 22:55
* @Description: IOC容器实现类
* @Version: 1.0
*/
public class SimpleApplicationContextImpl implements SimpleApplicationContext{
@Override
public Object getBean(String beanId) {
return BaseIOC.getInstance().getBean(beanId);
}
public SimpleApplicationContextImpl(String xmlPath) {
try {
BaseIOC.getInstance().parseXml(xmlPath);
} catch (Exception e) {
e.printStackTrace();
}
}
}
测试
实体类
package com.etime.pojo;
/**
* @BelongsProject: spring
* @BelongsPackage: com.etime.pojo
* @Author: zhanghang
* @CreateTime: 2022-10-18 15:57
* @Description: Spring测试使用pojo对象
* @Version: 1.0
*/
public class HelloSpring {
private String name;
private String addr;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddr() {
return addr;
}
public void setAddr(String addr) {
this.addr = addr;
}
@Override
public String toString() {
return "HelloSpring{" +
"name='" + name + '\'' +
", addr='" + addr + '\'' +
'}';
}
}
Spring-1.xml文件编写
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="helloSpring" class="com.etime.pojo.HelloSpring">
<property name="name" value="张三"/>
<property name="addr" value="成都"/>
</bean>
</beans>
编写测试程序
package com.etime.test;
import com.etime.common.SimpleApplicationContext;
import com.etime.common.SimpleApplicationContextImpl;
import com.etime.pojo.HelloSpring;
/**
* @BelongsProject: spring
* @BelongsPackage: com.etime.test
* @Author: zhanghang
* @CreateTime: 2022-10-18 23:48
* @Description: 测试简单IOC容器
* @Version: 1.0
*/
public class TestBaseIOC {
public static void main(String[] args) {
SimpleApplicationContext simpleApplicationContext = new SimpleApplicationContextImpl("src/main/resources/spring-1.xml");
HelloSpring helloSpring = (HelloSpring) simpleApplicationContext.getBean("helloSpring");
System.out.println(helloSpring);
}
}
测试结果
结语
至此,一个简单的SpringIOC容器就编写完成了,代码还有许多可以改进的地方,感谢阅读。