说明:
(1)这篇博客的目的是,帮助加深对Spring IoC容器的理解;主要是理解,Spring IoC容器背后是如何利用反射机制完成对象的创建和数据的注入的;
(2)这篇博客中的内容不需要记忆,仅作提升理解Spring IoC容器用;(3)通过本篇博客的介绍,能够感受到IoC容器的原理并不复杂;IoC容器本质的是一个Map键值对对象;
(4)本篇博客仅仅是IoC容器的基础实现;Spring框架中的IoC容器远比本篇博客中的内容复杂的多;
目录
2.创建applicationContext.xml配置文件
3.创建ApplicationContext接口和ClassPathXmlApplicationContext实现类
0.为了演示,创建一maven工程:s07
1.创建Apple类:
Apple类:
package com.imooc.spring.ioc.entity; import javax.print.attribute.standard.RequestingUserName; public class Apple { private String title; private String color; private String origin; public Apple() { } public Apple(String title, String color, String origin) { this.title = title; this.color = color; this.origin = origin; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } public String getOrigin() { return origin; } public void setOrigin(String origin) { this.origin = origin; } }
说明:
(1)这个Apple类没什么好说的,一个普通的javaBean;
然后,我们去创建一个配置文件,然后在配置文件中,按照Spring IoC的规则和样式,写一个Apple对象的信息;
2.创建applicationContext.xml配置文件
applicationContext.xml:
<?xml version="1.0" encoding="UTF-8"?> <beans> <bean id="sweetApple" class="com.imooc.spring.ioc.entity.Apple"> <property name="title" value="红富士"/> <property name="color" value="红色"/> <property name="origin" value="欧洲"/> </bean> </beans>
说明:
(1)这个配置文件名字可以随便起,那就干脆和Spring IoC一样,也叫做applicationContext.xml吧;
(2)这个applicationContext.xml中,按照Spring IoC配置文件的规则和样式,写了一个Apple对象的信息;
配置文件写好了之后,对于这个配置文件,是如何在运行时,创建对象嘞?我们需要自己去实现IoC容器;下面按照Spring IoC的规则和样式,去加载xml配置文件,完成IoC容器的初始化工作;
3.创建ApplicationContext接口和ClassPathXmlApplicationContext实现类
ApplicationContext接口:
package com.imooc.spring.ioc.context; /** * 这个接口,就来模拟Spring中的ApplicationContext接口; */ public interface ApplicationContext { public Object getBean(String beanId); }
说明:
(1)模拟Spring IoC容器,在ApplicationContext接口中,创建一个getBean()方法;
……………………………………………………
ClassPathXmlApplicationContext实现类:
package com.imooc.spring.ioc.context; import org.dom4j.Document; import org.dom4j.Element; import org.dom4j.Node; import org.dom4j.io.SAXReader; import java.io.File; import java.lang.reflect.Method; import java.net.URLDecoder; import java.util.HashMap; import java.util.List; import java.util.Map; /** * 这个类模拟Spring中的ClassPathXmlApplicationContext类; * 这个类的作用:(1)实现ApplicationContext接口;(2)完成IoC容器的创建过程; */ public class ClassPathXmlApplicationContext implements ApplicationContext{ private Map iocContainer = new HashMap(); public ClassPathXmlApplicationContext(){ try{ String filePath = this.getClass().getResource("/applicationContext.xml").getPath(); filePath = new URLDecoder().decode(filePath, "UTF-8"); SAXReader reader = new SAXReader(); Document document = reader.read(new File(filePath)); List<Node> beans = document.getRootElement().selectNodes("bean"); for (Node node : beans) { Element element = (Element) node; String id = element.attributeValue("id"); String className = element.attributeValue("class"); Class c = Class.forName(className); Object obj = c.newInstance(); List<Node> properties = element.selectNodes("property"); for (Node p : properties) { Element property = (Element) p; String propName = property.attributeValue("name"); String propValue = property.attributeValue("value"); String setMethodName = "set" + propName.substring(0, 1).toUpperCase() + propName.substring(1); Method setMethod = c.getMethod(setMethodName, String.class); setMethod.invoke(obj, propValue);//通过setter方法注入数据 } iocContainer.put(id, obj); } System.out.println("IoC容器初始化完毕。"); }catch (Exception e){ e.printStackTrace(); } } public Object getBean(String beanId) { return iocContainer.get(beanId); } }
说明:
(1)这个类的职责就是实现ApplicationContext接口,并且完成IoC容器的创建过程;可以这样理解:每一个ClassPathXmlApplicationContext对象,都对应一个IoC容器,这个容器本身是用来存储对象的;
(2)在Java中,IoC容器使用Map去保存对象,key对应了beanId,value对应了相应的对象;
(3)在实例化ClassPathXmlApplicationContext对象的过程中,去加载处理applicationContext.xml配置文件,所以主要代码都写在了ClassPathXmlApplicationContext的默认构造方法中了。同时,因为在这个过程中可能会产生异常,所以写在了try块中;
(4)获取applicationContext.xml这个配置文件的地址;
这儿的内容,前面遇到过N次,比如第一次遇到是在【前台系统二:需求分析与数据建模:创建数据工具类、分页工具类;Dao数据对象访问类;(分页模块的Model的Dao部分)】,如有需要可以快速参考下这篇博客;
(5)获得了applicationContext.xml文件的地址后,就是解析这个文件了;由于这个文件是xml文件,所以可以利用现有的轮子,以提高程序的效率;引入dom4j和jaxen;
dom4j:是java的xml解析组件,其可以把xml文件转成一个document对象,帮助我们读取和操作xml的内容;jaxen:在使用Dom4j,利用XPath查询时候,必须要先下载Jaxen的jar包;在快速查询xml文件的时候,需要引入这个dom4j底层依赖的Jaxen;这部分如有需要可以快速参考【XML三:如何使用Java操作XML?DOM;Dom4j简介;项目导入Dom4j这个jar包】及附近相关文章;
(6)解析xml文件:这部分如有需要可以快速参考【XML三:如何使用Java操作XML?DOM;Dom4j简介;项目导入Dom4j这个jar包】及附近相关文章;
有关反射技术,如有需要可以快速参考下【Java反射一:反射引入(一个案例,感性认识什么是反射);(.class,Class.forName()和.getClass()的区别)】及附近相关文章;
至此,这个IoC容器的初始化的代码就写好了;
4.创建SpringApplication类,去测试
SpringApplication类:
package com.imooc.spring.ioc; import com.imooc.spring.ioc.context.ApplicationContext; import com.imooc.spring.ioc.context.ClassPathXmlApplicationContext; import com.imooc.spring.ioc.entity.Apple; public class SpringApplication { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext(); Apple sweetApple = (Apple) context.getBean("sweetApple"); System.out.println(sweetApple); } }
运行结果:
……………………………………………………
这儿打一个断点,进一步观察: