步骤
手写简易版的SpringIOC步骤如下,本次实现过程基于如下的步骤的.
-
编写要解析的配置文件
-
Xml解析获取Bean节点
-
通过反射获取对象并设置对象属性值
-
获取容器Bean对象
环境
环境搭建:IDEA,Java8,搭建Maven工程。
代码实现
POM文件
<dependency> <groupId>dom4j</groupId> <artifactId>dom4j</artifactId> <version>1.6.1</version> </dependency>
Bean对象
public class Person { private String name; private String age; private Book book; // 省略 get/set/ toString }
public class Book { private String name; private String place; private String price; // 省略 get/set/ toString }
Person.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的配置文件 --> <bean id="book" class="bean.Book"> <property name="name" value="think in java"></property> <property name="place" value="China"></property> <property name="price" value="79"></property> </bean> <bean id="person" class="bean.Person"> <property name="name" value="grl"></property> <property name="age" value="11"></property> <property name="book" ref="book"></property> </bean> </beans>
IOC
ApplicationContext 接口
public interface ApplicationContext { Object getBean(String name); }
ClassPathXmlApplicationContext 实现
import ioc.factory.ApplicationContext; 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.Method; import java.net.URISyntaxException; import java.net.URL; import java.util.*; /** * @Desc: TODO 基于类路径加载配置文件 */ public class ClassPathXmlApplicationContext implements ApplicationContext { /** * 要解析的配置文件 */ private File file; /** * 存放Bean对象的实例 */ private Map map = Collections.synchronizedMap(new HashMap()); /** * 解析配置文件,实例化容器,将对象存放入容器当中 */ public ClassPathXmlApplicationContext(String configFile) throws Exception { URL url = this.getClass().getClassLoader().getResource(configFile); try { file = new File(url.toURI()); xmlParse(file); } catch (URISyntaxException e) { e.printStackTrace(); } } /** * 解析xml文件 * * @param file */ private void xmlParse(File file) throws DocumentException, ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException { // 创建saxReader对象 SAXReader reader = new SAXReader(); // 通过read方法读取一个文件 转换成Document对象 Document document = reader.read(file); // 获取根节点【beans】 Element rootElement = document.getRootElement(); // 获得根节点下的所有子节点【bean】 List<Element> elements = rootElement.elements(); Iterator iterator = elements.iterator(); while (iterator.hasNext()) { Element element = (Element) iterator.next(); String id = element.attributeValue("id"); String classPath = element.attributeValue("class"); // 使用java的反射机制初始化类 Class<?> clazz = Class.forName(classPath); // 反射调用有参函数 Object newInstance = clazz.newInstance(); // 获取类的所有方法,然后通过set方法给这个对象设置属性值 Method[] methods = clazz.getDeclaredMethods(); // 获取属性【properties】 List<Element> sonEle = element.elements(); // 遍历属性下的name,value for (Element el : sonEle) { // 获取配置文件属性名称 String attField = el.attributeValue("name"); Object value = el.attributeValue("value"); if (value == null) { // 属性为引用对象的属性 String ref = el.attributeValue("ref"); value = map.get(ref); } // 获得私有属性 Field declaredField = clazz.getDeclaredField(attField); // 暴力反射获取私有属性 declaredField.setAccessible(true); // 给有参构造函数赋值【value】 declaredField.set(newInstance, value); } // 将对象添加到容器里面 map.put(id, newInstance); } } /** * 获取Bean对象 */ public Object getBean(String name) { return map.get(name); } }
public class Test { public static void main(String[] args) throws Exception { ApplicationContext context = new ClassPathXmlApplicationContext("Person.xml"); Person person = (Person) context.getBean("person"); System.out.println(person); } }
控制台输出:
Person{name='grl', age='11', book=Book{name='think in java', place='China', price='79'}}
总结一句话:Java通过配置文件的解读,反射拿到类实例,获取该类的所有Set方法,过滤取出和Xml中配置的属性值,然后反射动态给该属性设置(Xml中配置的值),这样调用相应属性的get方法就可以获取到相应的值啦.