手写一个简单的IOC容器


说明

首先我们都知道,Spring框架中两个最重要的组件就是IOC和AOP。IOC 即 inversion of control 控制反转。Aop即 Aspect Oriented Programming 面向切面编程。那么我们这里就来手写一个简单的IOC容器。这里主要使用 xml配置的方式来实现IOC容器。

前置知识

手撸一个IOC容器需要先掌握好 ​​XML文件解析​​​ 和 ​​Java反射​​知识。

行了,下面我们就开始了。

项目搭建

创建项目

使用普通的maven搭建一个项目。

导入依赖

<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
</dependency>

<dependency>
<groupId>org.dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>2.1.1</version>
</dependency>

<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.62</version>
</dependency>
</dependencies>

实体类

package com.ooyhao.pojo;
import lombok.Data;
import java.io.Serializable;

@Data
public class User implements Serializable {

private byte age;
private Byte ageB;

private short height;
private Short heightS;

private int id;
private Integer idI;

private long weight;
private Long weightL;

private char sex;
private Character sexC;

private float salary;
private Float salaryF;

private double buy;
private Double buyD;

private boolean successB;
private Boolean success;

private String name;
}

spring配置文件

<?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="user" class="com.ooyhao.pojo.User">
<property name="age" value="2"/>
<property name="ageB" value="20"/>
<property name="height" value="170"/>
<property name="heightS" value="1700"/>
<property name="id" value="1"/>
<property name="idI" value="10"/>
<property name="weight" value="60"/>
<property name="weightL" value="600"/>
<property name="sex" value="m"/>
<property name="sexC" value="f"/>
<property name="salary" value="7500.5"/>
<property name="salaryF" value="9000.5"/>
<property name="buy" value="10.05"/>
<property name="buyD" value="100.5"/>
<property name="success" value="false"/>
<property name="successB" value="true"/>
<property name="name" value="欧阳"/>
</bean>
</beans>

自定义ApplicationContext

/**
* 简单手写一个IOC容器
*/
public class MyApplicationContext implements ApplicationContext {
private static Map<String,Object> beansContainer = new HashMap<>();

public MyApplicationContext(String path){
//构造器中处理
//处理ClassPath下的文件。
ClassPathResource resource = new ClassPathResource(path);
//利用DOM4J解析XML文件
SAXReader reader = new SAXReader();
try {
//读取xml文件,获取到文档
Document document = reader.read(resource.getInputStream());
//根目录beans
Element beansElement = document.getRootElement();
//迭代beans获取多个bean
Iterator<Element> beansIter = beansElement.elementIterator();
while(beansIter.hasNext()){
//单个bean
Element beanElement = beansIter.next();
//id
String id = beanElement.attributeValue("id");
//完整的类名
String className = beanElement.attributeValue("class");
//通过className反射出一个对象
Class<?> aClass = Class.forName(className);
//获取无参数的构造器
Constructor<?> constructor = aClass.getConstructor();
//利用无参数的构造器创建实例
Object o = constructor.newInstance();
//迭代bean获取property
Iterator<Element> beanIter = beanElement.elementIterator();
while (beanIter.hasNext()){
Element property = beanIter.next();
//属性名
String fieldName = property.attributeValue("name");
//属性值
String fieldValue = property.attributeValue("value");
//根据属性名获取属性对象
Field field = aClass.getDeclaredField(fieldName);
//属性的类型
Class<?> fieldType = field.getType();
//拼接set方法名
String setMethodName = "set" + fieldName.substring(0,1).toUpperCase()+ fieldName.substring(1);
//获取方法对象
Method method = aClass.getMethod(setMethodName, fieldType);

Object val = fieldValue;
switch (fieldType.getName()){
case "int":
case "java.lang.Integer":
val = Integer.valueOf(fieldValue);
break;
case "boolean":
case "java.lang.Boolean":
val = Boolean.valueOf(fieldValue);
break;
case "char":
case "java.lang.Character":
val = fieldValue.charAt(0);
break;
case "long":
case "java.lang.Long":
val = Long.valueOf(fieldValue);
break;
case "double":
case "java.lang.Double":
val = Double.valueOf(fieldValue);
break;
case "float":
case "java.lang.Float":
val = Float.valueOf(fieldValue);
break;
case "byte":
case "java.lang.Byte":
val = Byte.valueOf(fieldValue);
break;
case "short":
case "java.lang.Short":
val = Short.valueOf(fieldValue);
break;
}
method.invoke(o,val);
}
beansContainer.put(id,o);
}
} catch (DocumentException e) {
e.printStackTrace();
} catch (IOException 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();
}
}

public Object getBean(String s) throws BeansException {
Object o = beansContainer.get(s);
return o;
}

public <T> T getBean(Class<T> aClass) throws BeansException {
Collection<Object> values = beansContainer.values();
Iterator<Object> iterator = values.iterator();
while (iterator.hasNext()){
Object next = iterator.next();
if (next.getClass().equals(aClass)) {
return (T) next;
}
}
return null;
}
//部分未实现功能此处省略
}

上述部分就是实现简单的IOC重点内容。主要是基于 XML解析和发射知识。

项目测试

项目测试代码

ApplicationContext context = new MyApplicationContext("spring.xml");
User o = context.getBean(User.class);
System.out.println(JSONObject.toJSONString(o));

测试结果

{
"age": 2,
"ageB": 20,
"buy": 10.05,
"buyD": 100.5,
"height": 170,
"heightS": 1700,
"id": 1,
"idI": 10,
"name": "欧阳",
"salary": 7500.5,
"salaryF": 9000.5,
"sex": "m",
"sexC": "f",
"success": false,
"successB": true,
"weight": 60,
"weightL": 600
}

注意:这里只是实现了一个简单的IOC容器,并且只是实现了 java的简单类型和 包装类型,以及String类型的注入。