隨筆 - 63  文章 - 0  trackbacks - 0
          <2009年4月>
          2930311234
          567891011
          12131415161718
          19202122232425
          262728293012
          3456789

          常用鏈接

          留言簿(2)

          隨筆分類

          隨筆檔案

          搜索

          •  

          最新評論

          閱讀排行榜

          評論排行榜

          spring IOC 機(jī)制模擬實現(xiàn)收藏  

           

                 Java中,其反射和動態(tài)代理機(jī)制極其強(qiáng)大,我們可以通過其反射機(jī)制在運(yùn)行時獲取信息。而代理是一種基本的設(shè)計模式,它是一種為了提供額外的或不同的操作而插入到真實對象中的某個對象。而Java的動態(tài)代理在代理上更進(jìn)一步,既能動態(tài)的創(chuàng)建代理對象,又能動態(tài)的調(diào)用代理方法。Java的反射和動態(tài)代理機(jī)制,使Java變得更加強(qiáng)大。

                 Spring框架這幾年風(fēng)頭正勁,雖然使用者眾多,但真正了解其內(nèi)部實現(xiàn)原理的朋友卻并不是很多。其實,了解它的內(nèi)部實現(xiàn)機(jī)制和設(shè)計思想是很有必要的大家都知道,Spring框架的IOCAOP部分功能強(qiáng)大,很值得我們學(xué)習(xí)。那么讓我們在這兩篇文章中分別詳細(xì)的學(xué)習(xí)IOCAOP的實現(xiàn)吧。

          在本文中,主要講述的是用Java的反射機(jī)制實現(xiàn)IOC。下面,讓我們開始IOC之旅吧!

          一.             Java反射機(jī)制概述與初探

          Java的反射機(jī)制是Java語言的一個重要特性,Java具有的比較突出的動態(tài)機(jī)制就是反射(reflection)。通過它,我們可以獲取如下信息:

          1) 在運(yùn)行時判斷任意一個對象所屬的類;

          2) 在運(yùn)行時獲取類的對象;

          3) 在運(yùn)行時獲得類所具有的成員變量和方法等。

          下面讓我們通過調(diào)用一個Java Reflection API的演示實例來見識一下反射機(jī)制的強(qiáng)大。

          首先在IDE中建立名為reflection_proxyJava工程,并建立存放源文件的目錄src,并在src目錄下分別建立org.amigo. reflection目錄和org.amigo.proxy目錄來分別存放代理和反射的實例。我們在reflection目錄下建立ReflectionTest.java文件,在該文件中編寫代碼來演示Java Reflection API的使用。該類的代碼如下所示:

          package org.amigo.reflection;

          import java.awt.Button;
          import java.lang.reflect.Method;
          import java.util.Hashtable;

          /**
           *初探Java的反射機(jī)制.   
           *
          @author<a href="mailto:xiexingxing1121@126.com">AmigoXie</a>
           *Creationdate:2007-10-2-上午10:13:48
           
          */

          publicclass ReflectionTest 
          {

              
          /**
               *@paramargs
               
          */

              publicstaticvoid main(String[] args) 
          throws Exception {
                  ReflectionTest reflection 
          = new ReflectionTest();
                  reflection.getNameTest();
                  System.out.println(
          "");
                  reflection.getMethodTest();
              }


              
          /**
               *Class的getName()方法測試.
               *@throwsException
               
          */

              publicvoid getNameTest() 
          throws Exception {
                  System.out.println(
          "===========begin getNameTest============");
                  String name 
          = "阿蜜果";
                  Class cls 
          = name.getClass();
                  System.out.println(
          "String類名: " + cls.getName());
                  Button btn 
          = new Button();
                  Class btnClass 
          = btn.getClass();
                  System.out.println(
          "Button類名: " + btnClass.getName());
                  Class superBtnClass 
          = btnClass.getSuperclass();
                  System.out.println(
          "Button的父類名: " + superBtnClass.getName());
                  Class clsTest 
          = Class.forName("java.awt.Button");
                  System.out.println(
          "clsTest name: " + clsTest.getName());
                  System.out.println(
          "===========end getNameTest============");
              }

              
              
          /**
               *Class的getMethod()方法測試.
               *@throwsException
               
          */

              publicvoid getMethodTest() 
          throws Exception {
                  System.out.println(
          "===========begin getMethodTest==========");
                  Class cls 
          = Class.forName("org.amigo.reflection.ReflectionTest");
                  Class ptypes[] 
          = new Class[2];
                  ptypes[
          0= Class.forName("java.lang.String");
                  ptypes[
          1= Class.forName("java.util.Hashtable");
                  Method method 
          = cls.getMethod("testMethod", ptypes);
                  Object args[] 
          = new Object[2];
                  args[
          0= "hello, my dear!";
                  Hashtable
          <String, String> ht = new Hashtable<String, String>();
                  ht.put(
          "name""阿蜜果");
                  args[
          1= ht;

                  String returnStr 
          = (String) method.invoke(new ReflectionTest(), args);
                  System.out.println(
          "returnStr= " + returnStr);
                  System.out.println(
          "===========end getMethodTest==========");
              }


              
          public String testMethod(String str, Hashtable ht) throws Exception {
                  String returnStr 
          = "返回值";
                  System.out.println(
          "測試testMethod()方法調(diào)用");
                  System.out.println(
          "str= " + str);
                  System.out.println(
          "名字= " + (String) ht.get("name"));
                  System.out.println(
          "結(jié)束testMethod()方法調(diào)用");
                  
          return returnStr;
          }

          }

               運(yùn)行該例,可在控制臺看到如下內(nèi)容:

          ===========begin getNameTest============

          String類名: java.lang.String

          Button類名: java.awt.Button

          Button的父類名: java.awt.Component

          clsTest name: java.awt.Button

          ===========end getNameTest============

          ===========begin getMethodTest==========

          測試testMethod()方法調(diào)用

          str= hello, my dear!

          名字= 阿蜜果

          結(jié)束testMethod()方法調(diào)用

          returnStr= 返回值

          ===========end getMethodTest==========

              分析運(yùn)行結(jié)果,我們可以發(fā)現(xiàn),Java的反射機(jī)制使得我們在運(yùn)行時能夠判斷一個對象所屬的類,獲取對象的方法并得其進(jìn)行調(diào)用,并獲取方法的返回結(jié)果等功能。

          二.             IOC使用的背景

          在我們?nèi)粘5脑O(shè)計中,類與類之間存在著千絲萬縷的關(guān)系,如果兩個類存在著強(qiáng)耦合關(guān)系,那么在維護(hù)時,一個類的修改很可能會牽動另一個類的關(guān)聯(lián)修改,從而使得我們的維護(hù)工作步履維艱。下面讓我們來看這樣的一個強(qiáng)耦合反面例子。

          首先我們建立一個Chinese.java類,該類的sayHelloWorld(String name)方法,用中文對名為name的人問好,其內(nèi)容如下:

          package org.amigo.reflection;

          /**
           *中國人類.   
           *
          @author<a href="mailto:xiexingxing1121@126.com">AmigoXie</a>
           *Creationdate:2007-10-2-上午10:37:17
           
          */

          publicclass Chinese 
          {

              
          /**
               *用中文對某人問好.
               *@paramname姓名
               
          */

              publicvoid sayHelloWorld(String name) 
          {
                 String helloWorld 
          = "你好," + name;
                 System.out.println(helloWorld);
              }

          }

           

          下面我們接著建立一個American.java類,該類的sayHelloWorld(String name)方法,用英文對名為name的人問好,其內(nèi)容如下:

          package org.amigo.reflection;

          /**
           *美國人類.   
           *
          @author<a href="mailto:xiexingxing1121@126.com">AmigoXie</a>
           *@version1.0
           *Creationdate:2007-10-2-上午10:41:27

           
          */

          publicclass American 
          {

              
          /**
               *用英文對某人問好.
               *@paramname姓名
               
          */

              publicvoid sayHelloWorld(String name) 
          {
                 String helloWorld 
          = "Hello," + name;
                 System.out.println(helloWorld);
              }

          }

           

              最后我們編寫一個測試類對這兩個類的sayHelloWorld(String name)方法進(jìn)行測試,下面是該類的內(nèi)容:

          package org.amigo.reflection;

          /**
           *HelloWorld測試.
           *
          @author<a href="mailto:xiexingxing1121@126.com">AmigoXie</a>
           *Creationdate:2007-10-2-上午10:45:13
           
          */

          publicclass HelloWorldTest 
          {

              
          /**
               *測試Chinese和American的sayHelloWorld()方法.
               *@paramargs
               *
          @author<a href="mailto:xiexingxing1121@126.com">AmigoXie</a>
               *Creationdate:2007-10-2-上午10:43:51
               
          */

              publicstaticvoid main(String[] args) 
          {
                 Chinese chinese 
          = new Chinese();
                 chinese.sayHelloWorld(
          "阿蜜果");
                 
                 American american 
          = new American();
                 american.sayHelloWorld(
          "Amigo");
              }

          }


              觀察HelloWorldTest我們可以很清楚的看到,該類與Chinese.java類和American.java類都存在強(qiáng)耦合關(guān)系。

          上面的例子讓我們想到的是在N年以前,當(dāng)我們需要某個東西時,我們一般是自己制造。但是當(dāng)發(fā)展到了一定的階段后,工廠出現(xiàn)了,我們可以工廠中購買我們需要的東西,這極大的方便了我們。在上例中,我們都是通過new來創(chuàng)建新的對象,在開發(fā)中,這種強(qiáng)耦合關(guān)系是我們所不提倡的,那么我們應(yīng)該如何來實現(xiàn)這個例子的解耦呢?我們接著想到了使用工廠模式,我們需要新建一個工廠類來完成對象的創(chuàng)建,并采用依賴接口的方式,此時需要對代碼進(jìn)行如下修改:

          首先建立接口類Human.java,其內(nèi)容如下:

          package org.amigo.reflection;

          /**
           * 人類接口類.    
           * 
          @author <a href="mailto:xiexingxing1121@126.com">AmigoXie</a>
           * Creation date: 2007-10-2 - 上午11:04:56
           
          */

          public interface Human {

              
          /**
               * 對某人問好.
               * 
          @param name 姓名
               
          */

              
          public void sayHelloWorld(String name);
          }


          并將American.java類和Chinese.java類改為實現(xiàn)該接口,即類頭分別改成:public class American implements Humanpublic class Chinese implements Human

          接著編寫HumanFactory.java工廠類,其內(nèi)容為

          package org.amigo.reflection;

          /**
           * 工廠類.    
           * 
          @author <a href="mailto:xiexingxing1121@126.com">AmigoXie</a>
           * Creation date: 2007-10-2 - 上午11:09:30
           
          */

          public class HumanFactory {

                 
          /**
                  * 通過類型字段獲取人的相應(yīng)實例
                  * 
          @param type 類型
                  * 
          @return 返回相應(yīng)實例
                  
          */

                 
          public Human getHuman(String type) {
                        
          if ("chinese".equals(type)) {
                               
          return new Chinese();
                        }
           else {
                               
          return new American();
                        }

                 }

          }


          最后我們還需要修改測試類HelloWorld.java類,修改后的內(nèi)容如下:

          package org.amigo.reflection;

          /**
           * HelloWorld測試.
           * 
          @author <a href="mailto:xiexingxing1121@126.com">AmigoXie</a>
           * Creation date: 2007-10-2 - 上午10:45:13
           
          */

          public class HelloWorldTest {

                 
          /**
                  * 測試sayHelloWorld()方法.
                  * 
          @param args
                  * 
          @author <a href="mailto:xiexingxing1121@126.com">AmigoXie</a>
                  * Creation date: 2007-10-2 - 上午10:43:51
                  
          */

                 
          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");
                        }

          }

          觀察此例我們可以看到,該類不再與具體的實現(xiàn)類ChineseAmerican存在耦合關(guān)系,而只是與它們的接口類Human存在耦合關(guān)系,具體對象的獲取只是通過傳入字符串來獲取,很大程度上降低了類與類之間的耦合性。

          但是我們還是不太滿足,因為還需要通過chineseamerican在類中獲取實例,那么當(dāng)我們需要修改時實現(xiàn)時,我們還需要在類中修改這些字符串,那么還有沒有更好的辦法呢?讓我們在下節(jié)中進(jìn)行繼續(xù)探討。

          三.             IOC粉墨登場

          IOCInverse of Control)可翻譯為“控制反轉(zhuǎn)”,但大多數(shù)人都習(xí)慣將它稱為“依賴注入”。在Spring中,通過IOC可以將實現(xiàn)類、參數(shù)信息等配置在其對應(yīng)的配置文件中,那么當(dāng)需要更改實現(xiàn)類或參數(shù)信息時,只需要修改配置文件即可,這種方法在上例的基礎(chǔ)上更進(jìn)一步的降低了類與類之間的耦合。我們還可以對某對象所需要的其它對象進(jìn)行注入,這種注入都是在配置文件中做的,SpringIOC的實現(xiàn)原理利用的就是Java的反射機(jī)制, Spring還充當(dāng)了工廠的角色,我們不需要自己建立工廠類。Spring的工廠類會幫我們完成配置文件的讀取、利用反射機(jī)制注入對象等工作,我們可以通過bean的名稱獲取對應(yīng)的對象。

          下面讓我們看看如下的模擬Springbean工廠類:

          package org.amigo.reflection;

          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;

          /**
           * bean工廠類.    
           * 
          @author <a href="mailto:xiexingxing1121@126.com">AmigoXie</a>
           * Creation date: 2007-10-6 - 上午10:04:41
           
          */

          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反射機(jī)制,通過class的名稱獲取Class對象
                                      Class bean = Class.forName(cls.getText());
                                      
          //獲取對應(yīng)class的信息
                                      java.beans.BeanInfo info = java.beans.Introspector.getBeanInfo(bean);
                                      
          //獲取其屬性描述
          java.beans.PropertyDescriptor pd[] = info.getPropertyDescriptors();
                                      
          //設(shè)置值的方法
                                      Method mSet = null;
                                      
          //創(chuàng)建一個對象
                                      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的反射極致調(diào)用對象的某個set方法,并將值設(shè)置進(jìn)去                                              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 返回對應(yīng)對象
                  
          */

                 
          public Object getBean(String beanName) {
                        Object obj 
          = beanMap.get(beanName);
                        
          return obj;
                 }


                 
          /**
                  * 測試方法.
                  * 
          @param args
                  * 
          @author <a href="mailto:xiexingxing1121@126.com">AmigoXie</a>
                  * Creation date: 2007-10-6 - 上午11:21:14
                  
          */

                 
          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());
                 }

          }

          該類的init(xml)方法,通過指定的xml來給對象注入屬性,為了對該類進(jìn)行測試,我還需要新建一個JavaBean和在src目錄下新建一個名為config.xml的配置文件。JavaBean的內(nèi)容如下:

          package org.amigo.reflection;

          /**
           * 
           * 簡單的bean,用于測試   
           * 
          @author <a href="mailto:xiexingxing1121@126.com">AmigoXie</a>
           * Creation date: 2007-10-6 - 上午11:24:30
           
          */

          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;
                 }

          }

           

          這個簡單bean對象中有兩個屬性,分別為userNamepassword,下面我們在配置文件config.xml中對其屬性注入對應(yīng)的屬性值。配置文件內(nèi)容如下:

          <?xml version="1.0" encoding="UTF-8"?>

          <beans>
              
          <bean id="javaBean" class="org.amigo.reflection.JavaBean">
                 
          <property name="userName">
                     
          <value>阿蜜果</value>
                 
          </property>
                 
          <property name="password">
                     
          <value>12345678</value>
                 
          </property>
              
          </bean>
          </beans>

          類與配置文件都完成后,可以運(yùn)行BeanFactory.java文件,控制臺顯示內(nèi)容為:

          userName=阿蜜果

          password=12345678

          可以看到,雖然在main()方法中沒有對屬性賦值,但屬性值已經(jīng)被注入,在BeanFactory類中的Class bean = Class.forName(cls.getText());通過類名來獲取對應(yīng)的類,mSet.invoke(obj, value);通過invoke方法來調(diào)用特定對象的特定方法,實現(xiàn)的原理都是基于Java的反射機(jī)制,在此我們有一次見證了Java反射機(jī)制的強(qiáng)大。

          當(dāng)然,這只是對IOC的一個簡單演示,在Spring中,情況要復(fù)雜得多,例如,可以一個bean引用另一個bean,還可以有多個配置文件、通過多種方式載入配置文件等等。不過原理還是采用Java的反射機(jī)制來實現(xiàn)IOC的。

          四.             總結(jié)

          在本文中,筆者通過講述Java反射機(jī)制概述與初探、IOC使用的背景、IOC粉墨登場等內(nèi)容,演示了Java反射機(jī)制API的強(qiáng)大功能,并通過編寫自己的簡單的IOC框架,讓讀者更好的理解了IOC的實現(xiàn)原理。

          本文通過IOC的一個簡要實現(xiàn)實例,模擬了SpringIOC的實現(xiàn),雖然只是完成了Spring中依賴注入的一小部分工作,但是很好的展現(xiàn)了Java反射機(jī)制在Spring中的應(yīng)用,能使我們能更好的從原理上了解IOC的實現(xiàn),也能為我們實現(xiàn)自己的準(zhǔn)Spring框架提供方案,有興趣的朋友可以通過Spring的源碼進(jìn)行IOC的進(jìn)一步的學(xué)習(xí)。

          posted on 2009-04-06 11:43 lanxin1020 閱讀(472) 評論(0)  編輯  收藏 所屬分類: spring
          主站蜘蛛池模板: 齐齐哈尔市| 绥棱县| 镇巴县| 巴彦淖尔市| 土默特右旗| 综艺| 时尚| 饶阳县| 阿坝县| 宜良县| 茌平县| 勃利县| 黑山县| 陆良县| 白山市| 峨边| 西峡县| 辽宁省| 潜江市| 桦川县| 武胜县| 峨边| 兰溪市| 延津县| 时尚| 凌海市| 石城县| 沅江市| 墨脱县| 香格里拉县| 象山县| 桦川县| 梅河口市| 洞口县| 沁阳市| 侯马市| 财经| 裕民县| 平乐县| 新巴尔虎右旗| 郸城县|