解析Spring IOC原理——工厂模式与反射机制的综合应用

来源:互联网 发布:ubuntu jenkins git 编辑:程序博客网 时间:2024/05/20 23:03
(一)工厂模式

从一个例子开始讲起:

首先我们建立一个Chinese.java类,该类的sayHelloWorld(String name)方法,用中文对名为name的人问好,其内容如下:

     

[java] view plaincopyprint?
  1. public class Chinese {  
  2.     /**-- 用中文对某人问好. --*/  
  3.     publicvoid sayHelloWorld(String name) {  
  4.        String helloWorld = "你好," + name;  
  5.        System.out.println(helloWorld);  
  6.     }  
  7. }  

 

下面我们接着建立一个American.java类,该类的sayHelloWorld(String name)方法,用英文对名为name的人问好,其内容如下:下面我们接着建立一个American.java类,该类的sayHelloWorld(String name)方法,用英文对名为name的人问好,其内容如下:

[html] view plaincopyprint?
  1. publicclass American {  
  2.   
  3.     /*-- 用英文对某人问好 --*/  
  4.     publicvoid sayHelloWorld(String name) {  
  5.        String helloWorld = "Hello," + name;  
  6.        System.out.println(helloWorld);  
  7.     }  
  8. }  


 最后我们编写一个测试类对这两个类的sayHelloWorld(String name)方法进行测试,下面是该类的内容:

[java] view plaincopyprint?
  1. publicclass HelloWorldTest {  
  2.   
  3.     /*-- 测试Chinese和American的sayHelloWorld()方法 --*/  
  4.     publicstaticvoid main(String[] args) {  
  5.        Chinese chinese = new Chinese();  
  6.        chinese.sayHelloWorld("极度暴走");  
  7.          
  8.        American american = new American();  
  9.        american.sayHelloWorld("jidubaozou");  
  10.     }  
  11. }  

 

现实生活中我们总是把同一类产品放在一个工厂中生产,消费者只需要告诉工厂自己需要这一类产品中的哪一种产品,工厂就能够生产出对应的产品,从而完成交易。

这里我们把上面例子中的HelloWorldTest类想象为消费者,而Chinese和American类想象为产品,根据上面的例子,我们可以看到每当HelloWorldTest这个消费者需要一个产品时都要去自己去生产(new),这样消费者就不得不去了解各个产品的内部结构及生产过程。显然对于消费者来说这样是很麻烦的。

而在程序设计领域中这个问题就叫做强耦合问题,HelloWorldTest类与ChineseAmerican这两个类都存在强耦合关系。设想一下,如果程序中的很多类都需要用到ChineseAmerican类这两个类,那么每个类就必须和这两个类强耦合,而且现实的项目中类似于Chinese这种类往往不仅两个,这样会使得整个程序的耦合度很高,增大程序的复杂度。这时我们就想到,能否也使用一个工厂来生产这些类,这样如果某个类需要用到工厂中的哪个类,就只要通过和工厂生产就行了,从而降低了整个程序的耦合度。

下面看看工厂模式是如何实现的:

首先建立接口类Human.java,其内容如下:

[java] view plaincopyprint?
  1. public interface Human {  
  2.   
  3.     /** *//** 
  4.      * 对某人问好. 
  5.      * @param name 姓名 
  6.      */  
  7.     public void sayHelloWorld(String name);  
  8. }  

 

并将American.java类和Chinese.java类改为实现该接口,即类头分别改成:public class American implements Humanpublic class Chinese implements Human

接着编写HumanFactory.java工厂类,其内容为

 

[java] view plaincopyprint?
  1. public class HumanFactory {  
  2.   
  3.        /** *//** 
  4.         * 通过类型字段获取人的相应实例 
  5.         * @param type 类型 
  6.         * @return 返回相应实例 
  7.         */  
  8.        public Human getHuman(String type) {  
  9.               if ("chinese".equals(type)) {  
  10.                      return new Chinese();  
  11.               } else {  
  12.                      return new American();  
  13.               }  
  14.        }  
  15. }  

 

最后我们还需要修改测试类HelloWorld.java类,修改后的内容如下: 

 

[java] view plaincopyprint?
  1. public class HelloWorldTest {  
  2.   
  3.        /** *//** 
  4.         * 测试sayHelloWorld()方法. 
  5.         * @param args 
  6.         */  
  7.        public static void main(String[] args) {  
  8.               HumanFactory factory = new HumanFactory();  
  9.               Human human1 = factory.getHuman("chinese");  
  10.               human1.sayHelloWorld("极度暴走");  
  11.   
  12.               Human human2 = factory.getHuman("american");  
  13.               human2.sayHelloWorld("jidubaozou");  
  14.               }  
  15. }  


通过上面的例子可以看到,使用工厂模式实现的方式, HelloWorldTest 不再与Chinese类和American类耦合,而只是和他们共同的接口耦合,从而很大程度的降低的程序的耦合性。到这里或许很多人会有一点疑虑,使用工厂模式的HelloWorldTest 类也是需要耦合两个类(即HumanFactory 类和Human 接口)那么为什么能说降低耦合度呢,这就要是何时要使用工厂模式的问题了。假如类似于我们这个例子,HumanFactory 这个工厂只生产两个类,那的确没有什么必要使用工厂模式,但是现实的项目中往往有更多和Chinese类类似的类,比如English,Japanese,这时候就能体现出工厂模式的优势了

 

 (二)Spring IOC的原理

首先我们来讲下为什么要引入IOC:

假设上面举例的那个程序已经部署到服务器中,并已经上线运行,这时项目来了这样一个新需求:增加一个Japanese类,并且在HelloWorldTest 类中调用Japanese的sayHelloWorld方法。在没有引入IOC之前,很显然我们为了这个新需求,必须把项目停止下来,然后从新编译HumanFactory 和HelloWorldTest这两个类,最后再重新上线运行。

而使用IOC,则能够在不重新编译部署的情况下实现上面的新需求!

那么我们来看一下IOC是去怎么实现这个新需求的: 

 首先我们在com.human包内创建一个Japanese类:

[java] view plaincopyprint?
  1. public class Japanese implements Human{  
  2.      private String name;  
  3.      private String age;  
  4.        
  5.      public void sayHelloWorld(String string){  
  6.          System.out.println("你好,"+string);  
  7.      }  
  8.      public String getAge() {  
  9.      return age;  
  10.      }  
  11.      public void setAge(String age) {  
  12.      this.age = age;  
  13.      }  
  14.      public String getName() {  
  15.      return name;  
  16.      }  
  17.      public void setName(String name) {  
  18.          this.name = name;  
  19.      }  
  20. }  


 

然后对HelloWorldTest 类做如下修改

 

[java] view plaincopyprint?
  1. public class HelloWorldTest {  
  2.   
  3.        public static void main(String[] args) {  
  4.              ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");  
  5.              BeanFactory beanFactory=ctx.getBeanFactory();  
  6.              Human human=beanFactory.getBean("human");  
  7.              human.sayHelloWorld("极度暴走");  
  8.              }  
  9. }  

 

然后我们在beans.xml中做如下配置:

[html] view plaincopyprint?
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2.   
  3. <beans>  
  4.     <bean id="human" class="com.human.Japanese">  
  5.        <property name="name">  
  6.            <value>漩涡鸣人</value>  
  7.        </property>  
  8.        <property name="password">  
  9.            <value>22</value>  
  10.        </property>  
  11. <pre class="html" name="code"></bean>  
  12. </beans></pre>  
  13. <p><br>  
  14. 这样,HelloWorldTest 便会去调用Japanese类的sayHelloWorld()方法;用这种形式,我们只要修改beans.xml的配置便可以改变HelloWorldTest 类中human对象,并且不需要重新部署项目,这样就实现了human对象的热拔插。</p>  
  15. <p>从上面的代码可以看到spring IOC的这种实现方式主要是靠BeanFactory这个类,那么这个BeanFactory到底有什么神奇之处呢?</p>  
  16. <p>下面让我们模拟实现一个BeanFactory:</p>  
  17. <p><br>  
  18.  </p>  
  19. <pre class="java" name="code">import java.io.InputStream;  
  20. import java.lang.reflect.Method;  
  21. import java.util.HashMap;  
  22. import java.util.Iterator;  
  23. import java.util.Map;  
  24. import org.dom4j.Attribute;  
  25. import org.dom4j.Document;  
  26. import org.dom4j.Element;  
  27. import org.dom4j.io.SAXReader;  
  28.   
  29. /** *//**  
  30.  * bean工厂类.      
  31.  */  
  32. public class BeanFactory {  
  33.        private Map<String, Object> beanMap = new HashMap<String, Object>();  
  34.   
  35.        /** *//**  
  36.         * bean工厂的初始化.  
  37.         * @param xml xml配置文件  
  38.         */  
  39.        public void init(String xml) {  
  40.               try {  
  41.                      //读取指定的配置文件  
  42.                      SAXReader reader = new SAXReader();  
  43.                      ClassLoader classLoader = Thread.currentThread().getContextClassLoader();  
  44.                      //从class目录下获取指定的xml文件  
  45.                      InputStream ins = classLoader.getResourceAsStream(xml);  
  46.                      Document doc = reader.read(ins);  
  47.                      Element root = doc.getRootElement();     
  48.                      Element foo;  
  49.                      //遍历bean  
  50.                      for (Iterator i = root.elementIterator("bean"); i.hasNext();) {     
  51.                             foo = (Element) i.next();  
  52.                             //获取bean的属性id和class  
  53.                             Attribute id = foo.attribute("id");     
  54.                             Attribute cls = foo.attribute("class");  
  55.                             //利用Java反射机制,通过class的名称获取Class对象  
  56.                             Class bean = Class.forName(cls.getText());  
  57.                             //获取对应class的信息  
  58.                             java.beans.BeanInfo info = java.beans.Introspector.getBeanInfo(bean);  
  59.                             //获取其属性描述  
  60.                             java.beans.PropertyDescriptor pd[] = info.getPropertyDescriptors();  
  61.                             //设置值的方法  
  62.                             Method mSet = null;  
  63.                             //创建一个对象  
  64.                             Object obj = bean.newInstance();  
  65.                             //遍历该bean的property属性  
  66.                             for (Iterator ite = foo.elementIterator("property"); ite.hasNext();) {     
  67.                                    Element foo2 = (Element) ite.next();  
  68.                                    //获取该property的name属性  
  69.                                    Attribute name = foo2.attribute("name");  
  70.                                   String value = null;  
  71.                                    //获取该property的子元素value的值  
  72.                                    for(Iterator ite1 = foo2.elementIterator("value"); ite1.hasNext();) {  
  73.                                           Element node = (Element) ite1.next();                                                                                           value = node.getText();  
  74.                                           break;  
  75.                                    }  
  76.                                    for (int k = 0; k < pd.length; k++) {  
  77.                                           if (pd[k].getName().equalsIgnoreCase(name.getText())) {                                                                         mSet = pd[k].getWriteMethod();  
  78.                                    //利用Java的反射机制调用对象的某个set方法,并将值设进去  
  79.                                    mSet.invoke(obj, value);  
  80. }  
  81. }  
  82. }  
  83.   
  84. //将对象放入beanMap中,其中key为id值,value为对象  
  85. beanMap.put(id.getText(), obj);  
  86. }  
  87. } catch (Exception e) {  
  88.     System.out.println(e.toString());  
  89. }  
  90. }  
  91.   
  92.        /** *//**  
  93.         * 通过bean的id获取bean的对象.  
  94.         * @param beanName bean的id  
  95.         * @return 返回对应对象  
  96.         */  
  97.        public Object getBean(String beanName) {  
  98.               Object obj = beanMap.get(beanName);  
  99.               return obj;  
  100.        }  
  101.   
  102.        /** *//**  
  103.         * 测试方法.  
  104.         * @param args  
  105.         */  
  106.        public static void main(String[] args) {  
  107.              BeanFactory factory = new BeanFactory();  
  108.               factory.init("bean.xml");  
  109.               Human human= (Human) factory.getBean("human");  
  110.               human.sayHelloWorld("极度暴走");  
  111.               System.out.println("Name=" + human.getName());  
  112.               System.out.println("age=" + human.getAge());  
  113.        }  
  114. }  
  115. </pre>  
  116. <p><br>  
  117. </p>  
  118. <p>测试方法的执行结果:你好,极度暴走</p>  
  119. <p>漩涡鸣人</p>  
  120. <p>22</p>  
  121. <p> </p>  
  122. <p>总结:从上面的代码可以看出Spring的bean工厂主要实现了以下几个步骤</p>  
  123. <p>1.解析配置文件(bean.xml)</p>  
  124. <p>2.使用反射机制动态加载每个class节点中配置的类</p>  
  125. <p>3.为每个class节点中配置的类实例化一个对象</p>  
  126. <p>4.使用反射机制调用各个对象的seter方法,将配置文件中的属性值设置进对应的对象</p>  
  127. <p>5.将这些对象放在一个存储空间(beanMap)中</p>  
  128. <p>6.使用getBean方法从存储空间(beanMap)中取出指定的JavaBean</p>  
  129. <p> </p>  
  130. <p>PS:IOC的标准定义:</p>  
  131. <p>(Inversion of Control) </p>  
  132. <p>  中文译为: 控制反转</p>  
  133. <p>  IOC的基本概念是:不创建对象,但是描述创建它们的方式。在代码中不直接与对象和服务连接,但在配置文件中描述哪一个组件需要哪一项服务。容器负责将这些联系在一起。</p>  
  134. <p> </p>  
  135. <p> </p>  
  136. <pre></pre>  
  137. <pre></pre>  
  138. <pre></pre>  
  139. <pre></pre>  
  140. <pre></pre>  
  141. <pre></pre>  
  142. <pre></pre>  
  143. <pre></pre>  
  144. <pre></pre>  
  145. <pre></pre>  
  146. <pre></pre>  
  147.       
  148.         <div style="padding-top:20px">           
  149.             <p style="font-size:12px;">版权声明:本文为博主原创文章,未经博主允许不得转载。</p>  
  150.         </div>  
1 0
原创粉丝点击