1、前言
STRUTS是標準的"模型2"的WEB應用框架,其中的ActionServlet代表了"模型2"MVC設計模式中的"控制器"
。STRUTS應用程序一般使用JSP代碼生成用戶界面,這些代碼不包括任何商業邏輯,代表了MVC中的”VIEW”部分。需要執行商業邏輯的用戶界面中
的表單或超鏈將會由"控制器"
ActionServlet接收和處理。在STRUTS中,只有一個ActionServlet實例,這個實例將接收和處理應用中的相關用戶交互請求。
ActionServlet實例將選擇和調用相應的ACTION類來處理商業邏輯。在設計模式上,ACTION類被稱為“控制輔助者”,它將修改
JavaBeans,這些JavaBeans就是MVC中的” model”部分。本文將分析在STRUTS中進行模塊化編程的具體細節。
2、樣例描述
我們將作一個模塊編程的例子,這個例子包括三個模塊,缺省模塊、registration模塊和approval模塊。缺省模塊下有資源
index.html,我們使用它來連接其它兩個模塊。registration模塊和approval模塊的編程類似,都包含一個index.jsp和
一個結果jsp:result.jsp。下面是目錄結構:

目錄結構表明,struts的模塊由配置文件、java類(這里者action和form類)和資源文件構成,另外各模塊可以共享web.xml,message (這里是applicatonResources.properties)文件。
我們的例子的界面交互圖可以表示如下:

缺省模塊的index.html包括兩個鏈接,分別連接兩個模塊的index.jsp資源,registration模塊的index.jsp提
交后,如果驗證失敗會重新返回到該模塊的index.jsp,否則用資源resultok.jsp顯示注冊成功的結果。模塊approval的
index.jsp提交后進入到resultok.jsp的界面,顯示批準與否的結果。
我們從應用程序的文件組成和交互兩方面的模塊情況對我們要實現的例子進行了比較清晰的組織,下面我們講解如何在struts中實現模塊化編程。
3、STRUTS的模塊化機制
我們將講解STRUTS的相關配置,這些配置大部分與模塊化編程有關系,有些沒關系但對理解STRUTS程序有利。
3.1 ActionServlet參數
ActionServlet有好多參數可供設置,STRUTS在WEB應用部署描述符中定義這些參數:
·Config——逗號相隔的應用上下文相對的配置文件的路徑,這些配置文件包含了STRUTS WEB應用的缺省模塊的設置。缺省值為 /WEB-INF/struts-config.xml;
·config/${module} -逗號相隔的應用上下文相對的配置文件的路徑,這些配置文件包含了STRUTS
WEB應用的${module}模塊的設置。這個模塊的前綴是/${module},多個config/${module}參數定義了多個STRUTS
WEB應用模塊;
·convertNull - 如果這個參數的值為 true, 數值型Java 包裝類(比如java.lang.Integer)的初始值將會是null,而不是0。缺省值[false]
·rulesets-逗號相隔的附加的org.apache.commons.digester.RuleSet列表,Digester在分析配置文件時,除了針對標準的配置元素的RuleSet之外,還會利用這些對象來分析配置文件,這樣提供了一個配置擴展機制。
·validatin - 指示我們是否使用驗證型的XML分析器來處理配置文件,缺省值為 [true]
3.2 配置文件
我們說STRUTS針對每個模塊可以有一個或多個配置文件,這些配置文件使用XML來書寫,下面是標準的配置文件XML的元素解釋。
3.2.1 元素 action
這個元素描述了一個ActionMapping 對象,這個對象將用來處理用戶針對某個模塊相對應的URI 的請求。

3.2.2元素 action-mappings
這個元素描述了一個ActionMapping
對象集,類型是org.apache.struts.action.ActionMapping。與STRUTS的ActionServlet
注冊的URL模式匹配的用戶請求將具體地被這些ActionMapping 對象處理。子元素定義了各個ActionMapping對象。

3.2.3元素 controller
這個元素描述了一個struts模塊運行環境的配置——ControllerConfig bean



3.2.4 元素 data-source
這個元素描述了一個DataSource 對象——JDBC 2.0 標準擴展。這個對象將被保存在應用上下文中,而且可以象JavaBean 一樣被設置。

3.2.5 元素 exception
這個元素向struts系統為一個exception類型注冊了一個ExceptionHandler。.

3.2.6 元素 form-bean
這個元素定義了一個ActionForm[org.apache.struts.action.ActionForm子類,這個定義被"action"元素所引用。

3.2.7 元素 form-property
這個元素描述了一個配置DynaActionForm 或其子類的JavaBean屬性。當這個元素的父元素"form-bean"
的"type" 是 [org.apache.struts.action.DynaActionForm]
或其子類時有效。如果使用了一個定制的DynaActionForm 子類,父元素"form-bean" 的"dynamic"屬性必須設為
"true"。

3.2.8 元素 forward
這個元素描述了一個ActionForward
對象,這個對象將被Action的doPerform返回。在代碼中一般用一個邏輯名字來引用ActionForward
對象。一個"forward" 可以用來描述全局或局部ActionForwards. 全局的 forwards對模塊內所有的Action
對象有效,局部forwards 嵌套在 元素內,只能被相應的ActionMapping 中的Action訪問。

3.2.9元素 message-resources

3.2.10元素 plug-in

3.2.11 元素 set-property
?
目錄:
4、模塊定義
5、模塊選擇
6、總結
4、模塊定義
通過上面對STRUTS的模塊化機制的講解,我們現在可以開始實現我們的模塊化例子程序了。
4.1 Actionservlet參數
我們在struts的web.xml中定義模塊。下面的代碼定義了三個模塊:缺省模塊,approval和registration模塊,前綴分別是””,/approval和/registration。
<web-app> <servlet> <servlet-name>action</servlet-name> <servlet-class>org.apache.struts.action.ActionServlet</servlet-class> <init-param> <param-name>config</param-name> <param-value>/WEB-INF/struts-config.xml</param-value> </init-param> <init-param> <param-name>config/approval</param-name> <param-value>/WEB-INF/struts-config-approval.xml</param-value> </init-param> <init-param> <param-name>config/registration</param-name> <param-value>/WEB-INF/struts-config-registration.xml</param-value> </init-param> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>action</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping> </web-app>
|
這樣在初始化actionservlet的過程中,servletcontext的屬性中就會有這樣的屬性鍵/值關系:

4.2 approval模塊配置文件
下面是approval模塊的配置文件,定義了form和action,以及相應的forward。
<?xml version="1.0" encoding="UTF-8"?> <!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="approvalForm" type="com.i505.struts.approval.form.ApprovalForm"> </form-bean> </form-beans> <action-mappings> <action attribute="approvalForm" name="approvalForm" input="/index.jsp" path="/approval" scope="request" type="com.i505.struts.approval.action.ApprovalAction"> <forward name="success" contextRelative="false" path="/resultok.jsp" /> </action> </action-mappings> </struts-config> |
4.3 registration模塊配置文件
下面是registration模塊的配置文件,定義了form和action,以及相應的message-resources和forward。
<?xml version="1.0" encoding="UTF-8"?> <!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="registrationForm" type="com.i505.struts.registration.form.RegistrationForm" /> </form-beans> <action-mappings> <action attribute="registrationForm" input="/index.jsp" name="registrationForm" path="/registration" type="com.i505.struts.registration.action.RegistrationAction"> <forward name="success" path="/resultok.jsp" /> </action> </action-mappings> <message-resources parameter="com.i505.struts.ApplicationResources"/> </struts-config> |
5、模塊選擇本節主要講述struts中如何選擇模塊,實現模塊的真正運作的。
5.1 action的模塊選擇
當我們在瀏覽器中使用http://hostaddress/contextpath/module/action.do式樣的的url時,actionservlet會根據module選擇模塊對象,下面是actionservlet處理http請求的代碼:
protected void process(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { RequestUtils.selectModule(request, getServletContext()); ?????? getRequestProcessor(getModuleConfig(request)).process (request, response); } |
RequestUtils.selectModule函數將使用下面的代碼把url中的模塊前綴(下面代碼的prefix將代表上面url式樣中的/module)指定的模塊對象保存在request屬性中,這個模塊對象就成了處理這個請求的當前模塊對象:
// Expose the resources for this module ModuleConfig config = (ModuleConfig) context.getAttribute(Globals.MODULE_KEY + prefix); if (config != null) { request.setAttribute(Globals.MODULE_KEY, config); } else { request.removeAttribute(Globals.MODULE_KEY); } |
5.2 資源的模塊化
資源(比如jsp)的模塊化是指資源可以按照模塊一樣來組織,比如approval模塊的資源可以放在approval目錄下,而registration模塊的資源則放在registration目錄下,缺省模塊的資源放在webroot下。
url訪問這些資源很簡單,url式樣是
http://hostaddress/contextpath/module/xxx.jsp。對于input和forward訪問這些資源,我們只需
直接寫相對于模塊路徑下的路徑,注意它們必須以”/”開頭。如果forward是相對servletcontext的,則要加上模塊路徑。
<action-mappings> <action attribute="registrationForm" input="/index.jsp" name="registrationForm" path="/registration" type="com.i505.struts.registration.action.RegistrationAction"> <forward name="success" path="/resultok.jsp" /> </action> </action-mappings> |
5.3 Formtag中表單action url的生成
對于模塊編程,struts在formtag的action屬性好像有些問題,這些問題出現在struts沒有考慮直接訪問jsp時的情況。應為
forward和直接訪問這兩種環境是不同的,主要是直接訪問這些JSP,request屬性中沒有模塊對象,而forward訪問這些jsp時
request屬性中有模塊對象。我們需要修改代碼,使得在產生action屬性時不受jsp所在環境的影響,也就是我們將在formtag的
action屬性中指定模塊,而不是request中得到模塊。下面是registration模塊的index.jsp的代碼,它的formtag的
action屬性包括了模塊的前綴/registration:
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean"%> <%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html"%> <head> <title>申請注冊</title> <%@ page contentType="text/html;charset=GB2312" %> </head> <body> <html:form action="/registration/registration.do" > ??????姓名:<html:text property="name" /><html:errors property="name"/><br /><br /> ??????年齡:<html:text property="age" /><html:errors property="age"/><br /><br /> ??????<html:submit /> </html:form> </body> </html> |
下面我們來修改struts的相關代碼達到這個效果。
5.3.1 Formtag
Formtag的setAction將識別form tag的acton屬性的module前綴,并分離出真正的模塊相對的action路徑,lookup將直接從ServletContext中獲取模塊配置對象。
private String getActionPath(String action) { ????????????String temp = action.trim(); ????????????String x;?????? ?????? int pos=0; ????????????if(!temp.startsWith("/")) temp = "/"+ temp; ????????????pos = temp.indexOf("/", 1); ????????????if(pos<=0) return action; ???????????????????????? return temp.substring(pos);??????} private String getModulePrefix(String action) { ????????????String result; ????????????int pos; ????????????String temp=action.trim(); ????????????if(!temp.startsWith("/")) { ??????????????????temp= "/"+temp; ????????????} ????????????pos = temp.indexOf("/", 1); ????????????if(pos<=1) return ""; ????????????else ???????????? return temp.substring(0, pos); ??????????????????} public void setAction(String action) {this.modulePrefix = this.getModulePrefix(action); ????????????this.action = this.getActionPath(action); } protected void lookup() throws JspException { ????????????//我們直接從ServletContext中獲取模塊配置對象 ??????????????????moduleConfig = (ModuleConfig) pageContext.getServletContext().getAttribute(Globals.MODULE_KEY + modulePrefix); ??????…} rotected String renderFormStartElement() { HttpServletResponse response = (HttpServletResponse) this.pageContext.getResponse(); StringBuffer results = new StringBuffer("<form"); results.append(" name=\""); results.append(beanName); results.append("\""); results.append(" method=\""); results.append(method == null ? "post" : method); results.append("\" action=\""); //我們的action已經去掉了modulePrefix,所以我們得重新加上 results.append( response.encodeURL( RequestUtils.getActionMappingURL(this.modulePrefix+ this.action, this.pageContext))); … } |
5.3.2 Requestutils
Requestutils的getActionMappingURL主要用作附加servletcontext 路徑,因為我們現在在action參數附加了modulePrefix路徑,所以沒必要再追加模塊前綴。
public static String getActionMappingURL(String action, PageContext pageContext) { HttpServletRequest request = (HttpServletRequest) pageContext.getRequest(); StringBuffer value = new StringBuffer(request.getContextPath()); ModuleConfig config = (ModuleConfig) pageContext.getRequest().getAttribute(Globals.MODULE_KEY); //我們jsp中的formtag的action屬性已經表示了模塊,所以我們不能再追加模塊名// if (config != null) { // value.append(config.getPrefix()); // } // Use our servlet mapping, if one is specified String servletMapping = (String) pageContext.getAttribute(Globals.SERVLET_KEY, PageContext.APPLICATION_SCOPE); if (servletMapping != null) { String queryString = null; int question = action.indexOf("?"); if (question >= 0) { queryString = action.substring(question); } String actionMapping = getActionMappingName(action); if (servletMapping.startsWith("*.")) { value.append(actionMapping); value.append(servletMapping.substring(1)); } else if (servletMapping.endsWith("/*")) { value.append(servletMapping.substring(0, servletMapping.length() - 2)); value.append(actionMapping); } else if (servletMapping.equals("/")) { value.append(actionMapping); } if (queryString != null) { value.append(queryString); } } else { if (!action.startsWith("/")) { value.append("/"); } value.append(action); } // Return the completed value return (value.toString()); } |
6、總結模塊化編程有利于提高編程效率,但是struts中的模塊化支持有些小問題,本文詳細分析了struts支持模塊化編程的機制,并作了些修改,希
望對大家有幫助。另外如果我們可以把其改進為模塊化的相關的東西可以打成一個包進行動態部署(比如approval.mar)的話,那將會更加有用。