重新学习Spring之核心IOC容器的底层原理
一:IOC容器的定义
控制反转(Inversion of Control,英文缩写为IoC)是一个重要的面向对象编程的法则来削减计算机程序的耦合问题,也是轻量级的Spring框架的核心。 控制反转一般分为两种类型,依赖注入(Dependency Injection,简称DI)和依赖查找。依赖注入应用比较广泛。
二:Ioc容器相关含义
许多强大的功能都是由两个或是更多的类通过彼此的合作来实现业务逻辑,这使得每个对象和其他的对象产生依赖或者关联。(也就是对象持有其他对象的引用)。如果这个获取过程要靠自身实现,那么如你所见,这将导致代码高度耦合并且难以测试。
工厂模式只是一定程度上降低了这种代码的耦合性,
IoC模式可以彻底解决这种耦合,它把耦合从代码中移出去,放到统一的XML 文件中,通过一个容器在需要的时候把这个依赖关系形成,即把需要的接口实现注入到需要它的类中。这可能就是“依赖注入”说法的来源了。
优点:因为把对象生成放在了XML里定义,所以当我们需要换一个实现子类将会变成很简单,只要修改XML就可以了,这样我们甚至可以实现对象的热插拨(象USB)
缺点:1.创建对象的过程变得复杂,对于不习惯这种方式的人,会觉得有些别扭和不直观。
2.对象生成因为是使用反射编程,在效率上有些损耗。但相对于IoC提高的维护性和灵活性来说,这点损耗是微不足道的,除非某对象的生成对效率要求特别高
三:IOC容器实现原理
----->ioc容器实现原理项目图
----->beans.xml对应的java类
【1】一个xml节点在可以映射成一个java类。
beans根节点对应的java类
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package org.shangxiaofei.bjsxt.shang; 2 import java.util.ArrayList; 3 import java.util.List; 4 5 import javax.xml.bind.annotation.XmlElement; 6 import javax.xml.bind.annotation.XmlRootElement; 7 /** 8 * Beans.xml中相当于根节点对应的java对象 9 * @ClassName: Bean 10 * @Description: TODO(这里用一句话描述这个类的作用) 11 * @author 尚晓飞 12 * @date 2014-8-27 下午4:55:19 13 * 14 */ 15 @XmlRootElement 16 public class Beans { 17 //根节点下多有bean对象的集合 18 private List<Bean> list=new ArrayList<Bean>(); 19 20 public Beans() { 21 super(); 22 } 23 24 //name定义的是beans.xml中节点的名字 25 @XmlElement(name="bean") 26 public List<Bean> getList() { 27 return list; 28 } 29 30 public void setList(List<Bean> list) { 31 this.list = list; 32 } 33 34 35 }
bean节点对应的java类
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package org.shangxiaofei.bjsxt.shang; 2 3 import java.util.ArrayList; 4 import java.util.List; 5 6 import javax.xml.bind.annotation.XmlAttribute; 7 import javax.xml.bind.annotation.XmlElement; 8 9 /** 10 * 相当于beans.xml中<beans></beans>根节点下每一个<bean id="" className=""></bean>节点对应的java对象 11 * @ClassName: Bean 12 * @Description: TODO(这里用一句话描述这个类的作用) 13 * @author 尚晓飞 14 * @date 2014-8-27 下午5:00:58 15 * 16 */ 17 public class Bean { 18 //<bean></bean>节点中的属性 19 private String id; 20 21 22 //<bean></bean>节点中的属性 23 private String className; 24 25 26 //<bean></bean>节点下的<property></property>节点对应java对象的集合 27 private List<Property> list=new ArrayList<Property>(); 28 public Bean() { 29 super(); 30 } 31 32 //<bean></bean>节点中的属性 33 @XmlAttribute 34 public String getId() { 35 return id; 36 } 37 public void setId(String id) { 38 this.id = id; 39 } 40 41 //<bean></bean>节点中的属性 42 @XmlAttribute 43 public String getClassName() { 44 return className; 45 } 46 public void setClassName(String className) { 47 this.className = className; 48 } 49 50 //<bean></bean>节点下的<property></property>节点集合 51 @XmlElement(name="property") 52 public List<Property> getList() { 53 return list; 54 } 55 public void setList(List<Property> list) { 56 this.list = list; 57 } 58 59 60 }
property节点对应的java类
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package org.shangxiaofei.bjsxt.shang; 2 3 import javax.xml.bind.annotation.XmlAttribute; 4 5 /** 6 * <beans>节点下<bean>节点中<property>节点在java中对应的对象 7 * @ClassName: Property 8 * @Description: TODO(这里用一句话描述这个类的作用) 9 * @author 尚晓飞 10 * @date 2014-8-27 下午5:11:43 11 * 12 */ 13 public class Property { 14 //<property>节点中的属性 15 private String name; 16 17 //<property>节点中的属性 18 private String require; 19 public Property() { 20 super(); 21 } 22 23 //<property>节点的属性 24 @XmlAttribute 25 public String getName() { 26 return name; 27 } 28 public void setName(String name) { 29 this.name = name; 30 } 31 32 //<property>节点的属性 33 @XmlAttribute 34 public String getRequire() { 35 return require; 36 } 37 public void setRequire(String require) { 38 this.require = require; 39 } 40 41 42 }
【2】beans.xml的配置内容
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans> 3 4 <bean id="MyAction" className="com.bjsxt.shang.action.MyAction"> 5 <property name="studentService" require="StudentService"></property> 6 <property name="teacherService" require="TeacherService"></property> 7 </bean> 8 9 <bean id="StudentService" className="com.bjsxt.shang.service.impl.StudentServiceImp"></bean> 10 <bean id="TeacherService" className="com.bjsxt.shang.service.impl.TeacherServiceImp"></bean> 11 12 </beans>
【3】BeansFactory工厂类解析beans.xml来实现对象的生成和关系的建立
BeansFactory工厂类
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package com.bjsxt.shang.util; 2 3 4 import java.lang.reflect.Field; 5 import java.lang.reflect.InvocationTargetException; 6 import java.lang.reflect.Method; 7 import java.util.HashMap; 8 import java.util.Iterator; 9 import java.util.List; 10 import java.util.Map; 11 12 import javax.xml.bind.JAXBContext; 13 import javax.xml.bind.JAXBException; 14 import javax.xml.bind.Unmarshaller; 15 16 import org.shangxiaofei.bjsxt.shang.Bean; 17 import org.shangxiaofei.bjsxt.shang.Beans; 18 import org.shangxiaofei.bjsxt.shang.Property; 19 20 import com.bjsxt.shang.action.MyAction; 21 /** 22 * 此类是解析bens.xml文件,在解析过程中生成项目中配置好的类的对象,并根据配置的关系,建立项目中类与类之间的关联关系 23 * 24 * 面向接口编程,就是降低了代码的耦合度。 25 * 26 * 我们只要修改配置中接口对应的实现类,我们就可以改变功能。 27 * @ClassName: BeansFactory 28 * @Description: TODO(这里用一句话描述这个类的作用) 29 * @author 尚晓飞 30 * @date 2014-8-28 上午11:12:25 31 * 32 */ 33 public class BeansFactory { 34 //创建一个容器,用来存放xml解析过来的所有对象 35 private Map<String, Object> contaniner=new HashMap<String, Object>(); 36 37 //利用空构造器。来解析xml,并将解析过来的对象存放入容器中 38 public BeansFactory() throws JAXBException, ClassNotFoundException, InstantiationException, IllegalAccessException, SecurityException, NoSuchFieldException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException{ 39 //解析xml jaxb 40 JAXBContext context=JAXBContext.newInstance(Beans.class); 41 42 //context.createMarshaller() 编码 java-->XmlAccessOrder 43 //context.createUnmarshaller() 解码 xml-->java 44 45 Unmarshaller unmarshaller=context.createUnmarshaller(); 46 47 Beans beans=(Beans) unmarshaller.unmarshal(BeansFactory.class.getClassLoader().getResourceAsStream("beans.xml")); 48 49 //获取bens.xml中所有bean节点转换成的java对象 50 List<Bean> listBean=beans.getList(); 51 52 //将beans.xml中所有配置好的程序需要用的java对象生成,并将生成的对象存放入容器中,对应的key-value 为id-object 53 for (Iterator iterator = listBean.iterator(); iterator.hasNext();) { 54 Bean bean = (Bean) iterator.next(); 55 56 //获取bean对象中的属性值 配置的id和id对应的类名 57 String id=bean.getId(); 58 String className=bean.getClassName(); 59 60 //利用反射生成配置类名的对象 61 Class cls=Class.forName(className); 62 Object obj=cls.newInstance(); 63 64 //将每个类的对象存放到容器中 65 this.contaniner.put(id, obj); 66 } 67 68 69 //根据bean节点的下的配置,将java的对象与对象之间的关系建立起来,依赖注入set注入 70 for (Iterator iterator = listBean.iterator(); iterator.hasNext();) { 71 Bean bean = (Bean) iterator.next(); 72 73 //获取当前bean节点下的property节点的集合 74 List<Property> listProperty=bean.getList(); 75 //迭代当前bean下的property节点集合 76 for (Iterator iterator2 = listProperty.iterator(); iterator2.hasNext();) { 77 Property property = (Property) iterator2.next(); 78 //获取bean对象需要进行关联的属性名和对象引用的id 79 String name=property.getName(); 80 String require=property.getRequire(); 81 82 //获取当前的bean对象 83 Object obj1=contaniner.get(bean.getId());//当前宿主对象 84 Object obj2=contaniner.get(require);//当前从属对象 85 86 //拼接set方法的方法名 87 String methodName="set"+name.substring(0,1).toUpperCase()+name.substring(1); 88 //获取set方法的参数类型 89 Field field=obj1.getClass().getDeclaredField(name); 90 //获取属性的类型 91 field.getType(); 92 93 //获取set方法 94 Method method=obj1.getClass().getMethod(methodName, field.getType()); 95 96 //执行set方法,将需要关联的对象进行set注入 执行obj对象的set放入,注入obj2 97 method.invoke(obj1, obj2); 98 99 100 101 } 102 103 } 104 105 } 106 107 108 public Map<String, Object> getContaniner() { 109 return contaniner; 110 } 111 112 public void setContaniner(Map<String, Object> contaniner) { 113 this.contaniner = contaniner; 114 } 115 116 117 118 119 //测试方法 120 public static void main(String[] args) throws SecurityException, IllegalArgumentException, JAXBException, ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException, InvocationTargetException, NoSuchMethodException { 121 122 /** 123 * myAction类中引用了两个接口的引用。添加setget方法 124 * 125 * 两个接口各自有一个实现类。 126 * 127 */ 128 BeansFactory beansFactory=new BeansFactory(); 129 Map<String, Object> contaninerMap=beansFactory.getContaniner(); 130 MyAction myAction=(MyAction) contaninerMap.get("MyAction"); 131 myAction.add(); 132 133 134 //打印结果,是实现类中的方法中的打印语句: 135 //我需要添加一个学生在数据库 136 //我需要添加一个老师在数据库中 137 } 138 }
【4】MyAction类,此处省略了接口,和接口实现类的代码(在接口中定义一个方法,实现类实现,并在方法中打印一句话)
MyAction类
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
package com.bjsxt.shang.action; import com.bjsxt.shang.service.StudentService; import com.bjsxt.shang.service.TeacherService; public class MyAction { private StudentService studentService; private TeacherService teacherService; /** * 一个测试方法 * @Title: add * @Description: TODO(这里用一句话描述这个方法的作用) * @return * @return String 返回类型 * @author 尚晓飞 * @date 2014-8-27 下午5:29:33 */ public String add(){ studentService.addStudent(); teacherService.addTeacher(); return null; } public StudentService getStudentService() { return studentService; } public void setStudentService(StudentService studentService) { this.studentService = studentService; } public TeacherService getTeacherService() { return teacherService; } public void setTeacherService(TeacherService teacherService) { this.teacherService = teacherService; } }