11.1 Struts介紹
11.1.1 Struts簡介
Struts是一個基于Sun J2EE平臺的MVC框架,很好地實現(xiàn)了MVC模式,它由Craig McClanahan創(chuàng)建。Struts最早是作為Apache Jakarta項目的組成部分問世運作,Struts這個名字來源于在建筑中使用的金屬架。使用它的目的是為了減少運用MVC設計模型來開發(fā)Web應用的時間。它只有一個中心控制器,采用XML定制轉(zhuǎn)向的URL,采用Action來處理邏輯。
Struts通過一個配置文件,即可把握整個系統(tǒng)各部分之間的聯(lián)系,但這樣做不容易查找錯誤。Struts 其實就是在Model2基礎上實現(xiàn)的一個MVC框架。Model2的示意圖如圖11.1所示。
圖11.1 Model2的示意圖
與Spring一樣,通過在web.xml中的配置,使得所有的視圖層請求都要通過ActionServlet,由它進行客戶端的請求處理。它主要通過struts-config.xml文件來進行用戶請求的動作和對應Action的請求,將請求傳遞給Action,并將處理后的結(jié)果返回給視圖層。Struts的體系結(jié)構(gòu)如圖11.2所示。
圖11.2 Struts的體系結(jié)構(gòu)
11.1.2 Struts和Spring比較
可以看出:Struts的ActionServlet和Spring的DispatcherServlet類似,Struts的Action和Spring的Controller類似,Struts的配置文件struts-config.xml和Spring的dispatcherServlet- config.xml類似,Struts的DispatchAction和Spring的MultiActionController類似。
11.1.3 下載Struts
前面簡要講解了Struts的相關(guān)知識,當然最重要的還是練習,這里從Struts的下載開始講起。通過http://struts.apache.org/可以進入Struts的首頁,如圖11.3所示。

圖11.3 Struts的首頁
本書采用的版本是Struts 1.3.8單擊首頁的“Struts 1.3.8”鏈接,即可進入Struts1.3.8的下載頁面,如圖11.4所示。

圖11.4 Struts1.3.8的下載頁面
該頁面可下載Struts的相關(guān)版本。單擊“struts-1.3.8-all.zip”超鏈接,即可進行下載,大小約44MB。
11.1.4 配置Struts
解壓縮struts-1.3.8-all.zip,解壓縮完畢后,就可以在Eclipse中配置Struts了。具體實現(xiàn)思路如下:首先在Eclipse中建立一個項目myLogin,然后把Struts相關(guān)的jar配置到該項目中,最后在項目中建立3個包:com. myLogin.action用來存放控制器類。com. myLogin.bean用來存放實體類、com. myLogin.impl用來存放接口類。具體步驟如下:
運行Eclipse,單擊菜單“File”命令,Eclipse將顯示“File”命令。
選擇“New”|“Project”,單擊右鍵彈出“New Project”對話框,如圖11.5所示。

圖11.5 “New Project”對話框
選擇樹形“Java”|“Tomcat Project”節(jié)點,然后單擊“Next”按鈕,彈出“New Tomcat Project”對話框,如圖11.6所示。

圖11.6 “New Tomcat Project”對話框
在“Project name”文本框中輸入“myLogin”,然后單擊“Finish”按鈕,項目即建立成功,myLogin的目錄結(jié)構(gòu)如圖11.7所示。
在myLogin上單擊右鍵,選擇菜單“New”|“Package”命令,彈出“New Java Package”對話框,如圖11.8所示。

圖11.7 myLogin的目錄結(jié)構(gòu) 圖11.8 “New Java Package”對話框
在“Name”文本框中輸入“com. myLogin.action”,然后單擊“Finish”按鈕,即可建立com. myLogin.action包。
用同樣的方法建立com. myLogin.bean和com. myLogin.impl包。
在myLogin上單擊右鍵,選擇菜單“New”|“Folder”命令,彈出“New Folder”對話框,如圖11.9所示。

圖11.9 “New Folder”對話框
在“New Folder”對話框中的“Folder Name”文本框中輸入“jsp”,然后單擊“Finish”按鈕,即可建立jsp文件夾。
把struts-1.3.8-all.zip解壓縮后struts-1.3.8目錄下的struts-core-1.3.8.jar,struts-el- 1.3.8.jar,struts-extras-1.3.8.jar,struts-faces-1.3.8.jar,struts-mailreader-dao-1.3.8.jar,struts-scripting- 1.3.8.jar,struts-taglib-1.3.8.jar,struts-tiles-1.3.8.jar,commons-beanutils-1.7.0.jar,commons-chain-1.1.jar,commons-digester-1.8.jar,commons-logging-1.0.4.jar12個jar從/WEB-INF/lib/中復制到myLogin/WEB-INF/lib目錄下,即CLASSPATH中。
在myLogin上單擊右鍵,選擇“Properties”命令,彈出“Properties for myLogin”對話框,如圖11.10所示。

圖11.10 “Properties for myLogin”對話框
在對話框左邊的樹形菜單中,單擊“Java Build Path”節(jié)點,系統(tǒng)將在“Properties for myLogin”對話框的右邊出現(xiàn)“Java Build Path”的相關(guān)屬性,如圖11.11所示。
在“Libraries”選項卡中,單擊“Add JARs…”按鈕,彈出“JAR Selection”對話框,如圖11.12所示。

圖11.11 “Java Build Path”的相關(guān)屬性 圖11.12 “JAR Selection”對話框
在“JAR Selection”對話框中,單擊“myLogin”節(jié)點,打開樹形菜單,如圖11.13所示。
在打開的樹形菜單中,選擇struts-core-1.3.8.jar,struts-el-1.3.8.jar,struts-extras-1.3.8.jar, struts-faces-1.3.8.jar,struts-mailreader-dao-1.3.8.jar,struts-scripting-1.3.8.jar,struts-taglib-1.3.8.jar,struts-tiles-1.3.8.jar,commons-beanutils-1.7.0.jar,commons-chain-1.1.jar,commons-digester-1.8.jar,co- mmons-logging-1.0.4.jar,按住“Ctrl”鍵,選中這12個jar,然后單擊“OK”按鈕,返回“Properties for myLogin”對話框,如圖11.14所示。

圖11.13 打開樹形菜單 圖11.14 “Properties for myLogin”對話框
在“Properties for myLogin”對話框,單擊“OK”按鈕,上述12個jar加入到CLASSPATH中,完成對Struts的配置。
如果需要其他的jar,也要用這種方式加入到CLASSPATH中。
最終配置好Struts的myLogin項目的目錄結(jié)構(gòu)如圖11.15所示。

圖11.15 配置好Struts的myLogin項目的目錄結(jié)構(gòu)
上述建立myLogin工程的步驟很長,目的是讓讀者不要在這上面耗費太多的時間,只要讀者按照上面介紹的步驟一步一步地來,在環(huán)境配置方面就不會有太多的問題。
11.2 Struts的核心
支持Struts的核心類主要包括:ActionServlet,Action,ActionForm,Action Mapping等,它們的運行機理如圖11.16所示。
圖11.16 Struts核心類的運行機理
從圖11-6可以看出這些核心類的運作模式,下面分別進行講解。
11.2.1 ActionServlet(分發(fā)器)
org.apache.struts.action.ActionServlet繼承于javax.servlet.http.HttpServlet,ActionServlet是MVC模式中的Controller,是一個FrontController。它是一個標準的Servlet,它將request轉(zhuǎn)發(fā)給RequestProcessor來處理,執(zhí)行該方法后會返回一個ActionForward。這和Spring的DispatcherServlet是類似的,只不過Spring的DispatcherServlet 返回的是一個ModelAndView。ActionServlet的示例代碼如下:
//*******ActionServlet.java**************
package org.apache.struts.action;
//引入apache的commons包
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.logging.LogFactory;
//引入struts包
import org.apache.struts.Globals;
import org.apache.struts.util.RequestUtils;
import org.xml.sax.SAXException;
//引入servlet包
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletResponse;
//引入io包
import java.io.IOException;
import java.io.InputStream;
//引入math包
import java.math.BigDecimal;
import java.math.BigInteger;
//引入net包
import java.net.MalformedURLException;
import java.net.URLConnection;
//引入util包
import java.util.ArrayList;
import java.util.MissingResourceException;
/** 該類是作為MVC的Controller來使用的,繼承于HttpServlet */
public class ActionServlet extends HttpServlet {
/** 定義日志 */
protected static Log log = LogFactory.getLog(ActionServlet.class);
/** 定義默認的配置文檔 */
protected String config = "/WEB-INF/struts-config.xml";
/** 定義默認的commons-chain 的配置文檔*/
protected String chainConfig = "org/apache/struts/chain/chain-config.xml";
/** 定義configDigester */
protected Digester configDigester = null;
/** 定義向后兼容性 */
protected boolean convertNull = false;
/** 定義消息資源 */
protected MessageResources internal = null;
/** 定義資源基類 */
protected String internalName = "org.apache.struts.action.ActionResources";
/** 定義DTD的有關(guān)配置信息的版本 */
protected String[] registrations =
{
"-//Apache Software Foundation//DTD Struts Configuration 1.0//EN",
"/org/apache/struts/resources/struts-config_1_0.dtd",
"-//Apache Software Foundation//DTD Struts Configuration 1.1//EN",
"/org/apache/struts/resources/struts-config_1_1.dtd",
"-//Apache Software Foundation//DTD Struts Configuration 1.2//EN",
"/org/apache/struts/resources/struts-config_1_2.dtd",
"-//Apache Software Foundation//DTD Struts Configuration 1.3//EN",
"/org/apache/struts/resources/struts-config_1_3.dtd",
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN",
"/org/apache/struts/resources/web-app_2_3.dtd"
};
/** 映射描述 */
protected String servletMapping = null; // :FIXME: - multiples?
/** 定義Servlet的名稱 */
protected String servletName = null;
/** 覆寫destroy 方法,用來處理一些銷毀動作 */
if (log.isDebugEnabled()) {
log.debug(internal.getMessage("finalizing"));
}
//銷毀模組
destroyModules();
//銷毀相關(guān)屬性
destroyInternal();
getServletContext().removeAttribute(Globals.ACTION_SERVLET_KEY);
//獲取當前的加載類
ClassLoader classLoader =
Thread.currentThread().getContextClassLoader();
//如果為空,則獲取ActionServlet
if (classLoader == null) {
classLoader = ActionServlet.class.getClassLoader();
}
//釋放日志類
try {
LogFactory.release(classLoader);
} catch (Throwable t) {
;
}
//清空目錄日志工廠
CatalogFactory.clear();
//清空相關(guān)的屬性
PropertyUtils.clearDescriptors();
}
……
上面的代碼主要是覆寫了Servlet的destroy()方法,用來進行一些銷毀的動作,如釋放相關(guān)的模組、日志等,接下來就要進行一些初始化的工作,代碼如下所示:
……
/**處理一些初始化動作*/
public void init() throws ServletException {
final String configPrefix = "config/";
//獲取配置文檔的前綴為config的長度
final int configPrefixLength = configPrefix.length() - 1;
try {
initOther();
//初始化servlet
initServlet();
initChain();
//設定全局的servlet關(guān)鍵字
getServletContext().setAttribute(Globals.ACTION_SERVLET_KEY, this);
initModuleConfigFactory();
//初始化模組
ModuleConfig moduleConfig = initModuleConfig("", config);
//初始化消息
initModuleMessageResources(moduleConfig);
initModulePlugIns(moduleConfig);
//初始化formbean
initModuleFormBeans(moduleConfig);
//初始化forward
initModuleForwards(moduleConfig);
//初始化異常
initModuleExceptionConfigs(moduleConfig);
//初始化action
initModuleActions(moduleConfig);
moduleConfig.freeze();
//獲取初始化的參數(shù)名
Enumeration names = getServletConfig().getInitParameterNames();
while (names.hasMoreElements()) {
String name = (String) names.nextElement();
//假如獲取的名稱前綴不是config,則繼續(xù)獲取
if (!name.startsWith(configPrefix)) {
continue;
}
//獲取名稱的前綴
String prefix = name.substring(configPrefixLength);
//初始化模組的配置信息
moduleConfig =
initModuleConfig(prefix,
getServletConfig().getInitParameter(name));
initModuleMessageResources(moduleConfig);
initModulePlugIns(moduleConfig);
//初始化formbean
initModuleFormBeans(moduleConfig);
//初始化forward
initModuleForwards(moduleConfig);
initModuleExceptionConfigs(moduleConfig);
//初始化action
initModuleActions(moduleConfig);
moduleConfig.freeze();
}
//初始化模組的前綴
this.initModulePrefixes(this.getServletContext());
this.destroyConfigDigester();
} catch (UnavailableException ex) {
throw ex;
} catch (Throwable t) {
//記錄處理異常的日志
log.error("Unable to initialize Struts ActionServlet due to an "
+ "unexpected exception or error thrown, so marking the "
+ "servlet as unavailable. Most likely, this is due to an "
+ "incorrect or missing library dependency.", t);
throw new UnavailableException(t.getMessage());
}
}
/** 初始化模組的前綴 */
protected void initModulePrefixes(ServletContext context) {
ArrayList prefixList = new ArrayList();
//獲取相關(guān)的屬性名
Enumeration names = context.getAttributeNames();
//循環(huán)迭代屬性名
while (names.hasMoreElements()) {
String name = (String) names.nextElement();
//如果前綴不是定義的全局關(guān)鍵字,則繼續(xù)
if (!name.startsWith(Globals.MODULE_KEY)) {
continue;
}
//獲取前綴
String prefix = name.substring(Globals.MODULE_KEY.length());
//如果有前綴,則加入到list中
if (prefix.length() > 0) {
prefixList.add(prefix);
}
}
//將list轉(zhuǎn)換為數(shù)組
String[] prefixes =
(String[]) prefixList.toArray(new String[prefixList.size()]);
//設定相關(guān)屬性
context.setAttribute(Globals.MODULE_PREFIXES_KEY, prefixes);
}
……
上述代碼主要是進行初始化工作,如初始化Servlet、FormBean、異常、日志、模組等。為了轉(zhuǎn)發(fā)從視圖層傳來的請求,還必須覆寫doGet和doPost方法,然后這兩個方法同時調(diào)用了process方法,代碼如下所示:
……
/** 覆寫doGet方法,接受用戶在頁面的請求,調(diào)用process */
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
process(request, response);
}
** 覆寫doPost方法,接受用戶在頁面的請求,調(diào)用process */
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
process(request, response);
}
/** 增加servlet的映射 */
public void addServletMapping(String servletName, String urlPattern) {
if (servletName == null) {
return;
}
//判斷傳入的servlet的名稱和本身的是否一致
if (servletName.equals(this.servletName)) {
if (log.isDebugEnabled()) {
log.debug("Process servletName=" + servletName
+ ", urlPattern=" + urlPattern);
}
//將url賦給servletMapping
this.servletMapping = urlPattern;
}
}
/** 獲取消息資源 */
public MessageResources getInternal() {
return (this.internal);
}
/** 銷毀模組 */
protected void destroyModules() {
ArrayList values = new ArrayList();
Enumeration names = getServletContext().getAttributeNames();
//對名稱進行循環(huán),放在list中
while (names.hasMoreElements()) {
values.add(names.nextElement());
}
//將list轉(zhuǎn)換為Iterator
Iterator keys = values.iterator();
//進行迭代
while (keys.hasNext()) {
String name = (String) keys.next();
Object value = getServletContext().getAttribute(name);
//判斷獲取的名稱是否是ModuleConfig的實例
if (!(value instanceof ModuleConfig)) {
continue;
}
//如果是,則轉(zhuǎn)換為ModuleConfig
ModuleConfig config = (ModuleConfig) value;
if (this.getProcessorForModule(config) != null) {
this.getProcessorForModule(config).destroy();
}
//在ServletContext中移除該屬性
getServletContext().removeAttribute(name);
PlugIn[] plugIns =
(PlugIn[]) getServletContext().getAttribute(Globals.PLUG_INS_KEY
+ config.getPrefix());
//銷毀PlugIn
if (plugIns != null) {
for (int i = 0; i < plugIns.length; i++) {
int j = plugIns.length - (i + 1);
//銷毀PlugIn
plugIns[j].destroy();
}
//在ServletContext移除PLUG的關(guān)鍵字
getServletContext().removeAttribute(Globals.PLUG_INS_KEY
+ config.getPrefix());
}
}
}
/** 釋放configDigester */
protected void destroyConfigDigester() {
configDigester = null;
}
/** 釋放MessageResources */
protected void destroyInternal() {
internal = null;
}
/** 根據(jù)傳入的request,獲取ModuleConfig */
protected ModuleConfig getModuleConfig(HttpServletRequest request) {
ModuleConfig config = (ModuleConfig) request.getAttribute(Globals.MODULE_KEY);
//如果為空,則獲取ServletContext中的內(nèi)容
if (config == null) {
config = (ModuleConfig) getServletContext().getAttribute(Globals.MODULE_KEY);
}
return (config);
}
/**獲取RequestProcessor */
protected synchronized RequestProcessor getRequestProcessor(
ModuleConfig config) throws ServletException {
RequestProcessor processor = this.getProcessorForModule(config);
//假如沒有,則創(chuàng)建一個新的RequestProcessor
if (processor == null) {
try {
//創(chuàng)建一個新的RequestProcessor
processor = (RequestProcessor) RequestUtils.applicationInstance(config. getControllerConfig().getProcessorClass());
} catch (Exception e) {
throw new UnavailableException(
"Cannot initialize RequestProcessor of class "
+ config.getControllerConfig().getProcessorClass() + ": "
+ e);
}
//對RequestProcessor進行初始化
processor.init(this, config);
//設定關(guān)鍵字
String key = Globals.REQUEST_PROCESSOR_KEY + config.getPrefix();
//將關(guān)鍵字加入到ServletContext中
getServletContext().setAttribute(key, processor);
}
//返回RequestProcessor
return (processor);
}
/** 初始化ModuleConfig */
protected ModuleConfig initModuleConfig(String prefix, String paths)
throws ServletException {
if (log.isDebugEnabled()) {
log.debug("Initializing module path '" + prefix
+ "' configuration from '" + paths + "'");
}
//解析ModuleConfig
ModuleConfigFactory factoryObject = ModuleConfigFactory.createFactory();
ModuleConfig config = factoryObject.createModuleConfig(prefix);
// 初始化Digester
Digester digester = initConfigDigester();
//獲取路徑
List urls = splitAndResolvePaths(paths);
URL url;
//將配置信息加入到Digester
for (Iterator i = urls.iterator(); i.hasNext();) {
url = (URL) i.next();
digester.push(config);
//對Digester進行解析
this.parseModuleConfigFile(digester, url);
}
//將配置前綴加入到ServletContext
getServletContext().setAttribute(Globals.MODULE_KEY
+ config.getPrefix(), config);
//返回config
return config;
}
//……相關(guān)的異常處理配置和其他初始化的配置信息,這里不再進行講解
/** 初始化servlet */
protected void initServlet()
throws ServletException {
//獲取ervlet name
this.servletName = getServletConfig().getServletName();
// 準備一個Digester
Digester digester = new Digester();
//設定Digester的相關(guān)屬性
digester.push(this);
digester.setNamespaceAware(true);
digester.setValidating(false);
// 注冊DTDs
for (int i = 0; i < registrations.length; i += 2) {
URL url = this.getClass().getResource(registrations[i + 1]);
//如果url不為空,則注冊
if (url != null) {
digester.register(registrations[i], url.toString());
}
}
//配置相關(guān)的配置信息
digester.addCallMethod("web-app/servlet-mapping", "addServletMapping", 2);
digester.addCallParam("web-app/servlet-mapping/servlet-name", 0);
digester.addCallParam("web-app/servlet-mapping/url-pattern", 1);
//記錄目前的進程
if (log.isDebugEnabled()) {
log.debug("Scanning web.xml for controller servlet mapping");
}
//獲取web.xml
InputStream input =
getServletContext().getResourceAsStream("/WEB-INF/web.xml");
//如果沒有獲取到web.xml
if (input == null) {
log.error(internal.getMessage("configWebXml"));
throw new ServletException(internal.getMessage("configWebXml"));
}
//解析web.xml
try {
digester.parse(input);
} catch (IOException e) {
log.error(internal.getMessage("configWebXml"), e);
throw new ServletException(e);
} catch (SAXException e) {
//如果web.xml語法配置有錯誤
log.error(internal.getMessage("configWebXml"), e);
throw new ServletException(e);
} finally {
try {
//關(guān)閉web.xml
input.close();
} catch (IOException e) {
log.error(internal.getMessage("configWebXml"), e);
throw new ServletException(e);
}
}
// 記錄在web.xml中的Servlet名稱
if (log.isDebugEnabled()) {
log.debug("Mapping for servlet '" + servletName + "' = '"
+ servletMapping + "'");
}
//如果在web.xml中定義了映射
if (servletMapping != null) {
getServletContext().setAttribute(Globals.SERVLET_KEY, servletMapping);
}
}
……
通過前面的初始化工作及視圖層的請求轉(zhuǎn)發(fā),Struts即可進入process方法,從而開始具體的請求轉(zhuǎn)發(fā)工作,代碼如下所示:
……
/** 具體負責用戶請求的進程 */
protected void process(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
ModuleUtils.getInstance().selectModule(request, getServletContext());
//根據(jù)request獲取ModuleConfig
ModuleConfig config = getModuleConfig(request);
//根據(jù)ModuleConfig獲取RequestProcessor
RequestProcessor processor = getProcessorForModule(config);
//如果RequestProcessor為空,則根據(jù)request獲取
if (processor == null) {
processor = getRequestProcessor(config);
}
//執(zhí)行相關(guān)的處理進程
processor.process(request, response);
}
}
從示例代碼可以看出,在ActionServlet的doGet和doPost方法中,都調(diào)用了process方法,從而將請求交給RequestProcessor的process方法來進行處理。
//*******RequestProcessor.java**************
package org.apache.struts.action;
//引入apache的日志
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts.util.MessageResources;
import org.apache.struts.util.RequestUtils;
//引入servlet
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
//引入io
import java.io.IOException;
//引入util
import java.util.HashMap;
import java.util.Iterator;
import java.util.Locale;
/** 該類用來處理從request來的請求 */
public class RequestProcessor {
/** 定義include的路徑信息 */
public static final String INCLUDE_PATH_INFO =
"javax.servlet.include.path_info";
/** 定義servlet的路徑信息 */
public static final String INCLUDE_SERVLET_PATH =
"javax.servlet.include.servlet_path";
/** 定義日志 */
protected static Log log = LogFactory.getLog(RequestProcessor.class);
/** 定義要處理的action */
protected HashMap actions = new HashMap();
/** 定義配置信息 */
protected ModuleConfig moduleConfig = null;
/** 定義要使用的ActionServlet */
protected ActionServlet servlet = null;
/** 定義銷毀動作 */
public void destroy() {
synchronized (this.actions) {
Iterator actions = this.actions.values().iterator();
//銷毀每一個動作
while (actions.hasNext()) {
Action action = (Action) actions.next();
//設定動作的內(nèi)容為null
action.setServlet(null);
}
//清空儲存動作的Map
this.actions.clear();
}
//重置servlet為null
this.servlet = null;
}
/** 初始化 */
public void init(ActionServlet servlet, ModuleConfig moduleConfig)
throws ServletException {
//首先清空存儲動作的容器
synchronized (actions) {
actions.clear();
}
//然后設定servlet和配置信息
this.servlet = servlet;
this.moduleConfig = moduleConfig;
}
……
上述代碼主要用來做初始化和銷毀工作,也就是說每次在調(diào)用DispatcherServlet時,首先要將存儲的action清空,然后再將傳來的DispatcherServlet和相關(guān)配置檔注入RequestProcessor中,接著就要調(diào)用RequestProcessor的process方法,代碼如下所示:
……
/** 處理進程 */
public void process(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
//封裝request
request = processMultipart(request);
//獲取映射的路徑
String path = processPath(request, response);
//如果沒有路徑,則停止執(zhí)行
if (path == null) {
return;
}
//記錄要處理的方法和路徑
if (log.isDebugEnabled()) {
log.debug("Processing a '" + request.getMethod() + "' for path '"
+ path + "'");
}
//獲取request的Locale
processLocale(request, response);
//獲取request的Content
processContent(request, response);
processNoCache(request, response);
// General purpose preprocessing hook
if (!processPreprocess(request, response)) {
return;
}
//從request獲取消息
this.processCachedMessages(request, response);
// 從request獲取映射
ActionMapping mapping = processMapping(request, response, path);
//如果沒有映射,則返回
if (mapping == null) {
return;
}
//檢查要執(zhí)行這個動作的角色
if (!processRoles(request, response, mapping)) {
return;
}
//將request和ActionForm關(guān)聯(lián)起來
ActionForm form = processActionForm(request, response, mapping);
//從request獲取ActionForm,進行封裝
processPopulate(request, response, form, mapping);
//驗證封裝的數(shù)據(jù)是否有問題
try {
if (!processValidate(request, response, form, mapping)) {
return;
}
} catch (InvalidCancelException e) {
//如果有問題,則返回一個ActionForward
ActionForward forward = processException(request, response, e, form, mapping);
processForwardConfig(request, response, forward);
return;
} catch (IOException e) {
throw e;
} catch (ServletException e) {
throw e;
}
//根據(jù)映射判斷是否包含了Forward
if (!processForward(request, response, mapping)) {
return;
}
//檢查是否包含Include
if (!processInclude(request, response, mapping)) {
return;
}
//創(chuàng)建一個動作實例
Action action = processActionCreate(request, response, mapping);
//如果創(chuàng)建的動作為空,則返回
if (action == null) {
return;
}
//執(zhí)行這個動作,返回ActionForward
ActionForward forward =processActionPerform(request, response, action, form, mapping);
//將ActionForward放入Config中
processForwardConfig(request, response, forward);
}
/** 創(chuàng)建一個action */
protected Action processActionCreate(HttpServletRequest request,
HttpServletResponse response, ActionMapping mapping)
throws IOException {
//獲取action的名稱
String className = mapping.getType();
//記錄這個action的名稱
if (log.isDebugEnabled()) {
log.debug(" Looking for Action instance for class " + className);
}
Action instance;
//同步action
synchronized (actions) {
//返回存在的action實例
instance = (Action) actions.get(className);
//如果實例不為空,則返回
if (instance != null) {
if (log.isTraceEnabled()) {
log.trace(" Returning existing Action instance");
}
//返回action實例
return (instance);
}
// 如果實例為空,則重新創(chuàng)建一個
if (log.isTraceEnabled()) {
log.trace(" Creating new Action instance");
}
//創(chuàng)建一個action實例
try {
instance = (Action) RequestUtils.applicationInstance(className);
} catch (Exception e) {
log.error(getInternal().getMessage("actionCreate",
mapping.getPath()), e);
//發(fā)送錯誤信息 response.sendError(HttpServletResponse.SC_INTERNAL_ SERVER_ERROR,
getInternal().getMessage("actionCreate", mapping.getPath()));
//返回空
return (null);
}
//將action的名稱和實例存放在actions容器中
actions.put(className, instance);
}
//如果實例的servlet為空,則添加
if (instance.getServlet() == null) {
instance.setServlet(this.servlet);
}
//返回action的實例
return (instance);
}
/** 封裝ActionForm */
protected ActionForm processActionForm(HttpServletRequest request,
HttpServletResponse response, ActionMapping mapping) {
//創(chuàng)建一個ActionForm
ActionForm instance =
RequestUtils.createActionForm(request, mapping, moduleConfig,
servlet);
//如果創(chuàng)建不成功,就返回null
if (instance == null) {
return (null);
}
//假如request中沒有,則添加
if ("request".equals(mapping.getScope())) {
request.setAttribute(mapping.getAttribute(), instance);
} else {
//否則獲取session
HttpSession session = request.getSession();
//在session中存入
session.setAttribute(mapping.getAttribute(), instance);
}
//返回ActionForm實例
return (instance);
}
/** 處理Forward */
protected void processForwardConfig(HttpServletRequest request,
HttpServletResponse response, ForwardConfig forward)
throws IOException, ServletException {
if (forward == null) {
return;
}
//獲取forward的路徑
String forwardPath = forward.getPath();
String uri;
// 獲取路徑
String actionIdPath = RequestUtils.actionIdURL (forward, request, servlet);
if (actionIdPath != null) {
forwardPath = actionIdPath;
ForwardConfig actionIdForward = new ForwardConfig(forward);
//設定路徑
actionIdForward.setPath(actionIdPath);
forward = actionIdForward;
}
//假如路徑中有/
if (forwardPath.startsWith("/")) {
//處理后再獲取
uri = RequestUtils.forwardURL(request, forward, null);
} else {
uri = forwardPath;
}
//如果有設定Redirect
if (forward.getRedirect()) {
//對設定的Redirect進程處理
if (uri.startsWith("/")) {
uri = request.getContextPath() + uri;
}
//執(zhí)行response的sendRedirect方法
response.sendRedirect(response.encodeRedirectURL(uri));
} else {
//執(zhí)行doForward,返回視圖
doForward(uri, request, response);
}
}
/** 執(zhí)行Action */
protected ActionForward processActionPerform(HttpServletRequest request,
HttpServletResponse response, Action action, ActionForm form,
ActionMapping mapping)
throws IOException, ServletException {
try {
//執(zhí)行action的execute方法
return (action.execute(mapping, form, request, response));
} catch (Exception e) {
return (processException(request, response, e, form, mapping));
}
}
/** 處理信息 */
protected void processCachedMessages(HttpServletRequest request,
HttpServletResponse response) {
HttpSession session = request.getSession(false);
//如果沒有session,則返回
if (session == null) {
return;
}
//獲取ActionMessages
ActionMessages messages =
(ActionMessages) session.getAttribute(Globals.MESSAGE_KEY);
//如果消息不為空,則移除
if (messages != null) {
if (messages.isAccessed()) {
session.removeAttribute(Globals.MESSAGE_KEY);
}
}
//獲取session中的錯誤消息
messages = (ActionMessages) session.getAttribute(Globals.ERROR_KEY);
//如果錯誤消息不為空,則移除
if (messages != null) {
if (messages.isAccessed()) {
session.removeAttribute(Globals.ERROR_KEY);
}
}
}
/** 處理Content */
protected void processContent(HttpServletRequest request,
HttpServletResponse response) {
//獲取contentType
String contentType =
moduleConfig.getControllerConfig().getContentType();
//如果contentType不為空
if (contentType != null) {
response.setContentType(contentType);
}
}
/** 處理異常 */
protected ActionForward processException(HttpServletRequest request,
HttpServletResponse response, Exception exception, ActionForm form,
ActionMapping mapping)
throws IOException, ServletException {
//獲取異常句柄
ExceptionConfig config = mapping.findException(exception.getClass());
//如果句柄為空
if (config == null) {
log.warn(getInternal().getMessage("unhandledException",exception. getClass()));
//判斷異常類型
if (exception instanceof IOException) {
throw (IOException) exception;
} else if (exception instanceof ServletException) {
throw (ServletException) exception;
} else {
throw new ServletException(exception);
}
}
// 創(chuàng)建一個異常處理的實例
try {
ExceptionHandler handler = (ExceptionHandler) RequestUtils.applicationInstance (config.getHandler());
//執(zhí)行實例的execute方法
return (handler.execute(exception, config, mapping, form, request,
response));
} catch (Exception e) {
throw new ServletException(e);
}
}
/** 處理Forward */
protected boolean processForward(HttpServletRequest request,
HttpServletResponse response, ActionMapping mapping)
throws IOException, ServletException {
//獲取Forward
String forward = mapping.getForward();
//如果Forward為空
if (forward == null) {
return (true);
}
//獲取Forward
String actionIdPath = RequestUtils.actionIdURL(forward, this.moduleConfig, this.servlet);
if (actionIdPath != null) {
forward = actionIdPath;
}
//建立關(guān)聯(lián)
internalModuleRelativeForward(forward, request, response);
//返回false
return (false);
}
/** 處理Include */
protected boolean processInclude(HttpServletRequest request,
HttpServletResponse response, ActionMapping mapping)
throws IOException, ServletException {
//從映射中獲取include
String include = mapping.getInclude();
//如果include為空,則返回
if (include == null) {
return (true);
}
//如果forward在action中有別名
String actionIdPath = RequestUtils.actionIdURL(include, this.moduleConfig, this.servlet);
if (actionIdPath != null) {
include = actionIdPath;
}
//建立關(guān)聯(lián)
internalModuleRelativeInclude(include, request, response);
//返回false
return (false);
}
/** 處理Locale */
protected void processLocale(HttpServletRequest request,
HttpServletResponse response) {
//判斷配置里是否有Locale
if (!moduleConfig.getControllerConfig().getLocale()) {
return;
}
//如果已經(jīng)有一個Locale
HttpSession session = request.getSession();
//查看session里是否使用存儲了Locale,如果有就不需要了
if (session.getAttribute(Globals.LOCALE_KEY) != null) {
return;
}
//獲取Locale
Locale locale = request.getLocale();
//如果Locale不為空
if (locale != null) {
if (log.isDebugEnabled()) {
log.debug(" Setting user locale '" + locale + "'");
}
//加入到session中
session.setAttribute(Globals.LOCALE_KEY, locale);
}
}
/** 處理映射 */
protected ActionMapping processMapping(HttpServletRequest request,
HttpServletResponse response, String path)
throws IOException {
//獲取ActionMapping
ActionMapping mapping = (ActionMapping) moduleConfig.findActionConfig(path);
//判斷request里是否有映射
if (mapping != null) {
request.setAttribute(Globals.MAPPING_KEY, mapping);
//如果有,就返回
return (mapping);
}
//獲取ActionConfig
ActionConfig[] configs = moduleConfig.findActionConfigs();
//獲取映射,存入request中
for (int i = 0; i < configs.length; i++) {
if (configs[i].getUnknown()) {
mapping = (ActionMapping) configs[i];
request.setAttribute(Globals.MAPPING_KEY, mapping);
//返回映射
return (mapping);
}
}
//如果沒有映射,則返回一個消息
String msg = getInternal().getMessage("processInvalid");
//記錄這個錯誤消息
log.error(msg + " " + path);
response.sendError(HttpServletResponse.SC_NOT_FOUND, msg);
return null;
}
/** 處理組件 */
protected HttpServletRequest processMultipart(HttpServletRequest request) {
//如果獲取的request方法不是post
if (!"POST".equalsIgnoreCase(request.getMethod())) {
return (request);
}
//獲取contentType
String contentType = request.getContentType();
//如果contentType的類型是multipart/form-data,則封裝
if ((contentType != null)&& contentType.startsWith("multipart/form-data")) {
//對request進行封裝
return (new MultipartRequestWrapper(request));
} else {
return (request);
}
}
/** 處理NoCache */
protected void processNoCache(HttpServletRequest request,
HttpServletResponse response) {
//設定Nocache
if (moduleConfig.getControllerConfig().getNocache()) {
//設定頭
response.setHeader("Pragma", "No-cache");
response.setHeader("Cache-Control", "no-cache,no-store,max-age=0");
response.setDateHeader("Expires", 1);
}
}
/** 處理路徑*/
protected String processPath(HttpServletRequest request,
HttpServletResponse response)
throws IOException {
String path;
//獲取path
path = (String) request.getAttribute(INCLUDE_PATH_INFO);
//如果path為空
if (path == null) {
path = request.getPathInfo();
}
//如果不為空,則返回這個path
if ((path != null) && (path.length() > 0)) {
return (path);
}
//獲取servlet 的path
path = (String) request.getAttribute(INCLUDE_SERVLET_PATH);
//如果為空
if (path == null) {
path = request.getServletPath();
}
//如果不為空
String prefix = moduleConfig.getPrefix();
//獲取前綴
if (!path.startsWith(prefix)) {
String msg = getInternal().getMessage("processPath");
//記錄錯誤信息
log.error(msg + " " + request.getRequestURI());
response.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
//返回空
return null;
}
//截取path
path = path.substring(prefix.length());
//根據(jù)/和.進行截取
int slash = path.lastIndexOf("/");
int period = path.lastIndexOf(".");
//截取path
if ((period >= 0) && (period > slash)) {
path = path.substring(0, period);
}
//返回path
return (path);
}
……
上述代碼主要展示了process方法是如何進行轉(zhuǎn)發(fā)視圖層的請求的。下面主要展示process方法是如何處理模型層處理后要返回視圖層的動作的,代碼如下所示:
……
//……其他的一些處理器,這里不再進行詳細說明
/**根據(jù)url進行farword*/
protected void doForward(String uri, HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
//使用RequestDispatcher返回視圖層
RequestDispatcher rd = getServletContext().getRequestDispatcher(uri);
//判斷RequestDispatcher是否為空
if (rd == null) {
//返回錯誤信息
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
getInternal().getMessage("requestDispatcher", uri));
return;
}
//使用RequestDispatcher的forward方法
rd.forward(request, response);
}
/** 根據(jù)url,進行Include*/
protected void doInclude(String uri, HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
//使用RequestDispatcher返回視圖層
RequestDispatcher rd = getServletContext().getRequestDispatcher(uri);
//判斷RequestDispatcher是否為空
if (rd == null) {
//返回錯誤信息
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
getInternal().getMessage("requestDispatcher", uri));
return;
}
//使用RequestDispatcher的include方法
rd.include(request, response);
}
/** 返回MessageResources */
protected MessageResources getInternal() {
return (servlet.getInternal());
}
/** 返回ServletContext */
protected ServletContext getServletContext() {
return (servlet.getServletContext());
}
}
通過上面的代碼分析,可以看出,這里主要使用web.xml配置文件,也就是說,在Struts中,主要是通過配置文件來完成的,web.xml的示例代碼如下:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4"
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">
<servlet>
<servlet-name>actionServlet</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>
<!--應用程序的資源集合的類-->
<param-name> application </param-name>
<param-value>null</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>actionServlet</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app>
代碼說明:
— 這里的初始化參數(shù)<init-param>可以定義多個。
在web.xml里定義了servlet的映射和要使用的Struts配置文件,這里再給出一個struts-config.xml的示例代碼:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.2//EN" "http://jakarta.apache.org/struts/dtds/struts-config_1_2.dtd">
<struts-config>
<action-mappings>
<action path="/helloWorld" type="com.gf.action.HelloWorldAction">
<forward name="index" path="/WEB-INF/jsp/index.jsp"/>
</action>
</action-mappings>
</struts-config>
可以看出,在控制器部分,Struts和Spring很相似。Struts controller基本功能如下:
(1)截獲用戶的Http請求。
(2)把這個請求映射到相應的Action類,如果這是此類收到的第一個請求,則將初始化實例緩存。
(3)根據(jù)配置文件是否定義來創(chuàng)建或發(fā)現(xiàn)一個ActionForm bean實例,然后將請求過程移植到Bean。
(4)調(diào)用Action實例的exectue方法,并將ActioForm,Action Mapping,Request和Response對象傳給它。
(5)exectue返回一個ActionForward對象,然后返回視圖層。
11.2.2 Action(控制器)
如果要使用Struts,就離不開Action,但每個Action都只建立一個實例,所以Action不是線程安全的。Struts提供了多種Action供選擇使用。普通的Action只能通過調(diào)用execute執(zhí)行一項任務,而DispatchAction可以根據(jù)配置參數(shù)執(zhí)行,而不是僅進入execute()函數(shù),這樣可以執(zhí)行多種任務。LookupDispatchAction可以根據(jù)提交表單按鈕的名稱來執(zhí)行函數(shù),所有的控制器都繼承于Action,它的示例源代碼如下:
//*******Action.java**************
package org.apache.struts.action;
//引入struts的util包
import org.apache.struts.Globals;
import org.apache.struts.config.ModuleConfig;
import org.apache.struts.util.MessageResources;
import org.apache.struts.util.ModuleUtils;
import org.apache.struts.util.RequestUtils;
import org.apache.struts.util.TokenProcessor;
//引入servlet包
import javax.servlet.ServletContext;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.Locale;
/** 該類作為頁面請求和后臺處理的適配器 */
public class Action {
/** 定義一個TokenProcessor 實例 */
private static TokenProcessor token = TokenProcessor.getInstance();
/** 定義一個ActionServlet */
protected transient ActionServlet servlet = null;
/** 獲取一個ActionServlet */
public ActionServlet getServlet() {
return (this.servlet);
}
/** 設定ActionServlet */
public void setServlet(ActionServlet servlet) {
this.servlet = servlet;
}
/** 接受頁面請求,然后轉(zhuǎn)入處理的方法 */
public ActionForward execute(ActionMapping mapping, ActionForm form,
ServletRequest request, ServletResponse response)
throws Exception {
try {
//執(zhí)行execute方法
return execute(mapping, form, (HttpServletRequest) request,
(HttpServletResponse) response);
} catch (ClassCastException e) {
return null;
}
}
/** 接受頁面請求,然后轉(zhuǎn)入處理的方法,要繼承類來實現(xiàn)該方法 */
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response)
throws Exception {
return null;
}
/** 添加消息 */
protected void addMessages(HttpServletRequest request,
ActionMessages messages) {
if (messages == null) {
return;
}
//獲取已經(jīng)存在的消息類
ActionMessages requestMessages = (ActionMessages) request.getAttribute (Globals.MESSAGE_KEY);
//如果沒有,則創(chuàng)建一個新的
if (requestMessages == null) {
requestMessages = new ActionMessages();
}
//增加消息
requestMessages.add(messages);
//如果仍然沒有消息,則去除消息
if (requestMessages.isEmpty()) {
request.removeAttribute(Globals.MESSAGE_KEY);
return;
}
//將消息置入request
request.setAttribute(Globals.MESSAGE_KEY, requestMessages);
}
/** 添加錯誤 */
protected void addErrors(HttpServletRequest request, ActionMessages errors) {
if (errors == null) {
return;
}
//獲取已經(jīng)存在的錯誤類
ActionMessages requestErrors =
(ActionMessages) request.getAttribute(Globals.ERROR_KEY);
//如果沒有,則創(chuàng)建一個新的
if (requestErrors == null) {
requestErrors = new ActionMessages();
}
//添加錯誤信息
requestErrors.add(errors);
//如果仍然為空,則去除消息
if (requestErrors.isEmpty()) {
request.removeAttribute(Globals.ERROR_KEY);
return;
}
//將錯誤信息置入request
request.setAttribute(Globals.ERROR_KEY, requestErrors);
}
/** 產(chǎn)生一個Token */
protected String generateToken(HttpServletRequest request) {
return token.generateToken(request);
}
/** 獲取錯誤信息 */
protected ActionMessages getErrors(HttpServletRequest request) {
ActionMessages errors = (ActionMessages) request.getAttribute(Globals.ERROR_KEY);
//如果沒有,則創(chuàng)建一個新的
if (errors == null) {
errors = new ActionMessages();
}
//返回錯誤信息
return errors;
}
/** 獲取當前的Locale */
protected Locale getLocale(HttpServletRequest request) {
return RequestUtils.getUserLocale(request, null);
}
/** 獲取消息 */
protected ActionMessages getMessages(HttpServletRequest request) {
ActionMessages messages =ActionMessages) request.getAttribute(Globals. MESSAGE_KEY);
//如果沒有,則創(chuàng)建一個新的消息
if (messages == null) {
messages = new ActionMessages();
}
//返回消息
return messages;
}
/** 返回資源 */
protected MessageResources getResources(HttpServletRequest request) {
return ((MessageResources) request.getAttribute(Globals.MESSAGES_KEY));
}
/** 返回資源 */
protected MessageResources getResources(HttpServletRequest request,
String key) {
//獲取當前ServletContext
ServletContext context = getServlet().getServletContext();
ModuleConfig moduleConfig =ModuleUtils.getInstance().getModuleConfig(request, context);
// 返回MessageResources
return (MessageResources) context.getAttribute(key+ moduleConfig.getPrefix());
}
/** 返回Cancelled */
protected boolean isCancelled(HttpServletRequest request) {
return (request.getAttribute(Globals.CANCEL_KEY) != null);
}
/** 返回TokenValid */
protected boolean isTokenValid(HttpServletRequest request) {
return token.isTokenValid(request, false);
}
/** 返回TokenValid */
protected boolean isTokenValid(HttpServletRequest request, boolean reset) {
return token.isTokenValid(request, reset);
}
/** 重置Token */
protected void resetToken(HttpServletRequest request) {
token.resetToken(request);
}
/** 保存錯誤信息 */
protected void saveErrors(HttpServletRequest request, ActionMessages errors) {
//如果錯誤信息為空,則去除
if ((errors == null) || errors.isEmpty()) {
request.removeAttribute(Globals.ERROR_KEY);
//返回空
return;
}
//將錯誤信息置入request
request.setAttribute(Globals.ERROR_KEY, errors);
}
/** 保存消息 */
protected void saveMessages(HttpServletRequest request,ActionMessages messages) {
// 如果消息為空,則去除
if ((messages == null) || messages.isEmpty()) {
request.removeAttribute(Globals.MESSAGE_KEY);
return;
}
//將消息置入request
request.setAttribute(Globals.MESSAGE_KEY, messages);
}
/** 保存消息 */
protected void saveMessages(HttpSession session, ActionMessages messages) {
//如果消息為空,則去除
if ((messages == null) || messages.isEmpty()) {
request.removeAttribute(Globals.MESSAGE_KEY);
return;
}
//將消息置入request
session.setAttribute(Globals.MESSAGE_KEY, messages);
}
/** 保存錯誤 */
protected void saveErrors(HttpSession session, ActionMessages errors) {
//如果錯誤信息為空,則去除
if ((errors == null) || errors.isEmpty()) {
request.removeAttribute(Globals.ERROR_KEY);
//返回空
return;
}
//將錯誤信息置入request
session.setAttribute(Globals.ERROR_KEY, errors);
}
/** 保存Token */
protected void saveToken(HttpServletRequest request) {
token.saveToken(request);
}
/** 設定Locale */
protected void setLocale(HttpServletRequest request, Locale locale) {
HttpSession session = request.getSession();
//如果為空,則使用默認的
if (locale == null) {
locale = Locale.getDefault();
}
//將locale置入session
session.setAttribute(Globals.LOCALE_KEY, locale);
}
}
11.2.3 Action Mapping(映射)
ActionMapping是ActionConfig的子類,實質(zhì)上是對struts-config.xml的一個映射,從中可以取得所有的配置信息。
ActionServelt將ActionMapping傳遞給Action,主要是通過配置文件來實現(xiàn)的,在Struts中,默認的配置文件是WEB-INF下的struts-config.xml。通過配置文件struts-config.xml可以定義全局轉(zhuǎn)發(fā)的方式、ActionMapping、ActionForm和數(shù)據(jù)源。先來看前面的示例代碼:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.2//EN" "http://jakarta.apache.org/struts/dtds/struts-config_1_2.dtd">
<struts-config>
<action-mappings>
<!--請求的url對應于< action>中的path屬性-->
<action path="/helloWorld" type="com.gc.action.HelloWorldAction">
<!-- forward 表示執(zhí)行完畢后返回的頁面-->
<forward name="index" path="/WEB-INF/jsp/index.jsp"/>
</action>
</action-mappings>
</struts-config>
代碼說明如下:
— <action-mappings>用來描述ActionMapping,在<action-mappings>下的每一個<action>都是一個ActionMapping對象。當客戶端發(fā)出請求至ActionServlet時,請求的url對應于< action>中的path屬性,而要執(zhí)行的Action則是type屬性所設定的內(nèi)容。執(zhí)行完Action后,返回的ActionForward則在< forward >中設定。
— 上面配置文件的訪問路徑是:http://localhost:8080/myStruts/ helloWorld.do。
(1)全局轉(zhuǎn)發(fā)的定義方式
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.2//EN" "http://jakarta.apache.org/struts/dtds/struts-config_1_2.dtd">
<struts-config>
<!-- forward的屬性name表示全局轉(zhuǎn)發(fā)的名稱,path表示全局轉(zhuǎn)發(fā)的相對路徑-->
<global-forwards>
<forward name="welcome" path="/WEB-INF/jsp/welcome.jsp"/>
</global-forwards>
<action-mappings>
<!--請求的url對應于< action>中的path屬性-->
<action path="/helloWorld" type="com.gc.action.HelloWorldAction">
<!-- forward 表示執(zhí)行完畢后返回的頁面-->
<forward name="index" path="/WEB-INF/jsp/index.jsp"/>
</action>
</action-mappings>
</struts-config>
代碼說明:
— <global-forwards>中,forward的屬性name表示全局轉(zhuǎn)發(fā)的名稱,path表示全局轉(zhuǎn)發(fā)的相對路徑。
— forward除了name和path屬性外,還可以設定redirect屬性,如果redirect屬性設置為true,則ActionServlet會使用sendRedirect方法來轉(zhuǎn)發(fā)資源。
(2)定義ActionMapping
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.2//EN" "http://jakarta.apache.org/struts/dtds/struts-config_1_2.dtd">
<struts-config>
<!--全局路徑-->
<global-forwards>
<forward name="welcome" path="/WEB-INF/jsp/welcome.jsp"/>
</global-forwards>
<action-mappings>
<!--請求的url對應于< action>中的path屬性-->
<action path="/helloWorld" type="com.gc.action.HelloWorldAction">
<!-- forward 表示執(zhí)行完畢后返回的頁面-->
<forward name="index" path="/WEB-INF/jsp/index.jsp"/>
</action>
<action path="/regedit" type="com.gc.action.RegeditAction">
<!-- forward 表示執(zhí)行完畢后返回的頁面-->
<forward name="regedit" path="/WEB-INF/jsp/regedit.jsp"/>
</action>
</action-mappings>
</struts-config>
代碼說明:
— <action-mappings>中的每一個<action>都對應一個ActionMapping對象。
— <action>除了path和type屬性外,還可以設定很多屬性:name屬性,用來表示與Action相關(guān)聯(lián)的ActionForm;scope屬性,用來表示ActionForm的作用域;prefix屬性,用來表示ActionForm的前綴;suffix屬性,用來表示ActionForm的后綴;input屬性,用來表示當ActionForm發(fā)生錯誤時,必須返回的表單路徑;unknown屬性,設為true時,所有沒有定義ActionMapping的操作都將轉(zhuǎn)到這里來;validate屬性,設為true時,表示在執(zhí)行Action前,會調(diào)用ActionForm的validate方法來檢查輸入值。
(3)定義ActionForm
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.2//EN" "http://jakarta.apache.org/struts/dtds/struts-config_1_2.dtd">
<struts-config>
<!--全局路徑-->
<global-forwards>
<!-- forward 表示執(zhí)行完畢后返回的頁面-->
<forward name="welcome" path="/WEB-INF/jsp/welcome.jsp"/>
</global-forwards>
<form-beans>
<form-bean name="user" type=" com.gc.action.User"/>
</form-beans>
<action-mappings>
<!--請求的url對應于< action>中的path屬性-->
<action path="/helloWorld" type="com.gc.action.HelloWorldAction">
<!-- forward 表示執(zhí)行完畢后返回的頁面-->
<forward name="index" path="/WEB-INF/jsp/index.jsp"/>
</action>
<action path="/regedit" type="com.gc.action.RegeditAction" name=”user”>
<!-- forward 表示執(zhí)行完畢后返回的頁面-->
<forward name="regedit" path="/WEB-INF/jsp/regedit.jsp"/>
<forward name="success" path="/WEB-INF/jsp/success.jsp"/>
</action>
</action-mappings>
</struts-config>
代碼說明:
— <form-beans>用來定義用到的ActionForm,<form-bean>的屬性包括name和type。
— 在path屬性為regedit的action中,定義name屬性為user。
(4)定義數(shù)據(jù)源
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.2//EN" "http://jakarta.apache.org/struts/dtds/struts-config_1_2.dtd">
<struts-config>
<!--全局路徑-->
<global-forwards>
<forward name="welcome" path="/WEB-INF/jsp/welcome.jsp"/>
</global-forwards>
<form-beans>
<form-bean name="user" type=" com.gc.action.User"/>
</form-beans>
<action-mappings>
<!--請求的url對應于< action>中的path屬性-->
<action path="/helloWorld" type="com.gc.action.HelloWorldAction">
<forward name="index" path="/WEB-INF/jsp/index.jsp"/>
</action>
<action path="/regedit" type="com.gc.action.RegeditAction" name=”user”>
<!-- forward 表示執(zhí)行完畢后返回的頁面-->
<forward name="regedit" path="/WEB-INF/jsp/regedit.jsp"/>
<forward name="success" path="/WEB-INF/jsp/success.jsp"/>
</action>
</action-mappings>
<!--在Action中可以通過getDataSource(request, "conPool")來取得DataSource-->
<data-sources>
<data-source key=”conPool” type=” org.apache.commons.dbcp.BasicDataSource”
<set-property
autoCommit="true"
description="數(shù)據(jù)庫連接池"
driverClass="com.microsoft.jdbc.sqlserver.SQLServerDriver"
maxCount="150"
minCount="20"
url="jdbc:microsoft:sqlserver://localhost:1433/stdb"
username="admin"
password="admin" />
<data-source/>
</data-sources>
</struts-config>
代碼說明:
— 定義<data-source>的屬性key為conPool,則在Action中可以通過getDataSource(request, "conPool")來取得DataSource。
— 定義<data-source>的屬性type為org.apache.commons.dbcp.BasicDataSource,表示使用的是DBCP連接池。
— 在<set-property>中定義了與數(shù)據(jù)庫相關(guān)的屬性,因為相關(guān)屬性和其他數(shù)據(jù)庫類似,這里就不再解釋了。
//******* ActionMapping.java**************
package org.apache.struts.action;
//引入日志
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts.config.ActionConfig;
import org.apache.struts.config.ForwardConfig;
import java.util.ArrayList;
/** 該類用于映射,繼承于ActionConfig */
public class ActionMapping extends ActionConfig {
/** 定義日志類 */
private static Log log = LogFactory.getLog(ActionMapping.class);
/** 獲取ActionForward */
public ActionForward findForward(String forwardName) {
ForwardConfig config = findForwardConfig(forwardName);
//如果ForwardConfig為空
if (config == null) {
config = getModuleConfig().findForwardConfig(forwardName);
}
//如果ForwardConfig為空,記錄警告
if (config == null) {
if (log.isWarnEnabled()) {
log.warn("Unable to find '" + forwardName + "' forward.");
}
}
//返回ActionForward
return ((ActionForward) config);
}
/** 返回Forwards */
public String[] findForwards() {
ArrayList results = new ArrayList();
ForwardConfig[] fcs = findForwardConfigs();
//添加ForwardConfig
for (int i = 0; i < fcs.length; i++) {
results.add(fcs[i].getName());
}
//將list轉(zhuǎn)換為數(shù)組
return ((String[]) results.toArray(new String[results.size()]));
}
/** 獲取InputForward */
public ActionForward getInputForward() {
if (getModuleConfig().getControllerConfig().getInputForward()) {
return (findForward(getInput()));
} else {
//如果沒有,則創(chuàng)建一個新的
return (new ActionForward(getInput()));
}
}
}
11.2.4 ActionForm(表單控制器)
ActionForm使用了ViewHelper模式,是對HTML中Form的一個封裝,其中包含有validate方法,用于驗證Form數(shù)據(jù)的有效性。ActionForm是一個符合JavaBean規(guī)范的類,所有的屬性都應與get和set對應。對于一些復雜的系統(tǒng),還可以采用DynaActionForm來構(gòu)造動態(tài)的Form,即通過預制參數(shù)來生成Form,這樣可以更靈活地擴展程序。ActionForm.java的示例代碼如下:
//******* ActionForm.java**************
package org.apache.struts.action;
import org.apache.struts.upload.MultipartRequestHandler;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import java.io.Serializable;
/** 該類用于數(shù)據(jù)表單的封裝 */
public abstract class ActionForm implements Serializable {
/** 定義一個ActionServlet */
protected transient ActionServlet servlet = null;
/** 定義MultipartRequestHandler */
protected transient MultipartRequestHandler multipartRequestHandler;
/** 獲取ActionServlet */
protected ActionServlet getServlet() {
return (this.servlet);
}
/** 獲取ServletWrapper*/
public ActionServletWrapper getServletWrapper() {
return new ActionServletWrapper(getServlet());
}
/** 返回MultipartRequestHandler */
public MultipartRequestHandler getMultipartRequestHandler() {
return multipartRequestHandler;
}
/** 設定 ActionServlet */
public void setServlet(ActionServlet servlet) {
this.servlet = servlet;
}
/** 設定MultipartRequestHandler */
public void setMultipartRequestHandler(
MultipartRequestHandler multipartRequestHandler) {
this.multipartRequestHandler = multipartRequestHandler;
}
/** 重置 */
public void reset(ActionMapping mapping, ServletRequest request) {
try {
//將Bean的屬性值置為空
reset(mapping, (HttpServletRequest) request);
} catch (ClassCastException e) {
;
}
}
/** 重置 */
public void reset(ActionMapping mapping, HttpServletRequest request) {
//要繼承類實現(xiàn)該方法
}
/** 對數(shù)據(jù)進行驗證 */
public ActionErrors validate(ActionMapping mapping, ServletRequest request) {
try {
//驗證數(shù)據(jù)是否符合定義
return (validate(mapping, (HttpServletRequest) request));
} catch (ClassCastException e) {
return (null);
}
}
/** 驗證數(shù)據(jù),要繼承類實現(xiàn) */
public ActionErrors validate(ActionMapping mapping,
HttpServletRequest request) {
return (null);
}
}
從代碼中可以看出,開發(fā)人員應該在自己的Bean里覆蓋validate方法,并在配置文件里設置<action>元素的validate為true。在ActionServlet調(diào)用Action類前,它會調(diào)用validate(),如果返回的ActionErrors不是null,則ActinForm會根據(jù)錯誤關(guān)鍵字將ActionErrors存儲在請求屬性列表中。
如果返回的不是null,而且長度大于0,則根據(jù)錯誤關(guān)鍵字將實例存儲在請求的屬性列表中,然后ActionServlet將響應轉(zhuǎn)發(fā)到配置文件<action>元素的input屬性所指向的目標。如果需要執(zhí)行特定的數(shù)據(jù)有效性檢查,最好在Action類中進行這個操作,而不是在ActionForm類中進行。
典型的ActionFrom Bean只有屬性的設置與讀取方法,而沒有實現(xiàn)事務邏輯的方法,只有簡單的輸入檢查邏輯,使用的目的是為了存儲用戶在相關(guān)表單中輸入的最新數(shù)據(jù),以便可以將同一網(wǎng)頁進行再生,同時提供一組錯誤信息,這樣就可以讓用戶修改不正確的輸入數(shù)據(jù)。而真正對數(shù)據(jù)有效性進行檢查的是Action類或適當?shù)氖聞者壿婤ean。
示例的具體實現(xiàn)思路是:在前面已經(jīng)建立的myLogin工程的基礎上進行開發(fā),web.xml還采用原來的配置不變。首先編寫提交數(shù)據(jù)的頁面和輸出提交內(nèi)容的頁面,再編寫一個用來存放提交內(nèi)容的Bean,然后編寫繼承ActionFrom的控制器,再編寫Struts的配置文檔,最后啟動Tomcat,運行示例即可。具體步驟如下:
編寫提交數(shù)據(jù)的頁面submit.jsp,該頁面主要用來提交用戶輸入的內(nèi)容。為了演示方便,這里只提交一個內(nèi)容,該頁面放在myLogin/jsp目錄下,submit.jsp的示例代碼如下:
<%@page contentType="text/html;charset=GBK"%>
<html>
<head><title>練習使用ActionFrom</title></head>
<body>
<form name="HelloWorld" action="/myLogin/submit.do" method="post">
請輸入要提交的內(nèi)容:<input type="text" name="helloWorld" value=""/><br>
<input type="submit" value="提交"/>
</form>
</body>
</html>
編寫輸出提交內(nèi)容的頁面helloWorld.jsp,該頁面主要用來顯示用戶提交的內(nèi)容。為了演示方便,這里只是簡單演示一下helloWorld.jsp,該頁面放在myLogin/jsp目錄下,helloWorld.jsp的示例代碼如下:
<%@page contentType="text/html;charset=GBK"%>
<html>
<head><title>利用Struts輸出HelloWorld</title></head>
<%
String str = (String)request.getAttribute("helloWorld");
%>
<body>
<font size='22'><%=str%></font>
</body>
</html>
編寫一個用來存放提交內(nèi)容的類HelloWorld,Struts就是通過它來進行數(shù)據(jù)的動態(tài)綁定的。首先新建一個包com.myLogin.bean,將該類放在此包下,HelloWorld.java的示例代碼如下:
//******* HelloWorld.java**************
package com.myLogin.bean;
import org.apache.struts.action.ActionForm;
public class HelloWorld extends ActionForm {
//定義變量helloWorld
private String helloWorld = null;
//設定helloWorld
public void setHelloWorld(String helloWorld) {
this.helloWorld = helloWorld;
}
//獲得helloWorld
public String getHelloWorld() {
return this.helloWorld;
}
}
編寫繼承Action的控制器HelloWorldAction,該控制器實現(xiàn)execute方法,該類放在com.myLogin.action包下,HelloWorldAction的示例代碼如下:
//******* HelloWorldAction.java**************
package com.myLogin.action;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import com.myLogin.bean.HelloWorld;
public class HelloWorldAction extends Action{
public ActionForward execute(ActionMapping mapping, ActionForm form, Http ServletRequest request, HttpServletResponse response) throws Exception {
//將頁面提交的進行轉(zhuǎn)換成form
HelloWorld helloWorld = (HelloWorld)form;
//將獲取的頁面內(nèi)容注入request
request.setAttribute("helloWorld", helloWorld.getHelloWorld());
//返回到helloWorld.jsp頁面
return mapping.findForward("helloWorld");
}
}
編寫Struts的配置文檔struts-config.xml,該文件放在WEB-INF目錄下,struts-config.xml的示例代碼如下:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.2//EN" "http://jakarta.apache.org/struts/dtds/struts-config_1_2.dtd">
<struts-config>
<!--定義formbean-->
<form-beans>
<form-bean name="helloWorld" type="com.myLogin.bean.HelloWorld"/>
</form-beans>
<action-mappings>
<!--定義提交時訪問的路徑-->
<action path="/submit" type="com.myLogin.action.HelloWorldAction" name="helloWorld">
<forward name="helloWorld" path="/jsp/helloWorld.jsp"/>
</action>
<!--定義初次訪問時的路徑-->
<action path="/input"
type="org.apache.struts.actions.ForwardAction"
parameter="/jsp/submit.jsp"/>
</action-mappings>
</struts-config>
建立web.xml,該文件放在WEB-INF目錄下,web.xml的示例代碼如下:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4"
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">
<servlet>
<servlet-name>actionServlet</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<!--初始參數(shù)-->
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!--處理所有后綴為do的請求-->
<servlet-mapping>
<servlet-name>actionServlet</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app>
啟動Tomcat,運行示例。
在瀏覽器輸入http://localhost:8080/myLogin/input.do,瀏覽器即出現(xiàn)提示用戶輸入內(nèi)容的畫面,如圖11.17所示。
在瀏覽器輸入“ggggg”,然后單擊“提交”按鈕,即可看到在輸出頁面輸出了剛才輸入的信息“ggggg”,如圖11.18所示。

圖11.17 提示用戶輸入內(nèi)容的畫面 圖11.18 輸出了剛才輸入的信息“ggggg”
通過ActionForm的示例,讀者可以看到,Struts通過ActionForm實現(xiàn)了對數(shù)據(jù)的封裝,這樣從頁面取值和將值返回頁面時就非常方便。
11.2.5 ActionErrors(錯誤處理)
ActionErrors是對錯誤信息的包裝,一旦在執(zhí)行action或者form.validate中出現(xiàn)異常,即可產(chǎn)生一個ActionError并最終加入到ActionErrors。在Form驗證的過程中,如果有Error發(fā)生,則會將頁面重新導向至輸入頁,并提示錯誤。ActionErrors的源代碼如下:
//******* ActionErrors.java**************
package org.apache.struts.action;
import java.io.Serializable;
/** 該類對錯誤信息進行封裝 */
public class ActionErrors extends ActionMessages implements Serializable {
/** 構(gòu)造函數(shù) */
public ActionErrors() {
super();
}
/** 構(gòu)造函數(shù) */
public ActionErrors(ActionErrors messages) {
super(messages);
}
}
從上面的代碼中可以看出,該類繼承于ActionMessages,只是用來處理錯誤信息,所以關(guān)鍵還要看ActionMessages類,ActionMessages的源代碼如下:
//******* ActionMessages.java**************
package org.apache.struts.action;
import java.io.Serializable;
/** 該類對信息進行封裝和序列化 */
public class ActionMessage implements Serializable {
/** 定義消息的關(guān)鍵字 */
protected String key = null;
/** 定義消息的值 */
protected Object[] values = null;
/** 定義resource */
protected boolean resource = true;
/** 構(gòu)造函數(shù) */
public ActionMessage(String key) {
this(key, null);
}
/** 構(gòu)造函數(shù) */
public ActionMessage(String key, Object value0) {
this(key, new Object[] { value0 });
}
/** 構(gòu)造函數(shù) */
public ActionMessage(String key, Object value0, Object value1) {
this(key, new Object[] { value0, value1 });
}
/** 構(gòu)造函數(shù) */
public ActionMessage(String key, Object value0, Object value1, Object value2) {
this(key, new Object[] { value0, value1, value2 });
}
/** 構(gòu)造函數(shù) */
public ActionMessage(String key, Object value0, Object value1,
Object value2, Object value3) {
this(key, new Object[] { value0, value1, value2, value3 });
}
/** 構(gòu)造函數(shù) */
public ActionMessage(String key, Object[] values) {
this.key = key;
this.values = values;
this.resource = true;
}
/** 構(gòu)造函數(shù) */
public ActionMessage(String key, boolean resource) {
this.key = key;
this.resource = resource;
}
/** 獲得消息的關(guān)鍵字 */
public String getKey() {
return (this.key);
}
/** 獲得消息的值 */
public Object[] getValues() {
return (this.values);
}
/** 獲取Resource */
public boolean isResource() {
return (this.resource);
}
}
可以看出這里的ActionMessages類,類似于Java里面的Map,有關(guān)鍵字和值,只是這里進行了封裝,這和Spring的Model比較類似。
11.2.6 DispatchAction(多動作控制器)
前面講Action時,曾經(jīng)講過所有繼承它的類都要實現(xiàn)execute方法,然后在此方法里進行相應的邏輯處理。它的局限性是頁面中只有一個按鈕,如果有多個就沒有辦法,因為每個按鈕提交后,都要執(zhí)行execute方法??赡茏x者就會有疑問了,如果頁面中有多個按鈕,該怎么操作呢?Struts提供的DispatchAction就可以實現(xiàn)這樣的功能,先來看一下DispatchAction的源代碼,看它是如何實現(xiàn)這樣的操作的,DispatchAction的源代碼如下所示:
//******* DispatchAction.java**************
package org.apache.struts.actions;
//引入日志類
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
//引入servlet
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
//引入反射包
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
/** 該類用來實現(xiàn)多動作處理,繼承于BaseAction */
public abstract class DispatchAction extends BaseAction {
/** 定義日志 */
protected static Log log = LogFactory.getLog(DispatchAction.class);
/** 定義類的實例 */
protected Class clazz = this.getClass();
/** 定義多個方法的存儲器 */
protected HashMap methods = new HashMap();
/** 定義類型 */
protected Class[] types =
{
ActionMapping.class, ActionForm.class, HttpServletRequest.class,
HttpServletResponse.class
};
/** 定義execute方法 */
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response)
throws Exception {
//如果提交的是取消按鈕
if (isCancelled(request)) {
ActionForward af = cancelled(mapping, form, request, response);
//返回ActionForward
if (af != null) {
return af;
}
}
//獲取根據(jù)request獲取的參數(shù)
String parameter = getParameter(mapping, form, request, response);
//獲取方法的名稱
String name =
getMethodName(mapping, form, request, response, parameter);
// 判斷方法名是否為execute或perform
if ("execute".equals(name) || "perform".equals(name)) {
String message =messages.getMessage("dispatch.recursive", mapping.getPath());
//如果是,報錯
log.error(message);
throw new ServletException(message);
}
// 反射調(diào)用該方法
return dispatchMethod(mapping, form, request, response, name);
}
/** 未指定方法名時調(diào)用的方法 */
protected ActionForward unspecified(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response)
throws Exception {
String message =messages.getMessage("dispatch.parameter", mapping.getPath(), mapping.getParameter());
//如果未指定方法名,則報錯
log.error(message);
//拋出異常
throw new ServletException(message);
}
/** 當提交的是取消按鈕時執(zhí)行的方法 */
protected ActionForward cancelled(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response)
throws Exception {
return null;
}
/** 反射調(diào)用方法 */
protected ActionForward dispatchMethod(ActionMapping mapping,
ActionForm form, HttpServletRequest request,
HttpServletResponse response, String name)
throws Exception {
//如果沒有指定方法名,則調(diào)用unspecified
if (name == null) {
return this.unspecified(mapping, form, request, response);
}
//指定被調(diào)用的方法
Method method = null;
try {
//根據(jù)方法名獲取該方法
method = getMethod(name);
} catch (NoSuchMethodException e) {
String message =messages.getMessage("dispatch.method", mapping.getPath(), name);
//記錄錯誤
log.error(message, e);
//添加錯誤信息
String userMsg =messages.getMessage("dispatch.method.user", mapping.getPath());
throw new NoSuchMethodException(userMsg);
}
ActionForward forward = null;
//定義參數(shù)
try {
Object[] args = { mapping, form, request, response };
//反射調(diào)用指定的方法
forward = (ActionForward) method.invoke(this, args);
} catch (ClassCastException e) {
String message =messages.getMessage("dispatch.return", mapping.getPath(), name);
//記錄錯誤
log.error(message, e);
throw e;
} catch (IllegalAccessException e) {
String message =messages.getMessage("dispatch.error", mapping.getPath(), name);
//記錄錯誤
log.error(message, e);
throw e;
} catch (InvocationTargetException e) {
Throwable t = e.getTargetException();
//如果拋出的異常是Exception的實例
if (t instanceof Exception) {
throw ((Exception) t);
} else {
String message =messages.getMessage("dispatch.error", mapping.getPath(),
name);
//記錄錯誤
log.error(message, e);
throw new ServletException(t);
}
}
// 返回ActionForward 的實例
return (forward);
}
/**返回參數(shù)*/
protected String getParameter(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response)
throws Exception {
// 從映射中獲取參數(shù)
String parameter = mapping.getParameter();
//如果參數(shù)為空
if (parameter == null) {
String message =messages.getMessage("dispatch.handler", mapping.getPath());
//記錄錯誤
log.error(message);
//拋出異常
throw new ServletException(message);
}
//返回參數(shù)
return parameter;
}
/** 根據(jù)方法名獲取方法 */
protected Method getMethod(String name)
throws NoSuchMethodException {
synchronized (methods) {
Method method = (Method) methods.get(name);
//如果方法名為空
if (method == null) {
method = clazz.getMethod(name, types);
methods.put(name, method);
}
//返回指定的方法
return (method);
}
}
/** 根據(jù)參數(shù)值獲取方法名 */
protected String getMethodName(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response,
String parameter) throws Exception {
// 獲取方法名
return request.getParameter(parameter);
}
}
前面了解了多動作的實現(xiàn)機理,下面將通過具體的示例來深入講解實現(xiàn)方式。具體實現(xiàn)思路是:仍然在前面myLogin工程的繼承上進行,首先編寫多動作提交的頁面,信息輸出的頁面仍然采用前面的輸出頁面,然后編寫控制器,繼承DispatchAction,再編寫配置文件struts-config.xml,最后啟動Tomcat,運行示例,具體步驟如下:
編寫JSP頁面multiActionSubmit.jsp,該頁面有多個提交動作,用來演示多動作提交的處理方式,該頁面放在myLogin/jsp目錄下,multiActionSubmit.jsp的示例代碼如下:
<%@page contentType="text/html;charset=GBK"%>
<html>
<head><title>練習使用DispatchAction</title></head>
<body>
<form name="HelloWorld" action="/myLogin/helloWorldMultiAction.do" method="post">
請輸入要提交的內(nèi)容:<input type="text" name="helloWorld" value=""/><br>
<input type="submit" name="method" value="insert"/>
<input type="submit" name="method" value="update"/>
<input type="submit" name="method" value="delete"/>
</form>
</body>
</html>
仍然使用前面輸出信息的頁面helloWorld.jsp,helloWorld.jsp的示例代碼如下:
<%@page contentType="text/html;charset=GBK"%>
<html>
<head><title>利用Struts輸出HelloWorld</title></head>
<%
String str = (String)request.getAttribute("helloWorld");
%>
<body>
<font size='22'><%=str%></font>
</body>
</html>
編寫控制器HelloWorldMultiAction,繼承DispatchAction,HelloWorldMultiAction.java的示例代碼如下:
//******* HelloWorldMultiAction.java**************
package com.myLogin.action;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.actions.DispatchAction;
import com.myLogin.bean.HelloWorld;
public class HelloWorldMultiAction extends DispatchAction {
//新增動作
public ActionForward insert(ActionMapping mapping, ActionForm form, HttpServlet Request request, HttpServletResponse response) throws Exception {
HelloWorld helloWorld = (HelloWorld)form;
request.setAttribute("helloWorld", "您執(zhí)行的是新增動作:" + helloWorld. getHelloWorld());
return mapping.findForward("helloWorld");
}
//修改動作
public ActionForward update(ActionMapping mapping, ActionForm form, HttpServlet Requestrequest, HttpServletResponse response) throws Exception {
HelloWorld helloWorld = (HelloWorld)form;
request.setAttribute("helloWorld", "您執(zhí)行的是修改動作:" + helloWorld. getHelloWorld());
return mapping.findForward("helloWorld");
}
//刪除動作
public ActionForward delete(ActionMapping mapping, ActionForm form, HttpServlet Requestrequest, HttpServletResponse response) throws Exception {
HelloWorld helloWorld = (HelloWorld)form;
request.setAttribute("helloWorld", "您執(zhí)行的是刪除動作:" + helloWorld. getHelloWorld());
return mapping.findForward("helloWorld");
}
}
修改配置文檔struts-config.xml,示例代碼如下:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.2//EN" "http://jakarta.apache.org/struts/dtds/struts-config_1_2.dtd">
<struts-config>
<!--定義formbean-->
<form-beans>
<form-bean name="helloWorld" type="com.myLogin.bean.HelloWorld"/>
</form-beans>
<action-mappings>
<!--定義提交時訪問的路徑-->
<action path="/submit" type="com.myLogin.action.HelloWorldAction" name= "helloWorld">
<forward name="helloWorld" path="/jsp/helloWorld.jsp"/>
</action>
<!--定義提交時訪問的路徑-->
<action path="/helloWorldMultiAction" type="com.myLogin.action.HelloWorld MultiAction" name="helloWorld" parameter="method">
<forward name="helloWorld" path="/jsp/helloWorld.jsp"/>
</action>
<!--定義初次訪問時的路徑-->
<action path="/input"
type="org.apache.struts.actions.ForwardAction"
parameter="/jsp/submit.jsp"/>
<!--定義初次訪問多動作時的路徑-->
<action path="/inputTemp"
type="org.apache.struts.actions.ForwardAction"
parameter="/jsp/multiActionSubmit.jsp"/>
</action-mappings>
</struts-config>
啟動Tomcat,在瀏覽器輸入http://localhost:8080/myLogin/inputTemp.do,按Enter鍵后,瀏覽器即可出現(xiàn)提示用戶輸入的畫面,如圖11.19所示。
輸入“dddd”,然后單擊“insert”按鈕,即可轉(zhuǎn)入輸出新增的畫面,如圖11.20所示。

圖11.19 提示用戶輸入的畫面 圖11.20 輸出新增的畫面
單擊“update”按鈕,即可轉(zhuǎn)入輸出修改的畫面,如圖11.21所示。
單擊“delete”按鈕,即可轉(zhuǎn)入輸出刪除的畫面,如圖11.22所示。

圖11.21 輸出修改的畫面 圖11.22 輸出刪除的畫面
11.3 利用Struts實現(xiàn)用戶登錄的示例
前面對Struts的核心類進行了講解,下面將講解一個利用Struts實現(xiàn)用戶登錄的示例,使讀者對Struts的運行機理有更深的了解。
實現(xiàn)思路是:本示例仍然在前面myLogin工程的基礎上進行,首先新建一個登錄頁面,增加提交按鈕,接著新建一個ActionForm為User.java類,然后新建一個登錄的控制器,接著修改配置文檔,最后驗證上述功能。
11.3.1 編寫實現(xiàn)登錄的頁面login.jsp
新建一個登錄頁面login.jsp,增加提交按鈕,放在myLogin"jsp目錄下,login.jsp的示例代碼如下:
<%@page contentType="text/html;charset=GBK"%>
<html>
<head><title>實現(xiàn)用戶登錄的Struts實例</title></head>
<body>
<font size=’22’> "${msg}"<br> </font>
<form name="form1" action="/myLogin/login.do" method="post">
用戶名:<input type="text" name="username" value="${user.username}"/><br>
密碼:<input type="password" name="password" value="${user.password}"/><br>
<input type="submit" name=”method” value="提交"/>
</form>
</body>
</html>
11.3.2 編寫存儲登錄用戶信息的類User.java
在com.myLogin.bean包上,新建一個存儲登錄用戶信息的類User.java,該類繼承ActionForm,User.java的示例代碼如下:
//******* User.java**************
package com.myLogin.bean;
import org.apache.struts.action.ActionForm;
//該類繼承ActionForm
public class User extends ActionForm {
//定義用戶名
private String username = null;
//定義密碼
private String password = null;
//設定用戶名
public void setUsername(String username) {
this.username = username;
}
//獲取用戶名
public String getUsername() {
return this.username;
}
//設定密碼
public void setPassword(String password) {
this.password = password;
}
//獲取密碼
public String getPassword() {
return this.password;
}
}
11.3.3 編寫控制器LoginAction.java
在com.myLogin.action包上,新建一個控制器LoginAction.java,該類繼承Action,LoginAction.java的示例代碼如下:
//******* LoginAction.java**************
package com.myLogin.action;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.*;
import com.myLogin.bean.*;
public class LoginAction extends Action {
public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
String forwardJsp = "login";
//將頁面提交的內(nèi)容進行封裝
String username = ((User) form).getUsername();
String password = ((User) form).getPassword();
if (username == null && password == null) {
request.setAttribute("msg", "請輸入用戶名和密碼");
} else if ("".equals(username) || "".equals(password)) {
request.setAttribute("msg", "必須輸入用戶名或密碼");
} else if ("gf2008".equals(username) && "888888".equals(password)) {
request.setAttribute("msg", "登錄成功");
} else if (!"gf2008".equals(username)) {
request.setAttribute("msg", "用戶名填寫錯誤");
}
request.setAttribute("user",(User) form);
//返回到登錄畫面,顯示相關(guān)信息
return mapping.findForward(“login”);
}
}
11.3.4 配置Struts文檔struts-config.xml
修改前面的配置文檔struts-config.xml,修改后的示例代碼如下:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.2//EN" "http://jakarta.apache.org/struts/dtds/struts-config_1_2.dtd">
<struts-config>
<!--定義formbean-->
<form-beans>
<form-bean name="helloWorld" type="com.myLogin.bean.HelloWorld"/>
<form-bean name="user" type="com.myLogin.bean.User"/>
</form-beans>
<action-mappings>
<!--定義提交時訪問的路徑-->
<action path="/submit" type="com.myLogin.action.HelloWorldAction" name= "helloWorld">
<forward name="helloWorld" path="/jsp/helloWorld.jsp"/>
</action>
<!--定義提交時訪問的路徑-->
<action path="/helloWorldMultiAction" type="com.myLogin.action.HelloWorld MultiAction" name="helloWorld" parameter="method">
<forward name="helloWorld" path="/jsp/helloWorld.jsp"/>
</action>
<!--定義登錄時訪問的路徑-->
<action path="/login" type="com.myLogin.action.LoginAction" name="user">
<forward name="login" path="/jsp/login.jsp"/>
</action>
<!--定義初次訪問時的路徑-->
<action path="/input"
type="org.apache.struts.actions.ForwardAction"
parameter="/jsp/submit.jsp"/>
<!--定義初次訪問多動作時的路徑-->
<action path="/inputTemp"
type="org.apache.struts.actions.ForwardAction"
parameter="/jsp/multiActionSubmit.jsp"/>
<!--定義初次訪問登錄時的路徑-->
<action path="/loginTemp"
type="org.apache.struts.actions.ForwardAction"
parameter="/jsp/login.jsp"/>
</action-mappings>
</struts-config>
11.3.5 配置web.xml
仍然使用前面的配置文檔web.xml,示例代碼如下:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4"
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">
<servlet>
<!--定義映射關(guān)系-->
<servlet-name>actionServlet</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<!--定義初始化參數(shù)-->
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!--定義訪問時的后綴,所有為do的都被截取-->
<servlet-mapping>
<servlet-name>actionServlet</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app>
11.3.6 啟動Tomcat運行示例
具體步驟如下:
啟動Tomcat,在瀏覽器的地址欄中輸入http://localhost:8080/myLogin/loginTemp.do,按Enter鍵即可看到登錄畫面,如圖11.23所示。
如果用戶名輸入錯誤(這里輸入gf3008),即可看到提示用戶名填寫錯誤的畫面,如圖11.24所示。

圖11.23 登錄畫面 圖11.24 提示用戶名填寫錯誤的畫面
輸入用戶名“gf2008”,密碼“888888”,即可看到登錄成功的畫面,如圖11.25所示。

圖11.25 登錄成功的畫面