手动实现Spring中的IOC容器
注意:这就是一个用于学习理解Spring中IOC容器的简单例子!!!!
1、IOC容器实现的过程:
- 加载XML的配置文件,并且遍历文件中的标签
- 获取配置文件中单个Bean的ID和Class属性,然后根据Class文件加载对应的类,并且创建Bean的实例对象
- 获取属性标签中的属性值,填充到Bean的中
- 将设置好的Bean注册到IOC容器中
2、代码展示:
package com.baozi.Test; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import java.io.FileInputStream; import java.io.InputStream; import java.lang.reflect.Field; import java.util.HashMap; import java.util.Map; /** * @author BaoZi * @create 2019-07-19-8:54 */ public class SimpleIOC { private Map<String, Object> beanMap = new HashMap<>(); public SimpleIOC(String location) throws Exception { loadBeans(location); } public Object getBean(String name) { Object bean = beanMap.get(name); if (bean == null) { throw new IllegalArgumentException("there is no bean with name " + name); } return bean; } private void loadBeans(String location) throws Exception { // 加载 xml 配置文件 //把要解析的 XML 文档转化为输入流对象,以便 DOM 解析器解析它 InputStream inputStream = new FileInputStream(location); /** * javax.xml.parsers 包中的DocumentBuilderFactory用于创建DOM模式的解析器对象 , * DocumentBuilderFactory是一个抽象工厂类,它不能直接实例化,但该类提供了一个newInstance方法 , * 这个方法会根据本地平台默认安装的解析器,自动创建一个工厂的对象并返回。 */ //调用 DocumentBuilderFactory.newInstance() 方法得到创建 DOM 解析器的工厂。 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); //调用工厂对象的 newDocumentBuilder方法得到 DOM 解析器对象。 DocumentBuilder docBuilder = factory.newDocumentBuilder(); //调用 DOM 解析器对象的 parse() 方法解析 XML 文档,得到代表整个文档的 Document 对象, // 进行可以利用DOM特性对整个XML文档进行操作了。 Document doc = docBuilder.parse(inputStream); //得到 XML 文档的根节点 Element root = doc.getDocumentElement(); //得到节点的子节点 NodeList nodes = root.getChildNodes(); // 遍历 <bean> 标签 for (int i = 0; i < nodes.getLength(); i++) { Node node = nodes.item(i); if (node instanceof Element) { Element ele = (Element) node; String id = ele.getAttribute("id"); String className = ele.getAttribute("class"); // 加载 beanClass Class beanClass = null; try { beanClass = Class.forName(className); } catch (ClassNotFoundException e) { e.printStackTrace(); return; } // 创建 bean Object bean = beanClass.newInstance(); // 遍历 <property> 标签 //使用Document对象的getElementsByTagName()方 法,我们可以得到一个NodeList对象, // 一个Node对象代表了一个XML文档中的一个标签元素,而NodeList对象,观其名而知其意, // 所代表的是一个Node对象的列表: NodeList propertyNodes = ele.getElementsByTagName("property"); for (int j = 0; j < propertyNodes.getLength(); j++) { Node propertyNode = propertyNodes.item(j); if (propertyNode instanceof Element) { Element propertyElement = (Element) propertyNode; String name = propertyElement.getAttribute("name"); String value = propertyElement.getAttribute("value"); // 利用反射将 bean 相关字段访问权限设为可访问 Field declaredField = bean.getClass().getDeclaredField(name); declaredField.setAccessible(true); if (value != null && value.length() > 0) { // 将属性值填充到相关字段中 declaredField.set(bean, value); } else { //这里是Bean对象属性中循环引用问题(先不做处理) String ref = propertyElement.getAttribute("ref"); if (ref == null || ref.length() == 0) { throw new IllegalArgumentException("ref config error"); } // 将引用填充到相关字段中 declaredField.set(bean, getBean(ref)); } // 将 bean 注册到 bean 容器中 registerBean(id, bean); } } } } } //这里模仿Bean对象注入IOC容器中的过程 private void registerBean(String id, Object bean) { beanMap.put(id, bean); } }
3、测试实例:
Bean的实力对应的类:
public class Car { private String name; private String length; private String width; private String height; private Wheel wheel; //.......................... } public class Wheel { private String brand; private String specification ; // ............................ }
测试用的XML文件:
<beans> <bean id="wheel" class="com.baozi.Wheel"> <property name="brand" value="Michelin" /> <property name="specification" value="265/60 R18" /> </bean> <bean id="car" class="com.baozi.Car"> <property name="name" value="Mercedes Benz G 500"/> <property name="length" value="4717mm"/> <property name="width" value="1855mm"/> <property name="height" value="1949mm"/> <property name="wheel" ref="wheel"/> </bean> </beans>
测试类:
public class SimpleIOCTest { @Test public void getBean() throws Exception { String location = SimpleIOC.class.getClassLoader().getResource("spring-test.xml").getFile(); SimpleIOC bf = new SimpleIOC(location); Wheel wheel = (Wheel) bf.getBean("wheel"); System.out.println(wheel); Car car = (Car) bf.getBean("car"); System.out.println(car); } }