posts - 495,  comments - 11,  trackbacks - 0

          6.4 Spring整合Struts

          雖然Spring也提供了自己的MVC組件,但一來Spring的MVC組件過于繁瑣,二???? 來Struts的擁護者實在太多。因此,很多項目都會選擇使用Spring整合Struts框架。而且Spring確實可以無縫整合Struts框架,二者結(jié)合成一個更實際的J2EE開發(fā)平臺。

          6.4.1 利用Struts的PlugIn來啟動Spring容器

          使用Spring的Web應(yīng)用時,不用手動創(chuàng)建Spring容器,而是通過配置文件聲明式地創(chuàng)建Spring容器。因此,在Web應(yīng)用中創(chuàng)建Spring容器有如下兩個方式:

          ?? ● 直接在web.xml文件中配置創(chuàng)建Spring容器。

          ?? ● 利用第三方MVC框架的擴展點,創(chuàng)建Spring容器。

          其實第一種創(chuàng)建Spring容器的方式更加常見。為了讓Spring容器隨Web應(yīng)用的啟動而自動啟動,有如下兩個方法:

          ?? ● 利用ServletContextListener實現(xiàn)。

          ?? ● 采用load-on-startup Servlet實現(xiàn)。

          Spring提供ServletContextListener的一個實現(xiàn)類ContextLoaderListener,該類可以作為Listener使用,會在創(chuàng)建時自動查找WEB-INF/下的applicationContext.xml文件,因此,如果只有一個配置文件,并且文件名為applicationContext.xml,只需在web.xml文件中增加如下配置片段即可:

          <listener>

          ?? <listener-class>org.springframework.web.context.
          ??? ContextLoaderListener</listener-class>

          </listener>

          如果有多個配置文件需要載入,則考慮使用<context-param>元素來確定配置文件的文件名。ContextLoaderListener加載時,會查找名為contextConfigLocation的參數(shù)。因此,配置context-param時,參數(shù)名字應(yīng)該是contextConfigLocation。

          帶多個配置文件的web.xml文件如下:

          <?xml version="1.0" encoding="GBK"?>

          <!-- 指定Web配置文件的根元素,以及相應(yīng)的Schema信息 -->

          <web-app xmlns="http://java.sun.com/xml/ns/j2ee"

          ??? xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

          ??? xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee ??
          ??? http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"

          ??? version="2.4">

          ??? <!-- 確定多個配置文件 -->

          ??? <context-param>

          ??????? <!-- 參數(shù)名為contextConfigLocation -->

          ??????? <param-name>contextConfigLocation</param-name>

          ??????? <!-- 多個配置文件之間以“,”隔開 -->

          ??????? <param-value>/WEB-INF/daoContext.xml,/WEB-INF/
          ??????? applicationContext.xml</param-value>

          ??? </context-param>

          ??? <!-- 采用listener創(chuàng)建ApplicationContext實例 -->

          ??? <listener>

          ??????? <listener-class>org.springframework.web.context.
          ??????? ContextLoaderListener</listener-class>

          ??? </listener>

          </web-app>

          如果沒有通過contextConfigLocation指定配置文件,Spring會自動查找application- Context.xml配置文件;如果有contextConfigLocation,則利用該參數(shù)確定的配置文件。如果無法找到合適的配置文件,Spring將無法正常初始化。

          Spring根據(jù)bean定義創(chuàng)建WebApplicationContext對象,并將其保存在web應(yīng)用的ServletContext中。大部分情況下,應(yīng)用中的Bean無須感受到ApplicationContext的存在,只要利用ApplicationContext的IoC即可。

          如果需要在應(yīng)用中獲取ApplicationContext實例,可以通過如下代碼獲取:

          //獲取當(dāng)前Web應(yīng)用的Spring容器

          WebApplicationContext ctx =

          ??? WebApplicationContextUtils.getWebApplicationContext(servletContext);

          除此之外,Spring提供了一個特殊的Servlet類ContextLoaderServlet。該Servlet在啟動時,會自動查找WEB-INF/下的applicationContext.xml文件。

          當(dāng)然,為了讓ContextLoaderServlet隨應(yīng)用的啟動而啟動,應(yīng)將此Servlet配置成load-on-startup的Servlet,load-on-startup的值小一點比較合適,這樣可以保證Application- Context更快的初始化。

          如果只有一個配置文件,并且文件名為applicationContext.xml,在web.xml文件中增加如下一段即可:

          <servlet>

          ??? <servlet-name>context</servlet-name>

          ??? <servlet-class>org.springframework.web.context.ContextLoaderServlet
          ??? </servlet-class>

          ??? <load-on-startup>1</load-on-startup>

          </servlet>

          該Servlet用于提供“后臺”服務(wù),主要用于創(chuàng)建Spring容器,無須響應(yīng)客戶請求,因此無須配置servlet-mapping。

          如果有多個配置文件,一樣使用<context-param>元素來確定多個配置文件。

          事實上,不管是ContextLoaderServlet,還是ContextLoaderListener,都依賴于ContextLoader創(chuàng)建ApplicationContext實例。

          在ContextLoader代碼的第240行,有如下代碼:

          String configLocation = servletContext.getInitParameter
          (CONFIG_LOCATION_PARAM);

          if (configLocation != null) {

          ??? wac.setConfigLocations(StringUtils.tokenizeToStringArray
          ??? (configLocation,

          ??? ConfigurableWebApplicationContext.CONFIG_LOCATION_DELIMITERS));

          }

          其中,CONFIG_LOCATION_PARAM是該類的常量,其值為contextConfigLocation。可以看出,ContextLoader首先檢查servletContext中是否有contextConfigLocation的參數(shù),如果有該參數(shù),則加載該參數(shù)指定的配置文件。

          ContextLoaderServlet與ContextLoaderListener底層都依賴于ContextLoader。因此,二者的效果幾乎沒有區(qū)別。之間的區(qū)別不是它們本身引起的,而是由于Servlet規(guī)范,Listener比Servlet優(yōu)先加載。因此,采用ContextLoaderListener創(chuàng)建ApplicationContext的時機更早。

          當(dāng)然,也可以通過ServletContext的getAttribute方法獲取ApplicationContext。但使用WebApplicationContextUtils類更便捷,因為無須記住ApplicationContext的屬性名。即使ServletContext的WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRI- BUTE屬性沒有對應(yīng)對象,WebApplicationContextUtils的getWebApplicationContext()方法將會返回空,而不會引起異常。

          到底需要使用Listener,還是使用load-on-startup Servlet來創(chuàng)建Spring容器呢?通常推薦使用Listener來創(chuàng)建Spring容器。但Listerner是Servlet 2.3以上才支持的標(biāo)準(zhǔn),因此,必須Web容器支持Listener才可使用Listerner。

          注意:使用Listener創(chuàng)建Spring容器之前,應(yīng)先評估Web容器是否支持Listener標(biāo)準(zhǔn)。

          還有一種情況,利用第三方MVC框架的擴展點來創(chuàng)建Spring容器,比如Struts。在第2章介紹Strust框架時,知道Struts有一個擴展點PlugIn。

          實際上,Spring正是利用了PlugIn這個擴展點,從而提供與Struts的整合。Spring提供了PlugIn接口的實現(xiàn)類org.springframework.web.struts.ContextLoaderPlugIn。這個實現(xiàn)類可作為Struts的PlugIn配置,Struts框架啟動時,將自動創(chuàng)建Spring容器。

          為了利用Struts的PlugIn創(chuàng)建Spring容器,只需在Struts配置文件中增加如下片段?? 即可:

          <plug-in className="org.springframework.web.struts.ContextLoaderPlugIn">

          <set-property property="contextConfigLocation"

          ????? value="/WEB-INF/action-servlet.xml,/WEB-INF/applicationContext.
          ????? xml"/>

          </plug-in>

          其中,指定contextConfigLocation屬性值時,即可以指定一個Spring配置文件的位置,可以指定多個Spring配置文件的位置。

          6.4.2 MVC框架與Spring整合的思考

          對于一個基于B/S架構(gòu)的J2EE應(yīng)用而言,用戶請求總是向MVC框架的控制器請求,而當(dāng)控制器攔截到用戶請求后,必須調(diào)用業(yè)務(wù)邏輯組件來處理用戶請求。此時有一個問題,控制器應(yīng)該如何獲得業(yè)務(wù)邏輯組件?

          最容易想到的策略是,直接通過new關(guān)鍵字創(chuàng)建業(yè)務(wù)邏輯組件,然后調(diào)用業(yè)務(wù)邏輯組件的方法,根據(jù)業(yè)務(wù)邏輯方法的返回值確定結(jié)果。

          實際的應(yīng)用中,很少見到采用上面的訪問策略,因為這是一種非常差的策略。不這樣做至少有如下3個原因:

          ?? ● 控制器直接創(chuàng)建業(yè)務(wù)邏輯組件,導(dǎo)致控制器和業(yè)務(wù)邏輯組件的耦合降低到代碼層次,不利于高層次解耦。

          ?? ● 控制器不應(yīng)該負責(zé)業(yè)務(wù)邏輯組件的創(chuàng)建,控制器只是業(yè)務(wù)邏輯組件的使用者。無須關(guān)心業(yè)務(wù)邏輯組件的實現(xiàn)。

          ?? ● 每次創(chuàng)建新的業(yè)務(wù)邏輯組件將導(dǎo)致性能下降。

          答案是采用工廠模式或服務(wù)定位器。采用服務(wù)定位器的模式,是遠程訪問的場景。在這種場景下,業(yè)務(wù)邏輯組件已經(jīng)在某個容器中運行,并對外提供某種服務(wù)。控制器無須理會該業(yè)務(wù)邏輯組件的創(chuàng)建,直接調(diào)用即可,但在調(diào)用之前,必須先找到該服務(wù)——這就是服務(wù)定位器的概念。經(jīng)典J2EE應(yīng)用就是這種結(jié)構(gòu)的應(yīng)用。

          對于輕量級的J2EE應(yīng)用,工廠模式則是更實際的策略。因為輕量級的J2EE應(yīng)用里,業(yè)務(wù)邏輯組件不是EJB,通常就是一個POJO,業(yè)務(wù)邏輯組件的生成通常由工廠負責(zé),而且工廠可以保證該組件的實例只需一個就夠了,可以避免重復(fù)實例化造成的系統(tǒng)開銷。

          如圖6.2就是采用工廠模式的順序圖。

          圖6.2 工廠模式順序圖

          采用工廠模式,將控制器與業(yè)務(wù)邏輯組件的實現(xiàn)分離,從而提供更好的解耦。

          在采用工廠模式的訪問策略中,所有的業(yè)務(wù)邏輯組件的創(chuàng)建由工廠負責(zé),業(yè)務(wù)邏輯組件的運行也由工廠負責(zé)。而控制器只需定位工廠實例即可。

          如果系統(tǒng)采用Spring框架,則Spring成為最大的工廠。Spring負責(zé)業(yè)務(wù)邏輯組件的創(chuàng)建和生成,并可管理業(yè)務(wù)邏輯組件的生命周期。可以如此理解,Spring是一個性能非常優(yōu)秀的工廠,可以生產(chǎn)出所有的實例,從業(yè)務(wù)邏輯組件,到持久層組件,甚至控制器。

          現(xiàn)在的問題是,控制器如何訪問到Spring容器中的業(yè)務(wù)邏輯組件?為了讓Action訪 問Spring的業(yè)務(wù)邏輯組件,有兩種策略:

          ?? ● Spring管理控制器,并利用依賴注入為控制器注入業(yè)務(wù)邏輯組件。

          ?? ● 控制器顯式定位Spring工廠,也就是Spring的容器ApplicationContext實例,并從工廠中獲取業(yè)務(wù)邏輯組件實例的引用。

          第一種策略,充分利用Spring的IoC特性,是最優(yōu)秀的解耦策略。但不可避免帶來一些不足之處,歸納起來主要有如下不足之處:

          ?? ● Spring管理Action,必須將所有的Action配置在Spring容器中,而struts-config.xml文件中的配置也不會減少,導(dǎo)致配置文件大量增加。

          ?? ● Action的業(yè)務(wù)邏輯組件接收容器注入,將導(dǎo)致代碼的可讀性降低。

          總體而言,這種整合策略是利大于弊。

          第二種策略,與前面介紹的工廠模式并沒有太大的不同。區(qū)別是Spring容器充當(dāng)了業(yè)務(wù)邏輯組件的工廠。控制器負責(zé)定位Spring容器,通常Spring容器訪問容器中的業(yè)務(wù)邏輯組件。這種策略是一種折衷,降低了解耦,但提高了程序的可讀性。

          Spring完全支持這兩種策略,既可以讓Spring容器管理控制器,也可以讓控制器顯式定位Spring容器中的業(yè)務(wù)邏輯組件。

          6.4.3 使用DelegatingRequestProcessor

          這里介紹的是第一種整合策略:讓Spring管理Struts的Action。那么同樣有一個問題,讓Spring管理Struts的Action時,客戶端的HTTP 請求如何轉(zhuǎn)向Spring容器中的Action?

          當(dāng)使用Struts作為MVC框架時,客戶端的HTTP請求都是直接向ActionServlet請求的,因此關(guān)鍵就是讓ActionServlet將請求轉(zhuǎn)發(fā)給Spring容器中的Action。這很明顯可以利用Spring的另一個擴展點:通過擴展RequestProcessor完成,使用擴展的RequestProcessor替換Struts的RequestProcessor。

          Spring完成了這種擴展,Spring提供的DelegatingRequestProcessor繼承Request- Processor。為了讓Struts使用DelegatingRequestProcessor,還需要在struts-config.xml文件中增加如下一行:

          //使用spring的RequestProcessor替換struts原有的RequestProcessor

          <controller processorClass="org.springframework.web.struts.
          DelegatingRequestProcessor"/>

          完成這個設(shè)置后,Struts會將截獲到的用戶請求轉(zhuǎn)發(fā)到Spring context下的bean,根據(jù)bean的name屬性來匹配。而Struts中的action配置則無須配置class屬性,即使配置了class屬性也沒有任何用處,即下面兩行配置是完全一樣的:

          //配置struts action時,指定了實現(xiàn)類

          <action path="/user" type="lee.UserAction"/>

          //配置struts action時,沒有指定實現(xiàn)類

          <action path="/user"/>

          下面的示例程序在上一個示例程序的基礎(chǔ)上稍作修改,增加了客戶端驗證和程序國際化部分。也調(diào)用了Spring的業(yè)務(wù)bean來驗證登錄。先看修改后的struts-config.xml文件:

          <!-- XML文件版本,編碼集 -->

          <?xml version="1.0" encoding="gb2312"?>

          <!-- Struts配置文件的文件頭,包括DTD等信息 -->

          <!DOCTYPE struts-config PUBLIC

          ????????? "-//Apache Software Foundation//DTD Struts Configuration 1.2//EN"

          ????????? "http://struts.apache.org/dtds/struts-config_1_2.dtd">

          <!-- struts配置文件的根元素 -->

          <struts-config>

          ??? <!-- 配置formbean,所有的formbean都放在form-beans元素里定義 -->

          ??? <form-beans>

          ??????? <!-- 定義了一個formbean,確定formbean名和實現(xiàn)類 -->

          ??????? <form-bean name="loginForm" type="lee.LoginForm"/>

          ??? </form-beans>

          ??? <!-- 定義action部分,所有的action都放在action-mapping元素里定義 -->

          ??? <action-mappings>

          ??????? <!-- 這里只定義了一個action。而且沒有指定該action的type元素 -->

          ??????? <action path="/login" name="loginForm"

          ??????????? scope="request" validate="true" input="/login.jsp" >

          ??????????? <!-- 定義action內(nèi)的兩個局部forward元素 -->

          ??????????? <forward name="input" path="/login.jsp"/>

          ??????????? <forward name="welcome" path="/welcome.html"/>

          ??????? </action>

          ??? </action-mappings>

          ??? <!-- 使用DelegatingRequestProcessor替換RequestProcessor -->

          ??? <controller processorClass="org.springframework.web.struts.
          ??? DelegatingRequestProcessor"/>

          ??? <!-- 加載國際化的資源包 -->

          ??? <message-resources parameter="mess"/>

          ??? <!-- 裝載驗證的資源文件 -->

          ??? <plug-in className="org.apache.struts.validator.ValidatorPlugIn">

          ??????? <set-property property="pathnames" value="/WEB-INF/validator-
          ??????? rules.xml,/WEB-INF/validation.xml" />

          ??????? <set-property property="stopOnFirstError" value="true"/>

          ??? </plug-in>

          ??? <!-- 裝載Spring配置文件,隨應(yīng)用的啟動創(chuàng)建ApplicationContext實例 -->

          ??? <plug-in className="org.springframework.web.struts.
          ??? ContextLoaderPlugIn">

          ??????? <set-property property="contextConfigLocation"

          ??????????? value="/WEB-INF/applicationContext.xml,

          ?????????????????? /WEB-INF/action-servlet.xml"/>

          ??? </plug-in>

          </struts-config>

          修改后的struts-config.xml文件,增加加載國際化資源文件。配置Struts的action不需要class屬性,完成了ApplicationContext的創(chuàng)建。

          然后考慮web.xml文件的配置,在web.xml文件中必須配置Struts框架的加載。除此之外,因為使用了Spring管理Struts的Action,而Action是隨HTTP請求啟動的,因此,應(yīng)將Action的作用域配置成Request,為了使用Request作用域,必須在web.xml文件中增加適當(dāng)?shù)呐渲谩?/p>

          下面是web.xml文件的代碼:

          <?xml version="1.0" encoding="GBK"?>

          <!-- 指定Web配置文件的根元素,以及對應(yīng)的Schema信息 -->

          <web-app xmlns="http://java.sun.com/xml/ns/j2ee"

          ??? xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

          ??? xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
          ??? http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"

          ??? version="2.4">

          ??? <!-- 定義一個Filter,該Filter是使用Request作用域的基礎(chǔ) -->

          ??? <filter>

          ??????? <filter-name>requestContextFilter</filter-name>

          ??????? <filter-class>org.springframework.web.filter.
          ??????? RequestContextFilter </filter-class>

          ??? </filter>

          ??? <!-- 定義filter-mapping,讓上面的Filter過濾所有的用戶請求 -->

          ??? <filter-mapping>

          ??????? <filter-name>requestContextFilter</filter-name>

          ??????? <url-pattern>/*</url-pattern>

          ??? </filter-mapping>

          ??? <!-- 定義Struts的核心Servlet -->

          ??? <servlet>

          ??????? <servlet-name>action</servlet-name>

          ??????? <servlet-class>org.apache.struts.action.ActionServlet
          ??????? </servlet-class>

          ??????? <load-on-startup>2</load-on-startup>

          ??? </servlet>

          ??? <!-- 定義Struts的核心Servlet攔截所有*.do請求 -->

          ??? <servlet-mapping>

          ??????? <servlet-name>action</servlet-name>

          ??????? <url-pattern>*.do</url-pattern>

          ??? </servlet-mapping>

          ??? <!-- 關(guān)于Struts標(biāo)簽庫的配置 -->

          ??? <jsp-config>

          ??????? <!-- 配置bean標(biāo)簽 -->

          ??????? <taglib>

          ??????????? <taglib-uri>/tags/struts-bean</taglib-uri>

          ??????????? <taglib-location>/WEB-INF/struts-bean.tld</taglib-location>

          ??????? </taglib>

          ??????? <!-- 配置html標(biāo)簽 -->

          ??????? <taglib>

          ??????????? <taglib-uri>/tags/struts-html</taglib-uri>

          ??????????? <taglib-location>/WEB-INF/struts-html.tld</taglib-location>

          ??????? </taglib>

          ??????? <!-- 配置logic標(biāo)簽 -->

          ??????? <taglib>

          ??????????? <taglib-uri>/tags/struts-logic</taglib-uri>

          ??????????? <taglib-location>/WEB-INF/struts-logic.tld</taglib-location>

          ??????? </taglib>

          ??? </jsp-config>

          </web-app>

          posted on 2009-07-19 10:22 jadmin 閱讀(68) 評論(0)  編輯  收藏

          只有注冊用戶登錄后才能發(fā)表評論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 华宁县| 汤原县| 乌兰浩特市| 白山市| 郯城县| 探索| 高唐县| 巴青县| 洛浦县| 辉县市| 桂阳县| 阳朔县| 慈利县| 晴隆县| 深州市| 清水河县| 波密县| 沾益县| 治县。| 团风县| 泌阳县| 德化县| 东台市| 阿荣旗| 西乡县| 安宁市| 绩溪县| 敖汉旗| 措美县| 颍上县| 鲜城| 宜昌市| 两当县| 枣阳市| 噶尔县| 酒泉市| 二连浩特市| 任丘市| 岳西县| 德阳市| 新宁县|