隨筆 - 63  文章 - 0  trackbacks - 0
          <2025年5月>
          27282930123
          45678910
          11121314151617
          18192021222324
          25262728293031
          1234567

          常用鏈接

          留言簿(2)

          隨筆分類

          隨筆檔案

          搜索

          •  

          最新評論

          閱讀排行榜

          評論排行榜

          利用Java的反射與代理實現AOP

           

          一.AOP概述
                 AOP(Aspect Oriented Programing),即面向切面編程,它主要用于日志記錄、性能統計、控制、事務處理、異常處理等方面。它的主要意圖就要將日志記錄,性能統計,安全控制、事務處理、異常處理等等代碼從業務邏輯代碼中清楚地劃分出來。通過對這些行為的分離,我們希望可以將它們獨立地配置到業務邏輯方法中,而要改變這些行為的時候也不需要影響到業務邏輯方法代碼。
                 下面讓我們來看一個利用AOP來實現日志記錄的例子,在沒有使用AOP之前,我們的代碼如下面所講述。
                 下面這段代碼為業務的接口類代碼:

          package org.amigo.proxy; /** * 業務邏輯類接口. * @author <a href="mailto:xiexingxing1121@126.com">AmigoXie</a> * Creation date: 2007-10-7 - 上午09:09:53 */ public interface BusinessObj { /** * 執行業務. */ public void process(); } BusinessObj接口的某個實現類代碼如下: package org.amigo.proxy; /** * 業務邏輯對象實現類. * @author <a href="mailto:xiexingxing1121@126.com">AmigoXie</a> * Creation date: 2007-10-7 - 上午09:11:49 */ public class BusinessObjImpl implements BusinessObj { /** * 執行業務. */ public void process() { try { System.out.println("before process"); System.out.println("執行業務邏輯"); System.out.println("after process"); } catch (Exception e) { System.err.println("發生異常:" + e.toString()); } } }
              在上例中我們可以看到,在執行業務方法前、執行業務方法后以及異常發生時的日志記錄,我們都是通過在對應的類中寫入記錄日志的代碼來實現的,當有這種日志記錄需求的業務邏輯類不斷增多時,將會給我們的維護帶來很大困難,而且,在上面的例子中,日志代碼和業務邏輯代碼混合在一起,為日后的維護工作又抹上了一層“恐怖”色彩。
                 按照AOP的思想,我們首先需要尋找一個切面,在這里我們已經找到,即在業務邏輯執行前后以及異常發生時,進行相應的日志記錄。我們需要將這部分日志代碼放入一個單獨的類中,以便為以后的修改提供方便。
                 我們在截獲某個業務邏輯方法時,可以采用Java的動態代理機制來實現。
           
           
          二.Java的動態代理機制
          代理模式是常用的Java設計模式。代理類主要負責為委托類預處理消息、過濾信息、把消息轉發給委托類,以及事后處理信息等。
          動態代理類不僅簡化了編程工作,而且提高了軟件系統的擴展性和可維護性。我們可以通過實現java.lang.reflect.InvocationHandler接口提供一個執行處理器,然后通過java.lang.reflect.Proxy得到一個代理對象,通過這個代理對象來執行業務邏輯方法,在業務邏輯方法被調用的同時,自動調用會執行處理器。
                
          下面讓我們來創建一個日志攔截器類LogInterceptor.java文件,該類實現java.lang.reflect.InvocationHandler接口,其內容如下所示:

          package org.amigo.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * 日志攔截器,用來進行日志處理. * @author <a href="mailto:xiexingxing1121@126.com">AmigoXie</a> * Creation date: 2007-10-7 - 上午09:31:44 */ public class LogInterceptor implements InvocationHandler { private Object delegate; /** * 構造函數,設置代理對象. */ public LogInterceptor(Object delegate){ this.delegate = delegate; } /** * 方法的調用. * @param proxy * @param method 對應的方法 * @param args 方法的參信息 * @return 返回操作結果對象 * @throws Throwable */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = null; try { System.out.println("before process" + method); //調用代理對象delegate的method方法,并將args作為參數信息傳入 result = method.invoke(delegate, args); System.out.println("after process" + method); } catch (Exception e){ System.err.println("發生異常:" + e.toString()); } return result; } /** * 測試方法. * @param args * @throws Exception */ public static void main(String[] args) throws Exception { BusinessObj obj = new BusinessObjImpl(); //創建一個日志攔截器 LogInterceptor interceptor = new LogInterceptor(obj); //通過Proxy類的newProxyInstance(...)方法來獲得動態的代理對象 BusinessObj proxy = (BusinessObj) Proxy.newProxyInstance( BusinessObjImpl.class.getClassLoader(), BusinessObjImpl.class.getInterfaces(), interceptor); //執行動態代理對象的業務邏輯方法 proxy.process(); } }
           
          此時還需要對BusinessObj的實現類BusinessObjImpl類進行修改,去掉其日志記錄等內容,修改后的文件如下:

          package org.amigo.proxy; /** * 業務邏輯對象實現類. * @author <a href="mailto:xiexingxing1121@126.com">AmigoXie</a> * Creation date: 2007-10-7 - 上午09:11:49 */ public class BusinessObjImpl implements BusinessObj { /** * 執行業務. */ public void process() { System.out.println("執行業務邏輯"); } }
          運行LogInterceptor類我們可以發現,它實現了前面所需要的功能,但是很好的將業務邏輯方法的代碼和日志記錄的代碼分離開來,并且所有的業務處理對象都可以利用該類來完成日志的記錄,防止了重復代碼的出現,增加了程序的可擴展性和可維護性,從而提高了代碼的質量。那么Spring中的AOP的實現是怎么樣的呢?接著讓我們來對Spring的AOP進行探討,了解其內部實現原理。
          三.Spring中AOP的模擬實現
                 在學習了Java的動態代理機制后,我們在本節中將學習Java的動態代理機制在Spring中的應用。首先我們創建一個名為AopHandler的類,該類可生成代理對象,同時可以根據設置的前置或后置處理對象分別在方法執行前后執行一些另外的操作,該類的內容如下所示:

          package org.amigo.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * AOP處理器. * @author <a href="mailto:xiexingxing1121@126.com">AmigoXie</a> * Creation date: 2007-10-7 - 上午10:13:28 */ public class AopHandler implements InvocationHandler { //需要代理的目標對象 private Object target; //方法前置顧問 Advisor beforeAdvisor; //方法后置顧問 Advisor afterAdvisor; /** * 設置代理目標對象,并生成動態代理對象. * @param target 代理目標對象 * @return 返回動態代理對象 */ public Object setObject(Object target) { //設置代理目標對象 this.target = target; //根據代理目標對象生成動態代理對象 Object obj = Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); return obj; } /** * 若定義了前置處理,則在方法執行前執行前置處理, * 若定義了后置處理,則在方法調用后調用后置處理. * @param proxy 代理對象 * @param method 調用的業務方法 * @param args 方法的參數 * @return 返回結果信息 * @throws Throwable */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //進行業務方法的前置處理 if (beforeAdvisor != null) { beforeAdvisor.doInAdvisor(proxy, method, args); } //執行業務方法 Object result = method.invoke(target, args); //進行業務方法的后置處理 if (afterAdvisor != null) { afterAdvisor.doInAdvisor(proxy, method, args); } //返回結果對象 return result; } /** * 設置方法的前置顧問. * @param advisor 方法的前置顧問 */ public void setBeforeAdvisor(Advisor advisor) { this.beforeAdvisor = advisor; } /** * 設置方法的后置顧問. * @param advisor 方法的后置顧問 */ public void setAfterAdvisor(Advisor advisor) { this.afterAdvisor = advisor; } }
              在上類中,前置和后置顧問對象都繼承Advisor接口,接下來讓我們來看看顧問接口類的內容,該類定義了doInAdvisor(Object proxy, Method method, Object[] args)方法,如下所示:
          package org.amigo.proxy; import java.lang.reflect.Method; /** * * 顧問接口類. * @author <a href="mailto:xiexingxing1121@126.com">AmigoXie</a> * Creation date: 2007-10-7 - 上午10:21:18 */ public interface Advisor { /** * 所做的操作. */ public void doInAdvisor(Object proxy, Method method, Object[] args); } BeforeMethodAdvisor和AfterMethodAdvisor都實現了Advisor接口,分別為方法的前置顧問和后置顧問類。 BeforeMethodAdvisor.java文件(前置顧問類)的內容如下所示: package org.amigo.proxy; import java.lang.reflect.Method; /** * * 方法前置顧問,它完成方法的前置操作. * @author <a href="mailto:xiexingxing1121@126.com">AmigoXie</a> * Creation date: 2007-10-7 - 上午10:19:57 */ public class BeforeMethodAdvisor implements Advisor { /** * 在方法執行前所進行的操作. */ public void doInAdvisor(Object proxy, Method method, Object[] args) { System.out.println("before process " + method); } } AfterMethodAdvisor.java文件(后置顧問類)的內容如下所示: package org.amigo.proxy; import java.lang.reflect.Method; /** * * 方法的后置顧問,它完成方法的后置操作. * @author <a href="mailto:xiexingxing1121@126.com">AmigoXie</a> * Creation date: 2007-10-7 - 上午10:20:43 */ public class AfterMethodAdvisor implements Advisor { /** * 在方法執行后所進行的操作. */ public void doInAdvisor(Object proxy, Method method, Object[] args) { System.out.println("after process " + method); } }
          這兩個類分別在方法執行前和方法執行后做一些額外的操作。
                 對于在配置文件中對某個bean配置前置或后置處理器,我們可以在bean中增加兩個屬性aop和aopType,aop的值為對應的前置顧問類或后置顧問類的名稱,aopType用于指明該顧問類為前置還是后置顧問,為before時表示為前置處理器,為after時表示為后置處理器,這時候我們需要修改上一篇文章中的BeanFactory.java這個文件,添加其對aop和aopType的處理,修改后的該文件內容如下:

          package org.amigo.proxy; 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-7 - 下午04:04:34 */ 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(); InputStream ins = classLoader.getResourceAsStream(xml); Document doc = reader.read(ins); Element root = doc.getRootElement(); Element foo; //創建AOP處理器 AopHandler aopHandler = new AopHandler(); //遍歷bean for (Iterator i = root.elementIterator("bean"); i.hasNext();) { foo = (Element) i.next(); //獲取bean的屬性id、class、aop以及aopType Attribute id = foo.attribute("id"); Attribute cls = foo.attribute("class"); Attribute aop = foo.attribute("aop"); Attribute aopType = foo.attribute("aopType"); //配置了aop和aopType屬性時,需進行攔截操作 if (aop != null && aopType != null) { //根據aop字符串獲取對應的類 Class advisorCls = Class.forName(aop.getText()); //創建該類的對象 Advisor advisor = (Advisor) advisorCls.newInstance(); //根據aopType的類型來設置前置或后置顧問 if ("before".equals(aopType.getText())) { aopHandler.setBeforeAdvisor(advisor); } else if ("after".equals(aopType.getText())) { aopHandler.setAfterAdvisor(advisor); } } //利用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); } } } //為對象增加前置或后置顧問 obj = (Object) aopHandler.setObject(obj); //將對象放入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; } /** * 測試方法. * @param args */ public static void main(String[] args) { BeanFactory factory = new BeanFactory(); factory.init("config.xml"); BusinessObj obj = (BusinessObj) factory.getBean("businessObj"); obj.process(); } }
              觀察此類我們可以發現,該類添加了對bean元素的aop和aopType屬性的處理。編寫完該文件后,我們還需要修改src目錄下的配置文件:config.xml文件,在該文件中添加名為businessObj的bean,我們為其配置了aop和aopType屬性,增加了方法的前置顧問。增加的部分為:

          <bean id="businessObj" class="org.amigo.proxy.BusinessObjImpl" aop="org.amigo.proxy.BeforeMethodAdvisor" aopType="before"/>
                 此時運行BeanFactory.java這個類文件,運行結果如下:
          before process public abstract void org.amigo.proxy.BusinessObj.process()
          執行業務邏輯
                 由運行結果可以看出,前置處理已經生效。
                 本節中的例子只是實現了Spring的AOP一小部分功能,即為某個bean添加前置或后置處理,在Spring中,考慮的比這多很多,例如,為多個bean配置動態代理等等。但是究其根源,Spring中AOP的實現,是基于Java中無比強大的反射和動態代理機制。
           
          四.總結
                 本文講述了AOP的概念等信息,并詳細講解了Java的動態代理機制,接著又通過一個簡單的實例講解了Spring中AOP的模擬實現,使得讀者能夠更好地學習Java的反射和代理機制。
          通過這兩篇文章,使得我們能夠更加深入地理解Java的反射和動態代理機制,同時對Spring中盛行的IOC和AOP的后臺實現原理有了更加清晰的理解,Java的反射和動態代理機制的強大功能在這兩篇文章中可見一斑。有興趣的朋友可以通過學習Spring框架的源碼來進一步的理解Java的反射和動態代理機制,從而在實際的開發工作中更好地理解
          posted @ 2009-04-06 11:50 lanxin1020 閱讀(170) | 評論 (0)編輯 收藏
               摘要: spring IOC 機制模擬實現收藏            在Java中,其反射和動態代理機制極其強大,我們可以通過其反射機制在運行時獲取信息。而代理是一種基本的設計模式,它是一種為了提供額外的或不同的操作而插入到真實對象中的某個對象。而Java的動態代理在代理上更進一步,既能動態的創建代理對象,又...  閱讀全文
          posted @ 2009-04-06 11:43 lanxin1020 閱讀(473) | 評論 (0)編輯 收藏

          工廠模式是最重要的模式,因為大多數模式都需要用到工廠模式。如果不能正確的運用工廠模式,那么可以說無法成為合格的架構師。
          多數設計模式的內容講解的都是如何設計接口。
          接口如何產生呢?如果在客戶代碼(類庫的使用者稱之為客戶)中直接使用具體類,那么就失去了接口的意義。因為接口的使用目的,就是要降低客戶對具體類的依賴程度。如果在客戶代碼中直接使用接口,那么就造成了客戶對具體類名稱的依賴。(客戶最終需要以某種方式指明所需要的具體類,如配置文件或代碼,但是只需要指出一次,所以說降低對具體類的依賴程度)。要使客戶代碼不依賴具體類,唯一的方法,就是讓客戶代碼不依賴具體類的部分不知道具體類的名稱。知道具體類名稱的部分,僅僅是配置部分。(配置文件或者配置代碼)。
          依賴不可能完全消除,除非二者毫無聯系。但是可以將這種依賴的程度降到最低。
          既然不能直接創建具體類,那么就需要通過一個創建者類來創建接口的實現類。這樣就產生了工廠類。
          那么現在已經知道工廠類存在的理由,抽象創建接口的過程。
          這樣,就可以使用簡單工廠。
          簡單工廠,一般是兩級結構。工廠類創建接口。
          隨著接口創建復雜性的增強,可能在接口創建的過程中,一個創建者類,無法承擔創建所有的接口類的職責。
          可能會有這樣的情況,我們定義了一個接口,有6個實現類分別是123456號。但是,這六個實現類不可能用一個工廠創建出來,因為123號是 windows下的實現,而456號是linux上的實現。(假設我們使用的語言不是廣大人民群眾熱愛的java語言),那么這個時候,我還需要客戶方用相同的方式來創建這個借口,而不是在代碼中到處寫

          代碼
               if  (操作系統 == " windows " ){  
                
              }  
               
          else {  
                
              }  

          那樣就太麻煩了。設計模式就是為了減少麻煩,而不是什么別的廢話,比如什么太極八卦、天人合一、面向xx之類的。因為怕麻煩,所以搞出設計模式這個咚咚減少麻煩。如果你發現用了設計模式更麻煩了,那么肯定是你用錯了。
          這個時候為了省事,我就把工廠也抽象成一個接口(因為我有兩個相似的工廠,如果只有一個,我還廢話什么呢),這樣就成了工廠方法。
          當然,既然工廠方法成了一個接口,那么當然也需要用一個工廠來創建它。這個時候,創建是三級結構,簡單工廠(此時是工廠的工廠)創建工廠接口(本來是個類,現在因為進一步的抽象,成為接口了),工廠接口創建產品。
          過了一段時間,隨著我們的工廠業務不斷發展,我們有了很多產品。
          比如,我們有錘子和釘子兩種產品。這兩種產品都有windows品牌和linux品牌的。我們給錘子和釘子各自定義了一個創建的接口。
          代碼
              interface 錘子工廠{  
              造錘子();  
              }  
              
          interface 釘子工廠{  
              造釘子();  
              }  

          可是,我們發現某些用戶,用windows的錘子去敲linux的釘子,從而把程序敲出了bug。這當然是我們的錯誤,因為我們違反了一條金科玉律:
          要想使你的程序穩定運行,你假設用戶是豬。
          所以,我們把錘子和釘子的工廠合并,讓一個工廠只能造出配套的錘子和釘子,這樣豬就沒有犯錯誤的機會了。
          于是我們搞出一個抽象工廠:
          interface 鐵匠鋪{
          造錘子();
          造釘子();

          當然,這個鐵匠鋪是個接口,所以同樣需要用一個工廠來創建它。所以,這個時候,工廠還是三級結構。
          我們的工廠,業務很多,而且產品一般都是配套使用的(這樣可以多騙點錢),所以,我們大多數情況下,都是使用抽象工廠和簡單工廠。簡單工廠用來創建工廠,抽象工廠創建產品。
          工廠的作用,就是創建接口。
          其實我們不知道什么是設計模式,我們只是怕麻煩。什么是麻煩呢?
          我們覺得把同樣的代碼寫兩遍就非常麻煩。所以,我們寧可多寫幾句,也要解決麻煩。豬不怕麻煩,可以日復一日的重復相同的事情,可是我們不是豬。





          例子:
          public interface Plant { }//標志接口     
        1. //具體產品PlantA,PlantB       
        2. public class PlantA implements Plant {       
        3.      
        4.  public PlantA () {       
        5.   System.out.println("create PlantA !");       
        6.  }       
        7.      
        8.  public void doSomething() {       
        9.   System.out.println(" PlantA do something ...");       
        10.  }       
        11. }       
        12. public class PlantB implements Plant {       
        13.  public PlantB () {       
        14.   System.out.println("create PlantB !");       
        15.  }       
        16.      
        17.  public void doSomething() {       
        18.   System.out.println(" PlantB do something ...");       
        19.  }       
        20. }       
        21. // 產品 Fruit接口       
        22. public interface Fruit { }       
        23. //具體產品FruitA,FruitB       
        24. public class FruitA implements Fruit {       
        25.  public FruitA() {       
        26.   System.out.println("create FruitA !");       
        27.  }       
        28.  public void doSomething() {       
        29.   System.out.println(" FruitA do something ...");       
        30.  }       
        31. }       
        32. public class FruitB implements Fruit {       
        33.  public FruitB() {       
        34.   System.out.println("create FruitB !");       
        35.  }       
        36.  public void doSomething() {       
        37.   System.out.println(" FruitB do something ...");       
        38.  }       
        39. }       
        40. // 抽象工廠方法       
        41. public interface AbstractFactory {       
        42.  public Plant createPlant();       
        43.  public Fruit createFruit();       
        44. }       
        45. //具體工廠方法       
        46. public class FactoryA implements AbstractFactory {       
        47.  public Plant createPlant() {       
        48.   return new PlantA();       
        49.  }       
        50.  public Fruit createFruit() {       
        51.   return new FruitA();       
        52.  }       
        53. }       
        54. public class FactoryB implements AbstractFactory {       
        55.  public Plant createPlant() {       
        56.   return new PlantB();       
        57.  }       
        58.  public Fruit createFruit() {       
        59.   return new FruitB();       
        60.  }       
        61. }     
        62.  



          public Client {      

        63.       public method1() {      
        64.              AbstractFactory instance = new FactoryA();      
        65.              instance.createPlant();      
        66.        }      
        67. }  

        68.  

          posted @ 2009-04-06 10:02 lanxin1020 閱讀(146) | 評論 (0)編輯 收藏

          Spring的模塊化是很強的,各個功能模塊都是獨立的,我們可以選擇的使用。這一章先從Spring的IoC開始。所謂IoC就是一個用XML來定義生成對象的模式,我們看看如果來使用的。

             數據模型

             1、如下圖所示有三個類,Human(人類)是接口,Chinese(中國人)是一個子類,American(美國人)是另外一個子類。

          源代碼如下:

          java 代碼
          1. package cn.com.chengang.spring;   
          2. public interface Human {   
          3. void eat();   
          4. void walk();   
          5. }   
          6.   
          7. package cn.com.chengang.spring;   
          8. public class Chinese implements Human {   
          9. /* (非 Javadoc)  
          10. * @see cn.com.chengang.spring.Human#eat()  
          11. */  
          12. public void eat() {   
          13. System.out.println("中國人對吃很有一套");   
          14. }   
          15.   
          16. /* (非 Javadoc)  
          17. * @see cn.com.chengang.spring.Human#walk()  
          18. */  
          19. public void walk() {   
          20. System.out.println("中國人行如飛");   
          21. }   
          22. }   
          23.   
          24. package cn.com.chengang.spring;   
          25. public class American implements Human {   
          26. /* (非 Javadoc)  
          27. * @see cn.com.chengang.spring.Human#eat()  
          28. */  
          29. public void eat() {   
          30. System.out.println("美國人主要以面包為主");   
          31. }   
          32.   
          33. /* (非 Javadoc)  
          34. * @see cn.com.chengang.spring.Human#walk()  
          35. */  
          36. public void walk() {   
          37. System.out.println("美國人以車代步,有四肢退化的趨勢");   
          38. }   
          39. }  

           

          2、對以上對象采用工廠模式的用法如下

             創建一個工廠類Factory,如下。這個工廠類里定義了兩個字符串常量,所標識不同的人種。getHuman方法根據傳入參數的字串,來判斷要生成什么樣的人種。

          java 代碼
          1. package cn.com.chengang.spring;   
          2. public class Factory {   
          3. public final static String CHINESE = "Chinese";   
          4. public final static String AMERICAN = "American";   
          5.   
          6. public Human getHuman(String ethnic) {   
          7. if (ethnic.equals(CHINESE))   
          8. return new Chinese();   
          9. else if (ethnic.equals(AMERICAN))   
          10. return new American();   
          11. else  
          12. throw new IllegalArgumentException("參數(人種)錯誤");   
          13. }   
          14. }  

           

          下面是一個測試的程序,使用工廠方法來得到了不同的“人種對象”,并執行相應的方法。

          java 代碼
          1. package cn.com.chengang.spring;   
          2. public class ClientTest {   
          3. public static void main(String[] args) {   
          4. Human human = null;   
          5. human = new Factory().getHuman(Factory.CHINESE);   
          6. human.eat();   
          7. human.walk();   
          8. human = new Factory().getHuman(Factory.AMERICAN);   
          9. human.eat();   
          10. human.walk();   
          11. }   
          12. }  

           

           3、采用Spring的IoC的用法如下:

             在項目根目錄下創建一個bean.xml文件

          xml 代碼
          1. <?xml version="1.0" encoding="UTF-8"?>   
          2. <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">   
          3. <beans>   
          4. <bean id="Chinese" class="cn.com.chengang.spring.Chinese"/>   
          5. <bean id="American" class="cn.com.chengang.spring.American"/>   
          6. </beans>  

          修改ClientTest程序如下:

          java 代碼
          1. package cn.com.chengang.spring;   
          2. import org.springframework.context.ApplicationContext;   
          3. import org.springframework.context.support.FileSystemXmlApplicationContext;   
          4. public class ClientTest {   
          5. public final static String CHINESE = "Chinese";   
          6. public final static String AMERICAN = "American";   
          7.   
          8. public static void main(String[] args) {   
          9. // Human human = null;   
          10. // human = new Factory().getHuman(Factory.CHINESE);   
          11. // human.eat();   
          12. // human.walk();   
          13. // human = new Factory().getHuman(Factory.AMERICAN);   
          14. // human.eat();   
          15. // human.walk();   
          16.   
          17. ApplicationContext ctx = new FileSystemXmlApplicationContext("bean.xml");   
          18. Human human = null;   
          19. human = (Human) ctx.getBean(CHINESE);   
          20. human.eat();   
          21. human.walk();   
          22. human = (Human) ctx.getBean(AMERICAN);   
          23. human.eat();   
          24. human.walk();   
          25. }   
          26. }  

           從這個程序可以看到,ctx就相當于原來的Factory工廠,原來的Factory就可以刪除掉了。然后又把Factory里的兩個常量移到了ClientTest類里,整個程序結構基本一樣。

             再回頭看原來的bean.xml文件的這一句:

          <bean id="Chinese" class="cn.com.chengang.spring.Chinese"/>

             id就是ctx.getBean的參數值,一個字符串。class就是一個類(包名+類名)。然后在ClientTest類里獲得Chinese對象就是這么一句

          human = (Human) ctx.getBean(CHINESE);

             因為getBean方法返回的是Object類型,所以前面要加一個類型轉換。

             總結

             (1)也許有人說,IoC和工廠模式不是一樣的作用嗎,用IoC好象還麻煩一點。

             舉個例子,如果用戶需求發生變化,要把Chinese類修改一下。那么前一種工廠模式,就要更改Factory類的方法,并且重新編譯布署。而IoC只需要將class屬性改變一下,并且由于IoC利用了Java反射機制,這些對象是動態生成的,這時我們就可以熱插撥Chinese對象(不必把原程序停止下來重新編譯布署)

             (2)也許有人說,即然IoC這么好,那么我把系統所有對象都用IoC方式來生成。

             注意,IoC的靈活性是有代價的:設置步驟麻煩、生成對象的方式不直觀、反射比正常生成對象在效率上慢一點。因此使用IoC要看有沒有必要,我認為比較通用的判斷方式是:用到工廠模式的地方都可以考慮用IoC模式。

             (3)在上面的IoC的方式里,還有一些可以變化的地方。比如,bean.xml不一定要放在項目錄下,也可以放在其他地方,比如cn.com.chengang.spring包里。不過在使用時也要變化一下,如下所示:

          new FileSystemXmlApplicationContext("src/cn/com/chengang/spring/bean.xml");

             另外,bean.xml也可以改成其他名字。這樣我們在系統中就可以分門別類的設置不同的bean.xml。

             (4)關于IoC的低侵入性。

             什么是低侵入性?如果你用過Struts或EJB就會發現,要繼承一些接口或類,才能利用它們的框架開發。這樣,系統就被綁定在Struts、EJB上了,對系統的可移植性產生不利的影響。如果代碼中很少涉及某一個框架的代碼,那么這個框架就可以稱做是一個低侵入性的框架。

             Spring的侵入性很低,Humen.java、Chinese.java等幾個類都不必繼承什么接口或類。但在ClientTest里還是有一些Spring的影子:FileSystemXmlApplicationContext類和ctx.getBean方式等。
          現在,低侵入性似乎也成了判定一個框架的實現技術好壞的標準之一。

             (5)關于bean.xml的用法

             bean.xml的用法還有很多,其中內容是相當豐富的。假設Chinese類里有一個humenName屬性(姓名),那么原的bean.xml修改如下。此后生成Chinese對象時,“陳剛”這個值將自動設置到Chinese類的humenName屬性中。而且由于singleton為true這時生成Chinese對象將采用單例模式,系統僅存在一個Chinese對象實例。

          <?xml version="1.0" encoding="UTF-8"?>
          <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
          <beans>
          <bean id="Chinese" class="cn.com.chengang.spring.Chinese" singleton="true">
          <property name="humenName">
          <value>陳剛</value>
          </property>
          </bean>
          <bean id="American" class="cn.com.chengang.spring.American"/>
          </beans>

             關于bean.xml的其它用法,不再詳細介紹了,大家自己拿Spring的文檔一看就明白了。

           Spring能有效地組織J2EE應用各層的對象。不管是控制層的Action對象,還是業務層的Service對象,還是持久層的DAO對象,都可在Spring的管理下有機地協調、運行。Spring將各層的對象以松耦合的方式組織在一起,Action對象無須關心Service對象的具體實現,Service對象無須關心持久層對象的具體實現,各層對象的調用完全面向接口。當系統需要重構時,代碼的改寫量將大大減少。

            上面所說的一切都得宜于Spring的核心機制,依賴注入。依賴注入讓bean與bean之間以配置文件組織在一起,而不是以硬編碼的方式耦合在一起。理解依賴注入

            依賴注入(Dependency Injection)和控制反轉(Inversion of Control)是同一個概念。具體含義是:當某個角色(可能是一個Java實例,調用者)需要另一個角色(另一個Java實例,被調用者)的協助時,在傳統的程序設計過程中,通常由調用者來創建被調用者的實例。但在Spring里,創建被調用者的工作不再由調用者來完成,因此稱為控制反轉;創建被調用者實例的工作通常由Spring容器來完成,然后注入調用者,因此也稱為依賴注入。

            不管是依賴注入,還是控制反轉,都說明Spring采用動態、靈活的方式來管理各種對象。對象與對象之間的具體實現互相透明。在理解依賴注入之前,看如下這個問題在各種社會形態里如何解決:一個人(Java實例,調用者)需要一把斧子(Java實例,被調用者)。

            (1)原始社會里,幾乎沒有社會分工。需要斧子的人(調用者)只能自己去磨一把斧子(被調用者)。對應的情形為:Java程序里的調用者自己創建被調用者。

            (2)進入工業社會,工廠出現。斧子不再由普通人完成,而在工廠里被生產出來,此時需要斧子的人(調用者)找到工廠,購買斧子,無須關心斧子的制造過程。對應Java程序的簡單工廠的設計模式。

            (3)進入“按需分配”社會,需要斧子的人不需要找到工廠,坐在家里發出一個簡單指令:需要斧子。斧子就自然出現在他面前。對應Spring的依賴注入。

            第一種情況下,Java實例的調用者創建被調用的Java實例,必然要求被調用的Java類出現在調用者的代碼里。無法實現二者之間的松耦合。

            第二種情況下,調用者無須關心被調用者具體實現過程,只需要找到符合某種標準(接口)的實例,即可使用。此時調用的代碼面向接口編程,可以讓調用者和被調用者解耦,這也是工廠模式大量使用的原因。但調用者需要自己定位工廠,調用者與特定工廠耦合在一起。

            第三種情況下,調用者無須自己定位工廠,程序運行到需要被調用者時,系統自動提供被調用者實例。事實上,調用者和被調用者都處于Spring的管理下,二者之間的依賴關系由Spring提供。

            所謂依賴注入,是指程序運行過程中,如果需要調用另一個對象協助時,無須在代碼中創建被調用者,而是依賴于外部的注入。Spring的依賴注入對調用者和被調用者幾乎沒有任何要求,完全支持對POJO之間依賴關系的管理。依賴注入通常有兩種:

            ·設值注入。

            ·構造注入。
          .......

          posted @ 2009-04-06 00:13 lanxin1020 閱讀(717) | 評論 (0)編輯 收藏
               摘要: 最簡單的解決方案 我們先寫一個接口IHello.java代碼如下:  1package sinosoft.dj.aop.staticaop;  2  3public interface IHello {  4    /** *//**  5&nbs...  閱讀全文
          posted @ 2009-04-05 22:40 lanxin1020 閱讀(177) | 評論 (0)編輯 收藏
                 Action類是用戶請求和業務邏輯之間的橋梁,每個Action充當客戶的一項業務代理。在RequestProcessor類預處理請求時,在創建了Action的實例后,就調用自身的processActionPerform()方法,該方法再調用Action類的execute()。
          Action的excute()方法調用模型的業務方法,完成用戶請求,然后根據執行結果把請求轉發給其他合適的WEB組件。

          一、Action類緩存

                struts應用的生命周期中RequestProcessor只保證一個Action實例,所有的客戶請求都共享這個實例.所有請求可以同時執行它的excute()方法。RequestProcessor類包含一個HashMap,作為存放所有Action實例的緩存。每個Action實例在緩存中存放的key為Action類名。在RequestProcessor類的processActionCreate()方法中,首先檢查在HashMap中是否存在Action實例,如果有直接使用,否則創建一個新的。創建Action實力的代碼位于同步代碼塊中,以保證只有一個線程創建Action實例,然后放在HashMap中。供其他線程使用。
          如下代碼
        69. protected Action processActionCreate(HttpServletRequest request,   
        70.                                        HttpServletResponse response,   
        71.                                        ActionMapping mapping)   
        72.       throws IOException 
        73. {   
        74.       // Acquire the Action instance we will be using (if there is one)   
        75.       String className = mapping.getType();   
        76.       if (log.isDebugEnabled()) 
        77.      {   
        78.           log.debug(" Looking for Action instance for class " + className);   
        79.       }   
        80.   
        81.       // :TODO: If there were a mapping property indicating whether   
        82.       // an Action were a singleton or not ([true]),   
        83.       // could we just instantiate and return a new instance here?   
        84.   
        85.       Action instance = null;   
        86.       synchronized (actions) 
        87.      {   
        88.           // Return any existing Action instance of this class   
        89.           instance = (Action) actions.get(className);   
        90.           if (instance != null)
        91.          {   
        92.               if (log.isTraceEnabled()) 
        93.              {   
        94.                   log.trace("  Returning existing Action instance");   
        95.               }   
        96.               return (instance);   
        97.           }   
        98.   
        99.           // Create and return a new Action instance   
        100.           if (log.isTraceEnabled()) 
        101.          {   
        102.               log.trace("  Creating new Action instance");   
        103.           }   
        104.              
        105.           try
        106.          {   
        107.               instance = (Action) RequestUtils.applicationInstance(className);   
        108.               // :TODO: Maybe we should propagate this exception   
        109.               // instead of returning null.   
        110.           } 
        111.           catch (Exception e) 
        112.          {   
        113.               log.error(   
        114.                   getInternal().getMessage("actionCreate", mapping.getPath()),   
        115.                   e);   
        116.                      
        117.               response.sendError(   
        118.                   HttpServletResponse.SC_INTERNAL_SERVER_ERROR,   
        119.                   getInternal().getMessage("actionCreate", mapping.getPath()));                         
        120.               return (null);   
        121.           }         
        122.           instance.setServlet(this.servlet);   
        123.           actions.put(className, instance);   
        124.       }   
        125.   
        126.       return (instance);   
        127.   
        128.   }  

           

          二.創建支持多線程的Action
          1.什么是線程安全的代碼
          在多線程環境下能正確執行的代碼就是線程安全的。
          安全的意思是能正確執行,否則后果是程序執行錯誤,可能出現各種異常情況。

          2.如何編寫線程安全的代碼
          很多書籍里都詳細講解了如何這方面的問題,他們主要講解的是如何同步線程對共享資源的使用的問題。主要是對synchronized關鍵字的各種用法,以及鎖的概念。
          Java1.5中也提供了如讀寫鎖這類的工具類。這些都需要較高的技巧,而且相對難于調試。

          但是,線程同步是不得以的方法,是比較復雜的,而且會帶來性能的損失。等效的代碼中,不需要同步在編寫容易度和性能上會更好些。
          我這里強調的是什么代碼是始終為線程安全的、是不需要同步的。如下:
          1)常量始終是線程安全的,因為只存在讀操作。
          2)對構造器的訪問(new 操作)是線程安全的,因為每次都新建一個實例,不會訪問共享的資源。
          3)最重要的是:局部變量是線程安全的。因為每執行一個方法,都會在獨立的空間創建局部變量,它不是共享的資源。局部變量包括方法的參數變量。

          Servlet是在多線程環境下的。即可能有多個請求發給一個servelt實例,每個請求是一個線程。 struts下的action也類似,同樣在多線程環境下,你也必須編寫線程安全的Action類。
          保證線程安全的原則就是僅僅使用局部變量,謹慎使用實例變量(擁有狀態的實例,尤其是擁有業務對象狀態的實例). 如果要用到那些有狀態的實例,唯一和最好的辦法是在Action類中,僅僅在Action類的execute()方法中使用局部變量,對于每個調用execute()方法的線程,JVM會在每個線程的堆棧中創建局部變量,因此每個線程擁有獨立的局部變量,不會被其他線程共享.當線程執行完execute()方法后,它的局部變量就會被銷毀.
          如果Action類的實例變量是必須的話,需要采用JAVA同步機制(synchronized)對訪問共享資源的代碼塊進行同步


          三、Struts的幾種Action
          Struts提供了一些現成的Action類,直接使用可以大大節省時間,如下
          ForwardAction
          可以轉發到其他web組件,僅僅提供一個轉發功能,不作處理。
          IncludeAction
          包含其他web組件。
          DiapatchAction
          通常一個Action只完成一個操作,用這個Action可以完成一組相關的操作。
          LookupDispatchAction
          他是DiapatchAction的子類,也可以定義多個方法,但主要用于一個表單里有多個按鈕,而這些按鈕又有一個共同的名字的場合。
          SwitchAction
          用于子模塊之間的切換。


          四.ActionForward類
          Action類的excute()方法返回一個ActionForward對象,它代表了web資源的邏輯抽象,這里的web資源可以是jsp頁面、Java servlet、或Action。
          從excute返回ActionForward可以有兩種方法。
          1) 動態創建一個ActionForward實例
          return new ActionForward(”Failure”,”login.jsp”,true);
          2) 調用ActionMappin實例的findForward方法
          這個方法先從action級別找,然后在<global-forwards />級別找
          return mapping.findForward(“Failure”);

        129. posted @ 2009-04-05 11:19 lanxin1020 閱讀(195) | 評論 (0)編輯 收藏
          Struts框架只允許應用中存在一個ActionServlet類,但是可以存在多個客戶化的RequestProcessor類,每個子應用模塊都可以有單獨的RequestProcessor類,

          ActionServlet主要負責初始化,以及介紹請求并找到合適的RequestRrocessor,之后真正干活的是RequestProecssor和Action.
          上回說到ActionServlet的process方法最終會調用RequestProcessor類的process方法.下面介紹這個方法.
          一.RequestProcessor的process方法
          public void process(HttpServletRequest request,   
        130.                         HttpServletResponse response)   
        131.         throws IOException, ServletException 
        132. {   
        133.         // Wrap multipart requests with a special wrapper   
        134.         request = processMultipart(request);   
        135.         // Identify the path component we will use to select a mapping   
        136.         String path = processPath(request, response);   
        137.         if (path == null
        138.        {   
        139.             return;   
        140.         }    
        141.         if (log.isDebugEnabled()) 
        142.        {   
        143.             log.debug("Processing a '" + request.getMethod() +   
        144.                       "' for path '" + path + "'");   
        145.         }   
        146.         // Select a Locale for the current user if requested   
        147.         processLocale(request, response);   
        148.         // Set the content type and no-caching headers if requested   
        149.         processContent(request, response);   
        150.         processNoCache(request, response);   
        151.         // General purpose preprocessing hook   
        152.         if (!processPreprocess(request, response)) 
        153.         {   
        154.             return;   
        155.         }   
        156.         this.processCachedMessages(request, response);   
        157.         // Identify the mapping for this request   
        158.         ActionMapping mapping = processMapping(request, response, path);   
        159.         if (mapping == null)
        160.        {   
        161.             return;   
        162.         }   
        163.         // Check for any role required to perform this action   
        164.         if (!processRoles(request, response, mapping)) 
        165.        {   
        166.             return;   
        167.         }   
        168.         // Process any ActionForm bean related to this request   
        169.         ActionForm form = processActionForm(request, response, mapping);   
        170.         processPopulate(request, response, form, mapping);   
        171.         // Validate any fields of the ActionForm bean, if applicable   
        172.         try
        173.        {   
        174.             if (!processValidate(request, response, form, mapping)) 
        175.            {   
        176.                 return;   
        177.             }   
        178.         } 
        179.        catch (InvalidCancelException e) 
        180.        {   
        181.             ActionForward forward = processException(request, response, e, form, mapping);   
        182.             processForwardConfig(request, response, forward);   
        183.             return;   
        184.         } catch (IOException e) 
        185.        {   
        186.             throw e;   
        187.         } catch (ServletException e) 
        188.        {   
        189.             throw e;   
        190.         }   
        191.                
        192.         // Process a forward or include specified by this mapping   
        193.         if (!processForward(request, response, mapping))
        194.        {   
        195.             return;   
        196.         }   
        197.         if (!processInclude(request, response, mapping)) 
        198.        {   
        199.             return;   
        200.         }   
        201.         // Create or acquire the Action instance to process this request   
        202.         Action action = processActionCreate(request, response, mapping);   
        203.         if (action == null)
        204.         {   
        205.             return;   
        206.         }   
        207.         // Call the Action instance itself   
        208.         ActionForward forward =   
        209.             processActionPerform(request, response,   
        210.                                  action, form, mapping);   
        211.   
        212.         // Process the returned ActionForward instance   
        213.         processForwardConfig(request, response, forward);   
        214.   
        215.     }   

          1) 調用processMultipart()方法
          如果HTTP請求方式為post,并且contentType為”multipart/form-data”開頭,標準的HttpServletRequest對象將被重新包裝,以方便處理”multipart”類型的HTTP請求.如果請求方式為get,或正congtentType屬性不是”mulitipart”,就直接返回原始的HttpServletRequest對象.

          2) 調用processPath()方法
          獲得請求的URI的路徑,這一信息可用于選擇合適的Struts Action組件.

          3) 調用processLocale方法
          當ControllerConfig對象的locale屬性為true,將讀取用戶請求中包含的Locale信息,然后把Locale實例保存在session范圍內.

          4) 調用processContendType(contentType)方法
          讀取ControllerConfig對象的conttentType屬性,然后調用response.setContentType(contentType)方法,設置響應結果的文檔類型和字符編碼.
          processContent()方法如下

        216. protected void processContent(HttpServletRequest request,   
        217.                                  HttpServletResponse response) 
        218.  {   
        219.   
        220.        String contentType = moduleConfig.getControllerConfig().getContentType();   
        221.        if (contentType != null
        222.       {   
        223.            response.setContentType(contentType);   
        224.        }   
        225.   
        226.    }   

           


          5) 調用processNoCache()方法
          讀取ControllerConfig對象的nocache屬性,如果nocache屬性為true,在響應結果中將加入特定的頭參數:Pragma,Cache-Control和Expires,
          防止頁面被存儲在客戶的瀏覽器的緩存中,processNoCache方法的代碼如下:

        227. protected void processNoCache(HttpServletRequest request,   
        228.                                   HttpServletResponse response) 
        229. {   
        230.   
        231.         if (moduleConfig.getControllerConfig().getNocache()) 
        232.         {   
        233.             response.setHeader("Pragma""No-cache");   
        234.             response.setHeader("Cache-Control""no-cache,no-store,max-age=0");   
        235.             response.setDateHeader("Expires"1);   
        236.         }   
        237.     }  



          6)調用processPreprocess()方法
          該方法不執行任何操作.直接返回true.子類可以覆蓋這個方法.
          執行客戶化的預處理請求操作.

          7)調用processMapping()方法
          尋找和用戶請求的URI匹配的ActionMapping,如果不存在這樣的ActionMapping,則向用戶返回恰當的錯誤信息.

          8)調用processRoles()方法
          先判斷是否為Action配置了安全角色,如果配置了安全角色,就調用isUserInRole()方法判斷當前用戶是否具備必需的角色,如果不具備,就結束請求處理流程.,向用戶返回恰當的錯誤消息.

          9)調用processActionForm()方法
          先判斷是否為ActionMapping配置了ActionForm,如果配置了ActionForm,就先從ActionForm的存在范圍內(request或session)尋找改ActionForm實例,如果不存在,就創建一個實例,接下來把它保存在合適的范圍內,保存時使用的屬性key為ActionMapping的name屬性。

          10)調用processPopulate()方法
          如果為ActionMapping配置了ActionForm,就先調用ActionForm的reset()方法,再把請求中的表單數據組裝到ActionForm中。

          11)調用processValidate()方法
          如果為ActionMapping配置了ActionForm,并且ActionMapping的validate屬性為true,就調用ActionForm的validate()方法,如果validate方法返回的ActionErrors對象中包含ActionMessage對象,說明表單驗證失敗。就把ActionErrors對象放在request范圍內,再把請求轉發到ActionMapping的input屬性指定的Web組件。如果ActionForm的validate方法執行表單驗證成功,就繼續執行下面的處理流程。

          12)調用processForward()方法
          判斷是否在ActionMapping中配置了forward屬性。如果配置了這個屬性,就調用RequestDispatcher的forward方法,請求處理流程結束。否則進行下一步。

          13)調用processInclude()方法
          判斷是否在ActionMapping中配置了include屬性。如果配置了這個屬性,就調用RequestDispatcher的include方法,請求處理流程結束。否則進行下一步。

          14)調用processActionCreate()方法
          先判斷是否在Action緩存中存在這個Action實例,如果沒有就新建一個Action實例,把它放在Action緩存中??梢钥闯鯝ction也是只有一個實例在運行的。

          15)調用processActionPerform
          該方法調用Action實例的execute方法,該方法位于try/catch中,以及捕獲異常。processActionPerform()方放代碼如下。

        238. protected ActionForward   
        239.        processActionPerform(HttpServletRequest request,   
        240.                             HttpServletResponse response,   
        241.                             Action action,   
        242.                             ActionForm form,   
        243.                             ActionMapping mapping)   
        244.        throws IOException, ServletException {   
        245.        try 
        246.       {   
        247.            return (action.execute(mapping, form, request, response));   
        248.        } catch (Exception e)
        249.        {   
        250.            return (processException(request, response,   
        251.                                     e, form, mapping));   
        252.        }   
        253.    

           


          16)調用processActionForward方法
          把你的Action的excute方法返回的ActionFoward對象作為參數傳給它,processActionForward對象包的請求轉發信息來執行請求轉發或重定向。

          RequestProcessor類的process方法中,會訪問ControllerConfig、ActionMappig和ActionForward實力的屬性,ControllerConfig類和struts配置文件的<controlle>r元素對應,ActionMapping類和<action>元素對應,ActionForward和<forward>元素對應,process方法通過訪問這三個類實例的屬性來獲得相關的配置信息。
          寫了這么多,RequestProcessor干得事夠多的吧。

          二.擴展RequestProcessor
          如果想修改RequestProcessor的一些默認功能,改易覆蓋RequestProcessor基類中的相關方法.

        254. Public class CustomRequestProcessor extends RequestProcessor{   
        255.   protected void processPreprocess (HttpServletRequest request,   
        256.                                  HttpServletResponse response) {    
        257. ………………….   
        258. }   
        259. }  

          在struts配置文件中,<controller>元素的processorClass屬性用于配置你自己的RequestProcessor

        260. </controller    
        261. contentType=“text/html:charset=”GB2312”   
        262. locale=”true” nocache=”true” processorCalss=”com.test.CustomRequestProcessor”/>  

           




           

        263. posted @ 2009-04-05 11:15 lanxin1020 閱讀(186) | 評論 (0)編輯 收藏
                  大家都知道,Struts控制器組件負責接受用戶請求,更通模型,以及返回給用戶合適的視圖組件.
          控制器將模型層和視圖層分開,這樣分離,可以為同一個模型開發出不同的視圖.
                  下面時Struts的三大主要組件
          ActionServlet組件:充當Struts框架的中央控制器
          RequestProcessor組件:充當每個子應用模塊的請求處理器
          Action組件:真正來處理一項具體的業務.

          一. Struts的init()方法
          Struts應用中只存在ActionServlet的一個實例,Servlet容器在啟動時,或者用戶首次請求ActionServlet時加載ActionServlet類.在這兩種情況下,servlet容器都會在ActionServlet容器被加載后立即執行它的init()方法,這可以保證ActionServlet處理用戶請求時已經被初始化.

          下面根據Init()講述Struts的初始化過程
        264. public void init() throws ServletException {   
        265.   
        266.         // Wraps the entire initialization in a try/catch to better handle   
        267.         // unexpected exceptions and errors to provide better feedback   
        268.         // to the developer   
        269.         try {   
        270. //調用initInternal()方法,初始化Struts框架內的消息資源,如與系統日志相關的通知,警告,和錯誤消息.   
        271. 1)initInternal();   
        272.   
        273. //調用ininOther()方法,從web.xml文件中加載ActionServlet的初始化參數,如config參數   
        274. 2)initOther();   
        275.   
        276. //調用initServlet()方法,從web.xml文件中加載ActionServlet的URL映射信息.同時還會注冊web.xml文件和Struts配置文件所使用的DTD文件,這些DTD文件用戶驗證web.xml和struts配置文件的語法.其中方法里的 digester類負責解析web.xml,對字符串servletMapping屬性進行初始化   
        277. 3) initServlet();   
        278.   
        279. //把ActionServlet實例放到ServletContext里   
        280. getServletContext().setAttribute(Globals.ACTION_SERVLET_KEY, this);   
        281.   
        282. //初始化一個factory,用于創建moduleConfig   
        283. initModuleConfigFactory();   
        284.   
        285. //,加載并解析默認struts配置文件/WEB-INF/struts-config.xml,同時創建MoudleConfig實例,放到ServletContext中   
        286. 4)ModuleConfig moduleConfig = initModuleConfig("", config);   
        287.   
        288. //加載并初始化默認子應用模塊的消息資源;講解MessageResources對象,把它存儲在ServletContext中.   
        289. 5)initModuleMessageResources(moduleConfig);   
        290.   
        291. //加載并初始化默認子應用模塊的數據源,如果在struts配置文件中沒有定義<data-sources >元素,忽略這一流程.   
        292. 6)initModuleDataSources(moduleConfig);   
        293.   
        294. //加載并初始化默認子應用的所有插件   
        295. 7)initModulePlugIns(moduleConfig);   
        296.   
        297. //凍結moduleConfig(,在方法返回之前不能修改它,否則將拋出異常)   
        298. moduleConfig.freeze();   
        299.            
        300. //如果還有其他子應用模塊,將重復4-7步   
        301.       Enumeration names = getServletConfig().getInitParameterNames();   
        302.             while (names.hasMoreElements()) {   
        303.                 String name = (String) names.nextElement();   
        304.                 if (!name.startsWith("config/")) {   
        305.                     continue;   
        306.                 }   
        307.                 String prefix = name.substring(6);   
        308.                 moduleConfig = initModuleConfig   
        309.                     (prefix, getServletConfig().getInitParameter(name));   
        310.                 initModuleMessageResources(moduleConfig);   
        311.                 initModuleDataSources(moduleConfig);   
        312.                 initModulePlugIns(moduleConfig);   
        313.                 moduleConfig.freeze();   
        314.      }   
        315.   
        316. //將各個子模塊應用(除了默認的)的前綴存到一個字符數組中,并放到servletcontext中   
        317. this.initModulePrefixes(this.getServletContext());   
        318. //釋放創建的用于讀取配置文件的digester實例,釋放內存   
        319.             this.destroyConfigDigester();   
        320.         } catch (UnavailableException ex) {   
        321.             throw ex;   
        322.         } catch (Throwable t) {   
        323.             // The follow error message is not retrieved from internal message   
        324.             // resources as they may not have been able to have been    
        325.             // initialized   
        326.             log.error("Unable to initialize Struts ActionServlet due to an "  
        327.                 + "unexpected exception or error thrown, so marking the "  
        328.                 + "servlet as unavailable.  Most likely, this is due to an "  
        329.                 + "incorrect or missing library dependency.", t);   
        330.             throw new UnavailableException(t.getMessage());   
        331.         }       
        332. }  
        333.        將各個子模塊應用(除了默認的)的前綴存到一個字符數組中,并放到servletcontext中,對于默認的子應用模塊,在appclication范圍內存放他的MoudleConfig實例的key為“org.apache.struts.action.MODULE”,其他模塊如/account,存放的key為org.apache.struts.action.MODULE/account,消息,數據源和插件等部分存在servletcontext的key和上述方法類似,不在說明.


          二.ActionServlet的process方法
          ActionServlet接受到HTTP請求后,在doget()或doPost()方法中都會調用process()方法來處理請求.

        334.  public void doGet(HttpServletRequest request,   
        335.               HttpServletResponse response)   
        336.         throws IOException, ServletException {   
        337.   
        338.         process(request, response);   
        339.   
        340. }   

        341.   public void doPost(HttpServletRequest request,   

        342.                HttpServletResponse response)   
        343.         throws IOException, ServletException {   
        344.   
        345.         process(request, response);   
        346.   
        347. }   
        348. 下面是process方法,它看上去并不復雜,但他調用的其他方法比較復雜. 
           protected void process(HttpServletRequest request, HttpServletResponse response)   

        349.         throws IOException, ServletException {   
        350.         //根據request里的信息從servletContext里找到相應的子模塊ModuleConfig,和它下面的MessageResources,并放到request里,使其他組件可以方便的供request里取得應用配置信息和消息資源.   
        351.         ModuleUtils.getInstance().selectModule(request, getServletContext());   
        352. //取出MoudleConfig實例config   
        353.         ModuleConfig config = getModuleConfig(request);   
        354.     //根據config里這個子模塊的信息,從servletcontext里,取出這個子模塊的RequestProcessor實例   
        355.         RequestProcessor processor = getProcessorForModule(config);   
        356.     //如果processor實例為空,就新建一個.同時放到servletcontext里.   
        357.         if (processor == null) {   
        358.            processor = getRequestProcessor(config);   
        359.         }   
        360. //調用RequestProcessor的process方法處理,   
        361.         processor.process(request, response);   
        362.     }  

        363. 三. 擴展ActionServlet
          從Struts1.1開始,為減輕ActionServlet的負擔,多數功能已經移到RequestProcessor類中,所以基本不用擴展ActionServlet

          如果需要創建自己的ActionServlet,則可以創建一個它的子類.覆蓋init()方法(或其他方法),可以寫一些自己的操作,但要先調用super.init();
          定義如下的類:

        364. package sample;    
        365. public class ExtendedActionServlet extends ActionServlet {    
        366.         public void init() throws ServletException {    
        367.                super.init();    
        368.                //do some operations    
        369.                ……………    
        370.         }    
        371. }   


        372. 擴展完類后,還應該在web.xml文件中如下配置:

        373. <servlet>    
        374.         <servlet-name>sample</servlet-name>    
        375.         <servlet-class>sample.ExtendedActionServlet</servlet-class>    
        376. </servlet>    
        377.     
        378. <servlet-mapping>    
        379.        <servlet-name>sample</servlet-name>    
        380.        <url-pattern>/action/*<url-pattern> 



        381. 上面的/action/*表示負責處理所有以/action為前綴的URL,后面的/表示轉義
          posted @ 2009-04-05 11:10 lanxin1020 閱讀(196) | 評論 (0)編輯 收藏
               摘要:         Struts Recipes 的合著者 George Franciscus 將介紹另一個重大的 Struts 整合竅門 —— 這次是將 Struts 應用程序導入 Spring 框架。請跟隨 George,他將向您展示如何改變 Struts 動作,使得管理 Struts 動作就像管理 Spring beans 那...  閱讀全文
          posted @ 2009-04-05 10:17 lanxin1020 閱讀(137) | 評論 (0)編輯 收藏

          三種整合 Struts 應用程序與 Spring 的方式

           

          Struts Recipes 的合著者 George Franciscus 將介紹另一個重大的 Struts 整合竅門 —— 這次是將 Struts 應用程序導入 Spring 框架。請跟隨 George,他將向您展示如何改變 Struts 動作,使得管理 Struts 動作就像管理 Spring beans 那樣。結果是一個增強的 web 框架,這個框架可以方便地利用 Spring AOP 的優勢。

          您肯定已經聽說過控制反轉 (IOC) 設計模式,因為很長一段時間以來一直在流傳關于它的信息。如果您在任何功能中使用過 Spring 框架,那么您就知道其原理的作用。在本文中,我利用這一原理把一個 Struts 應用程序注入 Spring 框架,您將親身體會到 IOC 模式的強大。

          將一個 Struts 應用程序整合進 Spring 框架具有多方面的優點。首先,Spring 是為解決一些關于 JEE 的真實世界問題而設計的,比如復雜性、低性能和可測試性,等等。第二,Spring 框架包含一個 AOP 實現,允許您將面向方面技術應用于面向對象的代碼。第三,一些人可能會說 Spring 框架只有處理 Struts 比 Struts 處理自己好。但是這是觀點問題,我演示三種將 Struts 應用程序整合到 Spring 框架的方法后,具體由您自己決定使用哪一種。

          我所演示的方法都是執行起來相對簡單的,但是它們卻具有明顯不同的優點。我為每一種方法創建了一個獨立而可用的例子,這樣您就可以完全理解每種方法。請參閱 下載 部分獲得完整例子源代碼。請參閱 參考資料,下載 Struts MVC 和 Spring 框架。

          為什么 Spring 這么了不起?

          Spring 的創立者 Rod Johnson 以一種批判的眼光看待 Java™ 企業軟件開發,并且提議很多企業難題都能夠通過戰略地使用 IOC 模式(也稱作依賴注入)來解決。當 Rod 和一個具有奉獻精神的開放源碼開發者團隊將這個理論應用于實踐時,結果就產生了 Spring 框架。簡言之,Spring 是一個輕型的容器,利用它可以使用一個外部 XML 配置文件方便地將對象連接在一起。每個對象都可以通過顯示一個 JavaBean 屬性收到一個到依賴對象的引用,留給您的簡單任務就只是在一個 XML 配置文件中把它們連接好。

          IOC 和 Spring

          IOC 是一種使應用程序邏輯外在化的設計模式,所以它是被注入而不是被寫入客戶機代碼中。將 IOC 與接口編程應用結合,就像 Spring 框架那樣,產生了一種架構,這種架構能夠減少客戶機對特定實現邏輯的依賴。請參閱 參考資料 了解更多關于 IOC 和 Spring 的信息。

          依賴注入是一個強大的特性,但是 Spring 框架能夠提供更多特性。Spring 支持可插拔的事務管理器,可以給您的事務處理提供更廣泛的選擇范圍。它集成了領先的持久性框架,并且提供一個一致的異常層次結構。Spring 還提供了一種使用面向方面代碼代替正常的面向對象代碼的簡單機制。

          Spring AOP 允許您使用攔截器 在一個或多個執行點上攔截應用程序邏輯。加強應用程序在攔截器中的日志記錄邏輯會產生一個更可讀的、實用的代碼基礎,所以攔截器廣泛用于日志記錄。您很快就會看到,為了處理橫切關注點,Spring AOP 發布了它自己的攔截器,您也可以編寫您自己的攔截器。






          整合 Struts 和 Spring

          與 Struts 相似,Spring 可以作為一個 MVC 實現。這兩種框架都具有自己的優點和缺點,盡管大部分人同意 Struts 在 MVC 方面仍然是最好的。很多開發團隊已經學會在時間緊迫的時候利用 Struts 作為構造高品質軟件的基礎。Struts 具有如此大的推動力,以至于開發團隊寧愿整合 Spring 框架的特性,而不愿意轉換成 Spring MVC。沒必要進行轉換對您來說是一個好消息。Spring 架構允許您將 Struts 作為 Web 框架連接到基于 Spring 的業務和持久層。最后的結果就是現在一切條件都具備了。

          在接下來的小竅門中,您將會了解到三種將 Struts MVC 整合到 Spring 框架的方法。我將揭示每種方法的缺陷并且對比它們的優點。 一旦您了解到所有三種方法的作用,我將會向您展示一個令人興奮的應用程序,這個程序使用的是這三種方法中我最喜歡的一種。





          三個小竅門

          接下來的每種整合技術(或者竅門)都有自己的優點和特點。我偏愛其中的一種,但是我知道這三種都能夠加深您對 Struts 和 Spring 的理解。在處理各種不同情況的時候,這將給您提供一個廣闊的選擇范圍。方法如下:

          • 使用 Spring 的 ActionSupport 類整合 Structs
          • 使用 Spring 的 DelegatingRequestProcessor 覆蓋 Struts 的 RequestProcessor
          • 將 Struts Action 管理委托給 Spring 框架

          裝載應用程序環境

          無論您使用哪種技術,都需要使用 Spring 的 ContextLoaderPlugin 為 Struts 的 ActionServlet 裝載 Spring 應用程序環境。就像添加任何其他插件一樣,簡單地向您的 struts-config.xml 文件添加該插件,如下所示:

          <plug-in className=
                                  "org.springframework.web.struts.ContextLoaderPlugIn">
                                  <set-property property=
                                  "contextConfigLocation" value="/WEB-INF/beans.xml"/>
                                  </plug-in>
                                  

          前面已經提到過,在 下載 部分,您能夠找到這三個完全可使用的例子的完整源代碼。每個例子都為一個書籍搜索應用程序提供一種不同的 Struts 和 Spring 的整合方法。您可以在這里看到例子的要點,但是您也可以下載應用程序以查看所有的細節。





          竅門 1. 使用 Spring 的 ActionSupport

          手動創建一個 Spring 環境是一種整合 Struts 和 Spring 的最直觀的方式。為了使它變得更簡單,Spring 提供了一些幫助。為了方便地獲得 Spring 環境,org.springframework.web.struts.ActionSupport 類提供了一個 getWebApplicationContext() 方法。您所做的只是從 Spring 的 ActionSupport 而不是 Struts Action 類擴展您的動作,如清單 1 所示:


          清單 1. 使用 ActionSupport 整合 Struts
          package ca.nexcel.books.actions;
                                  import java.io.IOException;
                                  import javax.servlet.ServletException;
                                  import javax.servlet.http.HttpServletRequest;
                                  import javax.servlet.http.HttpServletResponse;
                                  import org.apache.struts.action.ActionError;
                                  import org.apache.struts.action.ActionErrors;
                                  import org.apache.struts.action.ActionForm;
                                  import org.apache.struts.action.ActionForward;
                                  import org.apache.struts.action.ActionMapping;
                                  import org.apache.struts.action.DynaActionForm;
                                  import org.springframework.context.ApplicationContext;
                                  import org.springframework.web.struts.ActionSupport;
                                  import ca.nexcel.books.beans.Book;
                                  import ca.nexcel.books.business.BookService;
                                  public class SearchSubmit extends ActionSupport {   |(1)
                                  public ActionForward execute(
                                  ActionMapping mapping,
                                  ActionForm form,
                                  HttpServletRequest request,
                                  HttpServletResponse response)
                                  throws IOException, ServletException {
                                  DynaActionForm searchForm = (DynaActionForm) form;
                                  String isbn = (String) searchForm.get("isbn");
                                  //the old fashion way
                                  //BookService bookService = new BookServiceImpl();
                                  ApplicationContext ctx =
                                  getWebApplicationContext();    |(2)
                                  BookService bookService =
                                  (BookService) ctx.getBean("bookService");   |(3)
                                  Book book = bookService.read(isbn.trim());
                                  if (null == book) {
                                  ActionErrors errors = new ActionErrors();
                                  errors.add(ActionErrors.GLOBAL_ERROR,new ActionError
                                  ("message.notfound"));
                                  saveErrors(request, errors);
                                  return mapping.findForward("failure") ;
                                  }
                                  request.setAttribute("book", book);
                                  return mapping.findForward("success");
                                  }
                                  }
                                  

          讓我們快速思考一下這里到底發生了什么。在 (1) 處,我通過從 Spring 的 ActionSupport 類而不是 Struts 的 Action 類進行擴展,創建了一個新的 Action。在 (2) 處,我使用 getWebApplicationContext() 方法獲得一個 ApplicationContext。為了獲得業務服務,我使用在 (2) 處獲得的環境在 (3) 處查找一個 Spring bean。

          這種技術很簡單并且易于理解。不幸的是,它將 Struts 動作與 Spring 框架耦合在一起。如果您想替換掉 Spring,那么您必須重寫代碼。并且,由于 Struts 動作不在 Spring 的控制之下,所以它不能獲得 Spring AOP 的優勢。當使用多重獨立的 Spring 環境時,這種技術可能有用,但是在大多數情況下,這種方法不如另外兩種方法合適。





          竅門 2. 覆蓋 RequestProcessor

          將 Spring 從 Struts 動作中分離是一個更巧妙的設計選擇。分離的一種方法是使用 org.springframework.web.struts.DelegatingRequestProcessor 類來覆蓋 Struts 的 RequestProcessor 處理程序,如清單 2 所示:


          清單 2. 通過 Spring 的 DelegatingRequestProcessor 進行整合
          <?xml version="1.0" encoding="ISO-8859-1" ?>
                                  <!DOCTYPE struts-config PUBLIC
                                  "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN"
                                  "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">
                                  <struts-config>
                                  <form-beans>
                                  <form-bean name="searchForm"
                                  type="org.apache.struts.validator.DynaValidatorForm">
                                  <form-property name="isbn"    type="java.lang.String"/>
                                  </form-bean>
                                  </form-beans>
                                  <global-forwards type="org.apache.struts.action.ActionForward">
                                  <forward   name="welcome"                path="/welcome.do"/>
                                  <forward   name="searchEntry"            path="/searchEntry.do"/>
                                  <forward   name="searchSubmit"           path="/searchSubmit.do"/>
                                  </global-forwards>
                                  <action-mappings>
                                  <action    path="/welcome" forward="/WEB-INF/pages/welcome.htm"/>
                                  <action    path="/searchEntry" forward="/WEB-INF/pages/search.jsp"/>
                                  <action    path="/searchSubmit"
                                  type="ca.nexcel.books.actions.SearchSubmit"
                                  input="/searchEntry.do"
                                  validate="true"
                                  name="searchForm">
                                  <forward name="success" path="/WEB-INF/pages/detail.jsp"/>
                                  <forward name="failure" path="/WEB-INF/pages/search.jsp"/>
                                  </action>
                                  </action-mappings>
                                  <message-resources parameter="ApplicationResources"/>
                                  <controller processorClass="org.springframework.web.struts.
                                  DelegatingRequestProcessor"/> |(1)
                                  <plug-in className="org.apache.struts.validator.ValidatorPlugIn">
                                  <set-property property="pathnames"
                                  value="/WEB-INF/validator-rules.xml,/WEB-INF/validation.xml"/>
                                  </plug-in>
                                  <plug-in className="org.springframework.web.struts.ContextLoaderPlugIn">
                                  <set-property property="csntextConfigLocation" value="/WEB-INF/beans.xml"/>
                                  </plug-in>
                                  </struts-config>
                                  

          我利用了 <controller> 標記來用 DelegatingRequestProcessor 覆蓋默認的 Struts RequestProcessor。下一步是在我的 Spring 配置文件中注冊該動作,如清單 3 所示:


          清單 3. 在 Spring 配置文件中注冊一個動作
          <?xml version="1.0" encoding="UTF-8"?>
                                  <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
                                  "http://www.springframework.org/dtd/spring-beans.dtd">
                                  <beans>
                                  <bean id="bookService" class="ca.nexcel.books.business.BookServiceImpl"/>
                                  <bean name="/searchSubmit"
                                  class="ca.nexcel.books.actions.SearchSubmit"> |(1)
                                  <property name="bookService">
                                  <ref bean="bookService"/>
                                  </property>
                                  </bean>
                                  </beans>
                                  

          注意:在 (1) 處,我使用名稱屬性注冊了一個 bean,以匹配 struts-config 動作映射名稱。SearchSubmit 動作揭示了一個 JavaBean 屬性,允許 Spring 在運行時填充屬性,如清單 4 所示:


          清單 4. 具有 JavaBean 屬性的 Struts 動作
          package ca.nexcel.books.actions;
                                  import java.io.IOException;
                                  import javax.servlet.ServletException;
                                  import javax.servlet.http.HttpServletRequest;
                                  import javax.servlet.http.HttpServletResponse;
                                  import org.apache.struts.action.Action;
                                  import org.apache.struts.action.ActionError;
                                  import org.apache.struts.action.ActionErrors;
                                  import org.apache.struts.action.ActionForm;
                                  import org.apache.struts.action.ActionForward;
                                  import org.apache.struts.action.ActionMapping;
                                  import org.apache.struts.action.DynaActionForm;
                                  import ca.nexcel.books.beans.Book;
                                  import ca.nexcel.books.business.BookService;
                                  public class SearchSubmit extends Action {
                                  private BookService bookService;
                                  public BookService getBookService() {
                                  return bookService;
                                  }
                                  public void setBookService(BookService bookService) { | (1)
                                  this.bookService = bookService;
                                  }
                                  public ActionForward execute(
                                  ActionMapping mapping,
                                  ActionForm form,
                                  HttpServletRequest request,
                                  HttpServletResponse response)
                                  throws IOException, ServletException {
                                  DynaActionForm searchForm = (DynaActionForm) form;
                                  String isbn = (String) searchForm.get("isbn");
                                  Book book = getBookService().read(isbn.trim());  |(2)
                                  if (null == book) {
                                  ActionErrors errors = new ActionErrors();
                                  errors.add(ActionErrors.GLOBAL_ERROR,new ActionError("message.notfound"));
                                  saveErrors(request, errors);
                                  return mapping.findForward("failure") ;
                                  }
                                  request.setAttribute("book", book);
                                  return mapping.findForward("success");
                                  }
                                  }
                                  

          在清單 4 中,您可以了解到如何創建 Struts 動作。在 (1) 處,我創建了一個 JavaBean 屬性。DelegatingRequestProcessor自動地配置這種屬性。這種設計使 Struts 動作并不知道它正被 Spring 管理,并且使您能夠利用 Sping 的動作管理框架的所有優點。由于您的 Struts 動作注意不到 Spring 的存在,所以您不需要重寫您的 Struts 代碼就可以使用其他控制反轉容器來替換掉 Spring。

          DelegatingRequestProcessor 方法的確比第一種方法好,但是仍然存在一些問題。如果您使用一個不同的 RequestProcessor,則需要手動整合 Spring 的 DelegatingRequestProcessor。添加的代碼會造成維護的麻煩并且將來會降低您的應用程序的靈活性。此外,還有過一些使用一系列命令來代替 Struts RequestProcessor 的傳聞。 這種改變將會對這種解決方法的使用壽命造成負面的影響。





          竅門 3. 將動作管理委托給 Spring

          一個更好的解決方法是將 Strut 動作管理委托給 Spring。您可以通過在 struts-config 動作映射中注冊一個代理來實現。代理負責在 Spring 環境中查找 Struts 動作。由于動作在 Spring 的控制之下,所以它可以填充動作的 JavaBean 屬性,并為應用諸如 Spring 的 AOP 攔截器之類的特性帶來了可能。

          清單 5 中的 Action 類與清單 4 中的相同。但是 struts-config 有一些不同:


          清單 5. Spring 整合的委托方法
          <?xml version="1.0" encoding="ISO-8859-1" ?>
                                  <!DOCTYPE struts-config PUBLIC
                                  "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN"
                                  "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">
                                  <struts-config>
                                  <form-beans>
                                  <form-bean name="searchForm"
                                  type="org.apache.struts.validator.DynaValidatorForm">
                                  <form-property name="isbn"    type="java.lang.String"/>
                                  </form-bean>
                                  </form-beans>
                                  <global-forwards type="org.apache.struts.action.ActionForward">
                                  <forward   name="welcome"                path="/welcome.do"/>
                                  <forward   name="searchEntry"            path="/searchEntry.do"/>
                                  <forward   name="searchSubmit"           path="/searchSubmit.do"/>
                                  </global-forwards>
                                  <action-mappings>
                                  <action    path="/welcome" forward="/WEB-INF/pages/welcome.htm"/>
                                  <action    path="/searchEntry" forward="/WEB-INF/pages/search.jsp"/>
                                  <action    path="/searchSubmit"
                                  type="org.springframework.web.struts.DelegatingActionProxy" |(1)
                                  input="/searchEntry.do"
                                  validate="true"
                                  name="searchForm">
                                  <forward name="success" path="/WEB-INF/pages/detail.jsp"/>
                                  <forward name="failure" path="/WEB-INF/pages/search.jsp"/>
                                  </action>
                                  </action-mappings>
                                  <message-resources parameter="ApplicationResources"/>
                                  <plug-in className="org.apache.struts.validator.ValidatorPlugIn">
                                  <set-property
                                  property="pathnames"
                                  value="/WEB-INF/validator-rules.xml,/WEB-INF/validation.xml"/>
                                  </plug-in>
                                  <plug-in
                                  className="org.springframework.web.struts.ContextLoaderPlugIn">
                                  <set-property property="contextConfigLocation" value="/WEB-INF/beans.xml"/>
                                  </plug-in>
                                  </struts-config>
                                  

          清單 5 是一個典型的 struts-config.xml 文件,只有一個小小的差別。它注冊 Spring 代理類的名稱,而不是聲明動作的類名,如(1)處所示。DelegatingActionProxy 類使用動作映射名稱查找 Spring 環境中的動作。這就是我們使用 ContextLoaderPlugIn 聲明的環境。

          將一個 Struts 動作注冊為一個 Spring bean 是非常直觀的,如清單 6 所示。我利用動作映射使用 <bean> 標記的名稱屬性(在這個例子中是 "/searchSubmit")簡單地創建了一個 bean。這個動作的 JavaBean 屬性像任何 Spring bean 一樣被填充:


          清單 6. 在 Spring 環境中注冊一個 Struts 動作
          <?xml version="1.0" encoding="UTF-8"?>
                                  <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
                                  "http://www.springframework.org/dtd/spring-beans.dtd">
                                  <beans>
                                  <bean id="bookService" class="ca.nexcel.books.business.BookServiceImpl"/>
                                  <bean name="/searchSubmit"
                                  class="ca.nexcel.books.actions.SearchSubmit">
                                  <property name="bookService">
                                  <ref bean="bookService"/>
                                  </property>
                                  </bean>
                                  </beans>
                                  





          動作委托的優點

          動作委托解決方法是這三種方法中最好的。Struts 動作不了解 Spring,不對代碼作任何改變就可用于非 Spring 應用程序中。RequestProcessor 的改變不會影響它,并且它可以利用 Spring AOP 特性的優點。

          動作委托的優點不止如此。一旦讓 Spring 控制您的 Struts 動作,您就可以使用 Spring 給動作補充更強的活力。例如,沒有 Spring 的話,所有的 Struts 動作都必須是線程安全的。如果您設置 <bean> 標記的 singleton 屬性為“false”,那么不管用何種方法,您的應用程序都將在每一個請求上有一個新生成的動作對象。您可能不需要這種特性,但是把它放在您的工具箱中也很好。您也可以利用 Spring 的生命周期方法。例如,當實例化 Struts 動作時,<bean> 標記的 init-method 屬性被用于運行一個方法。類似地,在從容器中刪除 bean 之前,destroy-method 屬性執行一個方法。這些方法是管理昂貴對象的好辦法,它們以一種與 Servlet 生命周期相同的方式進行管理。





          攔截 Struts

          前面提到過,通過將 Struts 動作委托給 Spring 框架而整合 Struts 和 Spring 的一個主要的優點是:您可以將 Spring 的 AOP 攔截器應用于您的 Struts 動作。通過將 Spring 攔截器應用于 Struts 動作,您可以用最小的代價處理橫切關注點。

          雖然 Spring 提供很多內置攔截器,但是我將向您展示如何創建自己的攔截器并把它應用于一個 Struts 動作。為了使用攔截器,您需要做三件事:

          1. 創建攔截器。
          2. 注冊攔截器。
          3. 聲明在何處攔截代碼。

          這看起來非常簡單的幾句話卻非常強大。例如,在清單 7 中,我為 Struts 動作創建了一個日志記錄攔截器。 這個攔截器在每個方法調用之前打印一句話:


          清單 7. 一個簡單的日志記錄攔截器
          package ca.nexcel.books.interceptors;
                                  import org.springframework.aop.MethodBeforeAdvice;
                                  import java.lang.reflect.Method;
                                  public class LoggingInterceptor implements MethodBeforeAdvice {
                                  public void before(Method method, Object[] objects, Object o) throws Throwable {
                                  System.out.println("logging before!");
                                  }
                                  }
                                  

          這個攔截器非常簡單。before() 方法在攔截點中每個方法之前運行。在本例中,它打印出一句話,其實它可以做您想做的任何事。下一步就是在 Spring 配置文件中注冊這個攔截器,如清單 8 所示:


          清單 8. 在 Spring 配置文件中注冊攔截器
          <?xml version="1.0" encoding="UTF-8"?>
                                  <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
                                  "http://www.springframework.org/dtd/spring-beans.dtd">
                                  <beans>
                                  <bean id="bookService" class="ca.nexcel.books.business.BookServiceImpl"/>
                                  <bean name="/searchSubmit"
                                  class="ca.nexcel.books.actions.SearchSubmit">
                                  <property name="bookService">
                                  <ref bean="bookService"/>
                                  </property>
                                  </bean>
                                  <!--  Interceptors -->
                                  <bean name="logger"
                                  class="ca.nexcel.books.interceptors.LoggingInterceptor"/> |(1)
                                  <!-- AutoProxies -->
                                  <bean name="loggingAutoProxy"
                                  class="org.springframework.aop.framework.autoproxy.
                                  BeanNameAutoProxyCreator"> |(2)
                                  <property name="beanNames">
                                  <value>/searchSubmit</valuesgt; |(3)
                                  </property>
                                  <property name="interceptorNames">
                                  <list>
                                  <value>logger</value> |(4)
                                  </list>
                                  </property>
                                  </bean>
                                  </beans>
                                  

          您可能已經注意到了,清單 8 擴展了 清單 6 中所示的應用程序以包含一個攔截器。具體細節如下:

          • 在 (1) 處,我注冊了這個攔截器。
          • 在 (2) 處,我創建了一個 bean 名稱自動代理,它描述如何應用攔截器。還有其他的方法定義攔截點,但是這種方法常見而簡便。
          • 在 (3) 處,我將 Struts 動作注冊為將被攔截的 bean。如果您想要攔截其他的 Struts 動作,則只需要在 "beanNames" 下面創建附加的 <value> 標記。
          • 在 (4) 處,當攔截發生時,我執行了在 (1) 處創建的攔截器 bean 的名稱。這里列出的所有攔截器都應用于“beanNames”。

          就是這樣。就像這個例子所展示的,將您的 Struts 動作置于 Spring 框架的控制之下,為處理您的 Struts 應用程序提供了一系列全新的選擇。在本例中,使用動作委托可以輕松地利用 Spring 攔截器提高 Struts 應用程序中的日志記錄能力。





          結束語

          在本文中,您已經學習了將 Struts 動作整合到 Spring 框架中的三種竅門。使用 Spring 的 ActionSupport 來整合 Struts(第一種竅門中就是這樣做的)簡單而快捷,但是會將 Struts 動作與 Spring 框架耦合在一起。如果您需要將應用程序移植到一個不同的框架,則需要重寫代碼。第二種解決方法通過委托 RequestProcessor 巧妙地解開代碼的耦合,但是它的可擴展性不強,并且當 Struts 的 RequestProcessor 變成一系列命令時,這種方法就持續不了很長時間。第三種方法是這三種方法中最好的:將 Struts 動作委托給 Spring 框架可以使代碼解耦,從而使您可以在您的 Struts 應用程序中利用 Spring 的特性(比如日志記錄攔截器)。

           


          posted @ 2009-04-05 09:37 lanxin1020 閱讀(146) | 評論 (0)編輯 收藏
          僅列出標題
          共7頁: 上一頁 1 2 3 4 5 6 7 下一頁 
          主站蜘蛛池模板: 沁阳市| 夏邑县| 攀枝花市| 墨竹工卡县| 龙陵县| 禄劝| 华阴市| 鄂伦春自治旗| 名山县| 定边县| 汽车| 平凉市| 玉龙| 镇江市| 甘德县| 综艺| 巴青县| 长海县| 陕西省| 寿阳县| 榆林市| 呼伦贝尔市| 绿春县| 玛纳斯县| 井冈山市| 盐亭县| 同心县| 台南市| 曲沃县| 延吉市| 江口县| 茂名市| 绵阳市| 井研县| 清水县| 临潭县| 得荣县| 桃江县| 宣城市| 错那县| 新密市|