javascript
用Spring更好地处理Struts动作三种整合
http://java.chinaitlab.com/Spring/525537_2.html
為什么 Spring 這么了不起?
???? Spring 的創立者 Rod Johnson 以一種批判的眼光看待 Java? 企業軟件開發,并且提議很多企業難題都能夠通過戰略地使用 IOC 模式(也稱作依賴注入)來解決。當 Rod 和一個具有奉獻精神的開放源碼開發者團隊將這個理論應用于實踐時,結果就產生了 Spring 框架。簡言之,Spring 是一個輕型的容器,利用它可以使用一個外部 XML 配置文件方便地將對象連接在一起。每個對象都可以通過顯示一個 JavaBean 屬性收到一個到依賴對象的引用,留給您的簡單任務就只是在一個 XML 配置文件中把它們連接好。
|
???? 依賴注入是一個強大的特性,但是 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 動作。為了使用攔截器,您需要做三件事:
??? 這看起來非常簡單的幾句話卻非常強大。例如,在清單 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 的特性(比如日志記錄攔截器)。
三種 Struts-Spring 整合竅門中的每一種都被實現成一個完整可用的應用程序
?
http://blog.csdn.net/huqingwei0824/article/details/7444662
采用DelegatingRequestProcessor將處理轉發給Spring容器中的bean (比如:struts action)????DelegatingRequestProcessor繼承自RequestProcessor。為了讓Struts使用DelegatingRequestProcessor,還需要在struts-config.xml文件中增加如下代碼:?
???????? <controller processorClass="org.springframework.web.struts.DelegatingRequestProcessor" />?
?? 這一行代碼是告訴Struts用DelegatingRequestProcessor來代替原來的RequestProcessor。完成設置這個設置后,Struts會將攔截到的用戶請求轉發到Spring context下的bean,根據bean的name屬性來匹配。而Struts中的action配置則無需配置type屬性(即使配置了type屬性也不起任何作用,除非在spring的配置文件里找不到對應的name屬性值,除非這個條件直到今天才知道。記下來備忘:))。?
?? 配置了上面的一行代碼后,就可以在Spring的配置文件(可以不是applicationContext.xml,比如假設這里是action-servlet.xml)中配置用來處理請求的Action bean了。配置的時候需要注意的是Action bean不再需要id屬性,而要用name替代id屬性,這時name屬性的值應與struts-config.xml中配置的Action的path屬性的值相同。?
?? 這樣,處理請求的Action就能處于Spring的管理之下了。?
?
總結
以上是生活随笔為你收集整理的用Spring更好地处理Struts动作三种整合的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Hibernate的拦截器和监听器 .
- 下一篇: Struts1.2配置详解