一、IOC简介
IOC-全称InversionofControl,中文解释:控制反转。另外,IOC又称DIDependencyInjection,中文解释:依赖注入。
IOC是一种新的设计模式,即IOC模式,系统中通过引入实现了IOC模式的IOC容器,即可由IOC容器来管理对象的生命周期、依赖关系等,从而使得应用程序的和依赖性规范与实际的应用程序代码分开。其中一个特点就是通过文本的配件文件进行应用程序组件间相互关系的配置,而不用重新修改并编译具体的代码。
很多大师都说IOC中有一个著名的好莱坞理论:你呆着别动,到时我会找你。这是什么意思呢,举个简单的例子,我们是如何找女朋友的?常见的情况是,我们到处去看哪里有长得漂亮身材又好的mm,然后打听她们的兴趣爱好、qq号、电话号、ip号、iq号………,想办法认识她们,投其所好送其所要,然后嘿嘿……这个过程是复杂深奥的,我们必须自己设计和面对每个环节。传统的程序开发也是如此,在一个对象中,如果要使用另外的对象,就必须得到它(自己new一个,或者从JNDI中查询一个),使用完之后还要将对象销毁(比如Connection等),对象始终会和其他的接口或类藕合起来。
下面看一个强耦合的例子:
class Chinese { /** *用中文对某人问好. *@paramname姓名 */ public void sayHelloWorld(String name) { String helloWorld = "你好," + name; System.out.println(helloWorld); } } class American { /** *用英文对某人问好. *@paramname姓名 */ public void sayHelloWorld(String name) { String helloWorld = "Hello," + name; System.out.println(helloWorld); } } public class test { public static void main(String[] args) { Chinese chinese = new Chinese(); chinese.sayHelloWorld("阿蜜果"); American american = new American(); american.sayHelloWorld("Amigo"); } }
上面的例子让我们想到的是在N年以前,当我们需要某个东西时,我们一般是自己制造。但是当发展到了一定的阶段后,工厂出现了,我们可以工厂中购买我们需要的东西,这极大的方便了我们。在上例中,我们都是通过new来创建新的对象,在开发中,这种强耦合关系是我们所不提倡的,那么我们应该如何来实现这个例子的解耦呢?我们接着想到了使用工厂模式,我们需要新建一个工厂类来完成对象的创建,并采用依赖接口的方式,此时需要对代码进行如下修改:
class Chinese implements Human{ /** *用中文对某人问好. *@paramname姓名 */ public void sayHelloWorld(String name) { String helloWorld = "你好," + name; System.out.println(helloWorld); } } class American implements Human { /** *用英文对某人问好. *@paramname姓名 */ public void sayHelloWorld(String name) { String helloWorld = "Hello," + name; System.out.println(helloWorld); } } interface Human { /** * 对某人问好. * @param name 姓名 */ public void sayHelloWorld(String name); } class HumanFactory { /** * 通过类型字段获取人的相应实例 * @param type 类型 * @return 返回相应实例 */ public Human getHuman(String type) { if ("chinese".equals(type)){ return new Chinese(); } else { return new American(); } } } public class test { public static void main(String[] args) { HumanFactory factory = new HumanFactory(); Human human1 = factory.getHuman("chinese"); human1.sayHelloWorld("阿蜜果"); Human human2 = factory.getHuman("american"); human2.sayHelloWorld("Amigo"); } }
观察此例我们可以看到,该类不再与具体的实现类Chinese和American存在耦合关系,而只是与它们的接口类Human存在耦合关系,具体对象的获取只是通过传入字符串来获取,很大程度上降低了类与类之间的耦合性。
但是我们还是不太满足,因为还需要通过chinese和american在类中获取实例,那么当我们需要修改时实现时,我们还需要在类中修改这些字符串,那么还有没有更好的办法呢?
在Spring中,通过IOC可以将实现类、参数信息等配置在其对应的配置文件中,那么当需要更改实现类或参数信息时,只需要修改配置文件即可,这种方法在上例的基础上更进一步的降低了类与类之间的耦合。我们还可以对某对象所需要的其它对象进行注入,这种注入都是在配置文件中做的,Spring的IOC的实现原理利用的就是Java的反射机制, Spring还充当了工厂的角色,我们不需要自己建立工厂类。Spring的工厂类会帮我们完成配置文件的读取、利用反射机制注入对象等工作,我们可以通过bean的名称获取对应的对象。
代码如下:
模拟Spring的bean工厂类:
package AOP; import java.io.InputStream; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import org.dom4j.Attribute; import org.dom4j.Document; import org.dom4j.Element; import org.dom4j.io.SAXReader; public class BeanFactory { private Map<String, Object> beanMap = new HashMap<String, Object>(); /** * bean工厂的初始化. * @param xml xml配置文件 */ public void init(String xml) { try { //读取指定的配置文件 SAXReader reader = new SAXReader(); ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); //从class目录下获取指定的xml文件 InputStream ins = classLoader.getResourceAsStream(xml); Document doc = reader.read(ins); Element root = doc.getRootElement(); Element foo; //遍历bean for (Iterator i = root.elementIterator("bean"); i.hasNext();) { foo = (Element) i.next(); //获取bean的属性id和class Attribute id = foo.attribute("id"); Attribute cls = foo.attribute("class"); //利用Java反射机制,通过class的名称获取Class对象 Class bean = Class.forName(cls.getText()); //获取对应class的信息 java.beans.BeanInfo info = java.beans.Introspector.getBeanInfo(bean); //获取其属性描述 java.beans.PropertyDescriptor pd[] = info.getPropertyDescriptors(); //设置值的方法 Method mSet = null; //创建一个对象 Object obj = bean.newInstance(); //遍历该bean的property属性 for (Iterator ite = foo.elementIterator("property"); ite.hasNext();) { Element foo2 = (Element) ite.next(); //获取该property的name属性 Attribute name = foo2.attribute("name"); String value = null; //获取该property的子元素value的值 for(Iterator ite1 = foo2.elementIterator("value"); ite1.hasNext();) { Element node = (Element) ite1.next(); value = node.getText(); break; } for (int k = 0; k < pd.length; k++) { if (pd[k].getName().equalsIgnoreCase(name.getText())) { mSet = pd[k].getWriteMethod(); //利用Java的反射极致调用对象的某个set方法,并将值设置进去 mSet.invoke(obj, value); } } } //将对象放入beanMap中,其中key为id值,value为对象 beanMap.put(id.getText(), obj); } } catch (Exception e) { System.out.println(e.toString()); } } /** * 通过bean的id获取bean的对象. * @param beanName bean的id * @return 返回对应对象 */ public Object getBean(String beanName) { Object obj = beanMap.get(beanName); return obj; } /* * 测试方法. */ public static void main(String[] args) { BeanFactory factory = new BeanFactory(); factory.init("config.xml"); JavaBean javaBean = (JavaBean) factory.getBean("javaBean"); System.out.println("userName=" + javaBean.getUserName()); System.out.println("password=" + javaBean.getPassword()); } }
JavaBean
package AOP; public class JavaBean { private String userName; private String password; public String getPassword() { return password; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public void setPassword(String password) { this.password = password; } }
在src目录下新建一个名为config.xml的配置文件,在配置文件config.xml中对其属性注入对应的属性值。配置文件内容如下:
<?xml version="1.0" encoding="UTF-8"?> <beans> <bean id="javaBean" class="AOP.JavaBean"> <property name="userName"> <value>阿蜜果</value> </property> <property name="password"> <value>12345678</value> </property> </bean> </beans>
可以看到,虽然在main()方法中没有对属性赋值,但属性值已经被注入,在BeanFactory类中的Class bean = Class.forName(cls.getText());通过类名来获取对应的类,mSet.invoke(obj, value);通过invoke方法来调用特定对象的特定方法,实现的原理都是基于Java的反射机制,在此我们有一次见证了Java反射机制的强大。
总结:
在本文中,笔者通过讲述Java反射机制概述与初探、IOC使用的背景、IOC粉墨登场等内容,演示了Java反射机制API的强大功能,并通过编写自己的简单的IOC框架,让读者更好的理解了IOC的实现原理。
本文通过IOC的一个简要实现实例,模拟了Spring中IOC的实现,虽然只是完成了Spring中依赖注入的一小部分工作,但是很好的展现了Java反射机制在Spring中的应用,能使我们能更好的从原理上了解IOC的实现,也能为我们实现自己的准Spring框架提供方案,有兴趣的朋友可以通过Spring的源码进行IOC的进一步的学习。
在Spring中,通过IOC可以将实现类、参数信息等配置在其对应的配置文件中,那么当需要更改实现类或参数信息时,只需要修改配置文件即可,这种方法在上例的基础上更进一步的降低了类与类之间的耦合。我们还可以对某对象所需要的其它对象进行注入,这种注入都是在配置文件中做的,Spring的IOC的实现原理利用的就是Java的反射机制, Spring还充当了工厂的角色,我们不需要自己建立工厂类。Spring的工厂类会帮我们完成配置文件的读取、利用反射机制注入对象等工作,我们可以通过bean的名称获取对应的对象。