JAVA—咖啡館

          ——歡迎訪問rogerfan的博客,常來《JAVA——咖啡館》坐坐,喝杯濃香的咖啡,彼此探討一下JAVA技術,交流工作經驗,分享JAVA帶來的快樂!本網站部分轉載文章,如果有版權問題請與我聯系。

          BlogJava 首頁 新隨筆 聯系 聚合 管理
            447 Posts :: 145 Stories :: 368 Comments :: 0 Trackbacks

          11.1  Struts介紹

          11.1.1  Struts簡介

          Struts是一個基于Sun J2EE平臺的MVC框架,很好地實現了MVC模式,它由Craig McClanahan創建。Struts最早是作為Apache Jakarta項目的組成部分問世運作,Struts這個名字來源于在建筑中使用的金屬架。使用它的目的是為了減少運用MVC設計模型來開發Web應用的時間。它只有一個中心控制器,采用XML定制轉向的URL,采用Action來處理邏輯。

          Struts通過一個配置文件,即可把握整個系統各部分之間的聯系,但這樣做不容易查找錯誤。Struts 其實就是在Model2基礎上實現的一個MVC框架。Model2的示意圖如圖11.1所示。

          圖11.1  Model2的示意圖

          與Spring一樣,通過在web.xml中的配置,使得所有的視圖層請求都要通過ActionServlet,由它進行客戶端的請求處理。它主要通過struts-config.xml文件來進行用戶請求的動作和對應Action的請求,將請求傳遞給Action,并將處理后的結果返回給視圖層。Struts的體系結構如圖11.2所示。

          圖11.2  Struts的體系結構

          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的相關知識,當然最重要的還是練習,這里從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的相關版本。單擊“struts-1.3.8-all.zip”超鏈接,即可進行下載,大小約44MB。

          11.1.4  配置Struts

          解壓縮struts-1.3.8-all.zip,解壓縮完畢后,就可以在Eclipse中配置Struts了。具體實現思路如下:首先在Eclipse中建立一個項目myLogin,然后把Struts相關的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”節點,然后單擊“Next”按鈕,彈出“New Tomcat Project”對話框,如圖11.6所示。

          圖11.6  “New Tomcat Project”對話框

          *  在“Project name”文本框中輸入“myLogin”,然后單擊“Finish”按鈕,項目即建立成功,myLogin的目錄結構如圖11.7所示。

            在myLogin上單擊右鍵,選擇菜單“New”|“Package”命令,彈出“New Java Package”對話框,如圖11.8所示。

                  

          圖11.7  myLogin的目錄結構                    圖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”節點,系統將在“Properties for myLogin”對話框的右邊出現“Java Build Path”的相關屬性,如圖11.11所示。

            在“Libraries”選項卡中,單擊“Add JARs…”按鈕,彈出“JAR Selection”對話框,如圖11.12所示。

             

          圖11.11 “Java Build Path”的相關屬性               圖11.12  “JAR Selection”對話框

            在“JAR Selection”對話框中,單擊“myLogin”節點,打開樹形菜單,如圖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項目的目錄結構如圖11.15所示。

          圖11.15  配置好Struts的myLogin項目的目錄結構

          上述建立myLogin工程的步驟很長,目的是讓讀者不要在這上面耗費太多的時間,只要讀者按照上面介紹的步驟一步一步地來,在環境配置方面就不會有太多的問題。

          11.2  Struts的核心

          支持Struts的核心類主要包括:ActionServlet,Action,ActionForm,Action Mapping等,它們的運行機理如圖11.16所示。

          圖11.16  Struts核心類的運行機理

          從圖11-6可以看出這些核心類的運作模式,下面分別進行講解。

          11.2.1  ActionServlet(分發器)

          org.apache.struts.action.ActionServlet繼承于javax.servlet.http.HttpServlet,ActionServlet是MVC模式中的Controller,是一個FrontController。它是一個標準的Servlet,它將request轉發給RequestProcessor來處理,執行該方法后會返回一個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的有關配置信息的版本 */

              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"

                  };

              /** 映射描述 */

          圓角矩形: 下面一段代碼定義了Struts在調用ActionServlet后,要銷毀的一些參數。

    protected String servletMapping = null; // :FIXME:                                                              - multiples?

              /** 定義Servlet的名稱 */

              protected String servletName = null;

              /** 覆寫destroy 方法,用來處理一些銷毀動作 */

              public void destroy() {

                  if (log.isDebugEnabled()) {

                      log.debug(internal.getMessage("finalizing"));

                  }

                  //銷毀模組

                  destroyModules();

                  //銷毀相關屬性

                  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();

                  //清空相關的屬性

                  PropertyUtils.clearDescriptors();

          }

          ……

          上面的代碼主要是覆寫了Servlet的destroy()方法,用來進行一些銷毀的動作,如釋放相關的模組、日志等,接下來就要進行一些初始化的工作,代碼如下所示:

          圓角矩形: 下面一段代碼定義了Struts在調用業務邏輯前要初始化的一些參數。

……

          /**處理一些初始化動作*/

          public void init() throws ServletException {

              final String configPrefix = "config/";

              //獲取配置文檔的前綴為config的長度

              final int configPrefixLength = configPrefix.length() - 1;

              try {

                  initInternal();

                  initOther();

                  //初始化servlet

                  initServlet();

                  initChain();

                  //設定全局的servlet關鍵字

                  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();

                  //獲取初始化的參數名

                  Enumeration names = getServletConfig().getInitParameterNames();

                  while (names.hasMoreElements()) {

                      String name = (String) names.nextElement();

                      //假如獲取的名稱前綴不是config,則繼續獲取

                      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();

              //獲取相關的屬性名

              Enumeration names = context.getAttributeNames();

              //循環迭代屬性名

              while (names.hasMoreElements()) {

                  String name = (String) names.nextElement();

                  //如果前綴不是定義的全局關鍵字,則繼續

                  if (!name.startsWith(Globals.MODULE_KEY)) {

                      continue;

                  }

                  //獲取前綴

                  String prefix = name.substring(Globals.MODULE_KEY.length());

                  //如果有前綴,則加入到list中

                  if (prefix.length() > 0) {

                      prefixList.add(prefix);

                  }

              }

              //將list轉換為數組

              String[] prefixes =

                  (String[]) prefixList.toArray(new String[prefixList.size()]);

              //設定相關屬性

              context.setAttribute(Globals.MODULE_PREFIXES_KEY, prefixes);

          }

          ……

          上述代碼主要是進行初始化工作,如初始化Servlet、FormBean、異常、日志、模組等。為了轉發從視圖層傳來的請求,還必須覆寫doGet和doPost方法,然后這兩個方法同時調用了process方法,代碼如下所示:

          ……

          /** 覆寫doGet方法,接受用戶在頁面的請求,調用process */

          public void doGet(HttpServletRequest request, HttpServletResponse response)

              throws IOException, ServletException {

              process(request, response);

          }

          ** 覆寫doPost方法,接受用戶在頁面的請求,調用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

          圓角矩形: 下面一段代碼定義了Struts要銷毀的模組。        this.servletMapping = urlPattern;

              }

          }

          /** 獲取消息資源 */

          public MessageResources getInternal() {

              return (this.internal);

          }

          /** 銷毀模組 */

          protected void destroyModules() {

              ArrayList values = new ArrayList();

              Enumeration names = getServletContext().getAttributeNames();

              //對名稱進行循環,放在list中

              while (names.hasMoreElements()) {

                  values.add(names.nextElement());

              }

              //將list轉換為Iterator

              Iterator keys = values.iterator();

              //進行迭代

              while (keys.hasNext()) {

                  String name = (String) keys.next();

                  Object value = getServletContext().getAttribute(name);

                  //判斷獲取的名稱是否是ModuleConfig的實例

                  if (!(value instanceof ModuleConfig)) {

                      continue;

                  }

                  //如果是,則轉換為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的關鍵字

                      getServletContext().removeAttribute(Globals.PLUG_INS_KEY

                          + config.getPrefix());

                  }

              }

          }

          /** 釋放configDigester */

          protected void destroyConfigDigester() {

              configDigester = null;

          }

          /** 釋放MessageResources */

          protected void destroyInternal() {

              internal = null;

          }

          /** 根據傳入的request,獲取ModuleConfig */

          protected ModuleConfig getModuleConfig(HttpServletRequest request) {

              ModuleConfig config = (ModuleConfig) request.getAttribute(Globals.MODULE_KEY);

              //如果為空,則獲取ServletContext中的內容

              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);

              //假如沒有,則創建一個新的RequestProcessor

              if (processor == null) {

                  try {

                      //創建一個新的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);

                  //設定關鍵字

                  String key = Globals.REQUEST_PROCESSOR_KEY + config.getPrefix();

          圓角矩形: 下面一段代碼實現了初始化模組配置的功能。        //將關鍵字加入到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;

          }

          //……相關的異常處理配置和其他初始化的配置信息,這里不再進行講解

          /** 初始化servlet */

          protected void initServlet()

              throws ServletException {

              //獲取ervlet name

              this.servletName = getServletConfig().getServletName();

              // 準備一個Digester

              Digester digester = new Digester();

              //設定Digester的相關屬性

              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());

                  }

              }

              //配置相關的配置信息

              digester.addCallMethod("web-app/servlet-mapping", "addServletMapping", 2);

              digester.addCallParam("web-app/servlet-mapping/servlet-name", 0);

          圓角矩形: 下面一段代碼實現了解析web. xml的功能。    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 {

                      //關閉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);

              }

          }

          ……

          通過前面的初始化工作及視圖層的請求轉發,Struts即可進入process方法,從而開始具體的請求轉發工作,代碼如下所示:

              ……

               /** 具體負責用戶請求的進程 */

              protected void process(HttpServletRequest request,

                  HttpServletResponse response)

                  throws IOException, ServletException {

                  ModuleUtils.getInstance().selectModule(request, getServletContext());

                  //根據request獲取ModuleConfig

                  ModuleConfig config = getModuleConfig(request);

                  //根據ModuleConfig獲取RequestProcessor

                  RequestProcessor processor = getProcessorForModule(config);

                  //如果RequestProcessor為空,則根據request獲取

                  if (processor == null) {

                      processor = getRequestProcessor(config);

                  }

                  //執行相關的處理進程

                  processor.process(request, response);

              }

          }

          從示例代碼可以看出,在ActionServlet的doGet和doPost方法中,都調用了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();

                          //設定動作的內容為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;

          }

          ……

          上述代碼主要用來做初始化和銷毀工作,也就是說每次在調用DispatcherServlet時,首先要將存儲的action清空,然后再將傳來的DispatcherServlet和相關配置檔注入RequestProcessor中,接著就要調用RequestProcessor的process方法,代碼如下所示:

          ……

              /** 處理進程 */

              public void process(HttpServletRequest request, HttpServletResponse response)

                  throws IOException, ServletException {

                  //封裝request

                  request = processMultipart(request);

                  //獲取映射的路徑

                  String path = processPath(request, response);

                  //如果沒有路徑,則停止執行

                  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);

          圓角矩形: 下面一段代碼實現了在調用具體業務邏輯前,先對ActionForm進行驗證。

        //如果沒有映射,則返回

                  if (mapping == null) {

                      return;

                  }

                  //檢查要執行這個動作的角色

                  if (!processRoles(request, response, mapping)) {

                      return;

                  }

                  //將request和ActionForm關聯起來

                  ActionForm form = processActionForm(request, response, mapping);

                  //從request獲取ActionForm,進行封裝

                  processPopulate(request, response, form, mapping);

                  //驗證封裝的數據是否有問題

                  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;

                  }

                  //根據映射判斷是否包含了Forward

                  if (!processForward(request, response, mapping)) {

                      return;

                  }

                  //檢查是否包含Include

                  if (!processInclude(request, response, mapping)) {

                      return;

                  }

                  //創建一個動作實例

                  Action action = processActionCreate(request, response, mapping);

                  //如果創建的動作為空,則返回

                  if (action == null) {

                      return;

                  }

                  //執行這個動作,返回ActionForward

                  ActionForward forward =processActionPerform(request, response, action, form, mapping);

                  //將ActionForward放入Config中

                  processForwardConfig(request, response, forward);

              }

              /** 創建一個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);

                      }

                      // 如果實例為空,則重新創建一個

                      if (log.isTraceEnabled()) {

                          log.trace("  Creating new Action instance");

                      }

                      //創建一個action實例

                      try {

                          instance = (Action) RequestUtils.applicationInstance(className);

                      } catch (Exception e) {

                          log.error(getInternal().getMessage("actionCreate",

                                  mapping.getPath()), e);

                  //發送錯誤信息        response.sendError(HttpServletResponse.SC_INTERNAL_ SERVER_ERROR,

                              getInternal().getMessage("actionCreate", mapping.getPath()));

                          //返回空

                          return (null);

                      }

                      //將action的名稱和實例存放在actions容器中

                      actions.put(className, instance);

                  }

          圓角矩形: 下面一段代碼實現了將頁面的數據封裝成ActionForm。        //如果實例的servlet為空,則添加

                  if (instance.getServlet() == null) {

                      instance.setServlet(this.servlet);

                  }

                  //返回action的實例

                  return (instance);

              }

              /** 封裝ActionForm */

              protected ActionForm processActionForm(HttpServletRequest request,

                  HttpServletResponse response, ActionMapping mapping) {

                  //創建一個ActionForm

                  ActionForm instance =

                      RequestUtils.createActionForm(request, mapping, moduleConfig,

                          servlet);

                  //如果創建不成功,就返回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;

                      }

                      //執行response的sendRedirect方法

                      response.sendRedirect(response.encodeRedirectURL(uri));

                  } else {

                      //執行doForward,返回視圖

                      doForward(uri, request, response);

                  }

              }

              /** 執行Action */

              protected ActionForward processActionPerform(HttpServletRequest request,

                  HttpServletResponse response, Action action, ActionForm form,

                  ActionMapping mapping)

                  throws IOException, ServletException {

                  try {

                      //執行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();

          圓角矩形: 下面一段代碼描述了Struts如何處理異常的功能。        //如果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);

                      }

                  }

                  // 創建一個異常處理的實例

                  try {

                      ExceptionHandler handler = (ExceptionHandler) RequestUtils.applicationInstance (config.getHandler());

                      //執行實例的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

          圓角矩形: 下面一段代碼描述了Struts如何實現返回視圖層的功能。        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;

                  }

                  //建立關聯

                  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;

                  }

                  //建立關聯

                  internalModuleRelativeInclude(include, request, response);

                  //返回false

                  return (false);

              }

              /** 處理Locale */

              protected void processLocale(HttpServletRequest request,

                  HttpServletResponse response) {

                  //判斷配置里是否有Locale

                  if (!moduleConfig.getControllerConfig().getLocale()) {

                      return;

                  }

                  //如果已經有一個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 + "'");

          圓角矩形: 下面一段代碼描述了Struts如何實現映射的功能。            }

                      //加入到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);

          圓角矩形: 下面一段代碼描述了Struts如何實現獲取相對和絕對路徑的功能。

        //如果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());

                  //根據/和.進行截取

                  int slash = path.lastIndexOf("/");

                  int period = path.lastIndexOf(".");

                  //截取path

                  if ((period >= 0) && (period > slash)) {

                      path = path.substring(0, period);

                  }

                  //返回path

                  return (path);

          }

          ……

          上述代碼主要展示了process方法是如何進行轉發視圖層的請求的。下面主要展示process方法是如何處理模型層處理后要返回視圖層的動作的,代碼如下所示:

          ……

              //……其他的一些處理器,這里不再進行詳細說明

              /**根據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);

              }

              /** 根據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>

          代碼說明:

          — 這里的初始化參數<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)根據配置文件是否定義來創建或發現一個ActionForm bean實例,然后將請求過程移植到Bean。

          (4)調用Action實例的exectue方法,并將ActioForm,Action Mapping,Request和Response對象傳給它。

          (5)exectue返回一個ActionForward對象,然后返回視圖層。

          11.2.2  Action(控制器)

          如果要使用Struts,就離不開Action,但每個Action都只建立一個實例,所以Action不是線程安全的。Struts提供了多種Action供選擇使用。普通的Action只能通過調用execute執行一項任務,而DispatchAction可以根據配置參數執行,而不是僅進入execute()函數,這樣可以執行多種任務。LookupDispatchAction可以根據提交表單按鈕的名稱來執行函數,所有的控制器都繼承于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;

              }

              /** 接受頁面請求,然后轉入處理的方法 */

              public ActionForward execute(ActionMapping mapping, ActionForm form,

                  ServletRequest request, ServletResponse response)

                  throws Exception {

                  try {

                      //執行execute方法

                      return execute(mapping, form, (HttpServletRequest) request,

          圓角矩形: 下面一段代碼定義了要繼承Action,則必須實現execute方法。                (HttpServletResponse) response);

                  } catch (ClassCastException e) {

                      return null;

                  }

              }

              /** 接受頁面請求,然后轉入處理的方法,要繼承類來實現該方法 */

              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;

                  }

                  //獲取已經存在的消息類

                  ActionMessages requestMessages = (ActionMessages) request.getAttribute (Globals.MESSAGE_KEY);

                  //如果沒有,則創建一個新的

                  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;

                  }

                  //獲取已經存在的錯誤類

                  ActionMessages requestErrors =

                      (ActionMessages) request.getAttribute(Globals.ERROR_KEY);

                  //如果沒有,則創建一個新的

                  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);

              }

              /** 產生一個Token */

              protected String generateToken(HttpServletRequest request) {

                  return token.generateToken(request);

              }

              /** 獲取錯誤信息 */

              protected ActionMessages getErrors(HttpServletRequest request) {

                  ActionMessages errors = (ActionMessages) request.getAttribute(Globals.ERROR_KEY);

          圓角矩形: 下面一段代碼描述了要獲取的本地資源和消息。        //如果沒有,則創建一個新的

                  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);

                  //如果沒有,則創建一個新的消息

                  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的子類,實質上是對struts-config.xml的一個映射,從中可以取得所有的配置信息。

          ActionServelt將ActionMapping傳遞給Action,主要是通過配置文件來實現的,在Struts中,默認的配置文件是WEB-INF下的struts-config.xml。通過配置文件struts-config.xml可以定義全局轉發的方式、ActionMapping、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>    

              <action-mappings>

                  <!--請求的url對應于< action>中的path屬性-->

                  <action path="/helloWorld" type="com.gc.action.HelloWorldAction">

                      <!-- forward 表示執行完畢后返回的頁面-->

                       <forward name="index" path="/WEB-INF/jsp/index.jsp"/>

                  </action>

              </action-mappings>

          </struts-config>

          代碼說明如下:

          —  <action-mappings>用來描述ActionMapping,在<action-mappings>下的每一個<action>都是一個ActionMapping對象。當客戶端發出請求至ActionServlet時,請求的url對應于< action>中的path屬性,而要執行的Action則是type屬性所設定的內容。執行完Action后,返回的ActionForward則在< forward >中設定。

          — 上面配置文件的訪問路徑是:http://localhost:8080/myStruts/ helloWorld.do。

          (1)全局轉發的定義方式

          <?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表示全局轉發的名稱,path表示全局轉發的相對路徑-->   

              <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 表示執行完畢后返回的頁面-->

                       <forward name="index" path="/WEB-INF/jsp/index.jsp"/>

                  </action>

              </action-mappings>

          </struts-config>

          代碼說明:

          —  <global-forwards>中,forward的屬性name表示全局轉發的名稱,path表示全局轉發的相對路徑。

          —  forward除了name和path屬性外,還可以設定redirect屬性,如果redirect屬性設置為true,則ActionServlet會使用sendRedirect方法來轉發資源。

          (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 表示執行完畢后返回的頁面-->

                       <forward name="index" path="/WEB-INF/jsp/index.jsp"/>

                  </action>

                  <action path="/regedit" type="com.gc.action.RegeditAction">

                      <!-- forward 表示執行完畢后返回的頁面-->

                       <forward name="regedit" path="/WEB-INF/jsp/regedit.jsp"/>

                  </action>

              </action-mappings>

          </struts-config>

          代碼說明:

          —  <action-mappings>中的每一個<action>都對應一個ActionMapping對象。

          —  <action>除了path和type屬性外,還可以設定很多屬性:name屬性,用來表示與Action相關聯的ActionForm;scope屬性,用來表示ActionForm的作用域;prefix屬性,用來表示ActionForm的前綴;suffix屬性,用來表示ActionForm的后綴;input屬性,用來表示當ActionForm發生錯誤時,必須返回的表單路徑;unknown屬性,設為true時,所有沒有定義ActionMapping的操作都將轉到這里來;validate屬性,設為true時,表示在執行Action前,會調用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 表示執行完畢后返回的頁面-->

                  <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 表示執行完畢后返回的頁面-->

                       <forward name="index" path="/WEB-INF/jsp/index.jsp"/>

                  </action>

                  <action path="/regedit" type="com.gc.action.RegeditAction" name=”user”>

                      <!-- forward 表示執行完畢后返回的頁面-->

                       <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)定義數據源

          <?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 表示執行完畢后返回的頁面-->

                       <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="數據庫連接池"

                          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>中定義了與數據庫相關的屬性,因為相關屬性和其他數據庫類似,這里就不再解釋了。

          //******* 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轉換為數組

                  return ((String[]) results.toArray(new String[results.size()]));

              }

              /** 獲取InputForward */

              public ActionForward getInputForward() {

                  if (getModuleConfig().getControllerConfig().getInputForward()) {

                      return (findForward(getInput()));

                  } else {

                      //如果沒有,則創建一個新的

                      return (new ActionForward(getInput()));

                  }

              }

          }

          11.2.4  ActionForm(表單控制器)

          ActionForm使用了ViewHelper模式,是對HTML中Form的一個封裝,其中包含有validate方法,用于驗證Form數據的有效性。ActionForm是一個符合JavaBean規范的類,所有的屬性都應與get和set對應。對于一些復雜的系統,還可以采用DynaActionForm來構造動態的Form,即通過預制參數來生成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;

          /** 該類用于數據表單的封裝 */

          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) {

                  //要繼承類實現該方法

              }

              /** 對數據進行驗證 */

              public ActionErrors validate(ActionMapping mapping, ServletRequest request) {

                  try {

                      //驗證數據是否符合定義

                      return (validate(mapping, (HttpServletRequest) request));

                  } catch (ClassCastException e) {

                      return (null);

                  }

              }

              /** 驗證數據,要繼承類實現 */

              public ActionErrors validate(ActionMapping mapping,

                  HttpServletRequest request) {

                  return (null);

              }

          }

          從代碼中可以看出,開發人員應該在自己的Bean里覆蓋validate方法,并在配置文件里設置<action>元素的validate為true。在ActionServlet調用Action類前,它會調用validate(),如果返回的ActionErrors不是null,則ActinForm會根據錯誤關鍵字將ActionErrors存儲在請求屬性列表中。

          如果返回的不是null,而且長度大于0,則根據錯誤關鍵字將實例存儲在請求的屬性列表中,然后ActionServlet將響應轉發到配置文件<action>元素的input屬性所指向的目標。如果需要執行特定的數據有效性檢查,最好在Action類中進行這個操作,而不是在ActionForm類中進行。

          典型的ActionFrom Bean只有屬性的設置與讀取方法,而沒有實現事務邏輯的方法,只有簡單的輸入檢查邏輯,使用的目的是為了存儲用戶在相關表單中輸入的最新數據,以便可以將同一網頁進行再生,同時提供一組錯誤信息,這樣就可以讓用戶修改不正確的輸入數據。而真正對數據有效性進行檢查的是Action類或適當的事務邏輯Bean。

          示例的具體實現思路是:在前面已經建立的myLogin工程的基礎上進行開發,web.xml還采用原來的配置不變。首先編寫提交數據的頁面和輸出提交內容的頁面,再編寫一個用來存放提交內容的Bean,然后編寫繼承ActionFrom的控制器,再編寫Struts的配置文檔,最后啟動Tomcat,運行示例即可。具體步驟如下:

          *  編寫提交數據的頁面submit.jsp,該頁面主要用來提交用戶輸入的內容。為了演示方便,這里只提交一個內容,該頁面放在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">

                  請輸入要提交的內容:<input type="text" name="helloWorld" value=""/><br>

                  <input type="submit" value="提交"/>

              </form>

          </body>

          </html>

          *  編寫輸出提交內容的頁面helloWorld.jsp,該頁面主要用來顯示用戶提交的內容。為了演示方便,這里只是簡單演示一下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>

          *  編寫一個用來存放提交內容的類HelloWorld,Struts就是通過它來進行數據的動態綁定的。首先新建一個包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,該控制器實現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 {  

                  //將頁面提交的進行轉換成form

                  HelloWorld helloWorld = (HelloWorld)form;

                  //將獲取的頁面內容注入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>

                  <!--初始參數-->

                  <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,瀏覽器即出現提示用戶輸入內容的畫面,如圖11.17所示。

            在瀏覽器輸入“ggggg”,然后單擊“提交”按鈕,即可看到在輸出頁面輸出了剛才輸入的信息“ggggg”,如圖11.18所示。

                  

          圖11.17  提示用戶輸入內容的畫面                圖11.18  輸出了剛才輸入的信息“ggggg”

          通過ActionForm的示例,讀者可以看到,Struts通過ActionForm實現了對數據的封裝,這樣從頁面取值和將值返回頁面時就非常方便。

          11.2.5  ActionErrors(錯誤處理)

          ActionErrors是對錯誤信息的包裝,一旦在執行action或者form.validate中出現異常,即可產生一個ActionError并最終加入到ActionErrors。在Form驗證的過程中,如果有Error發生,則會將頁面重新導向至輸入頁,并提示錯誤。ActionErrors的源代碼如下:

          //******* ActionErrors.java**************

          package org.apache.struts.action;

          import java.io.Serializable;

          /** 該類對錯誤信息進行封裝 */

          public class ActionErrors extends ActionMessages implements Serializable {

                 /** 構造函數 */

              public ActionErrors() {

                  super();

              }

              /** 構造函數 */

              public ActionErrors(ActionErrors messages) {

                  super(messages);

              }

          }

          從上面的代碼中可以看出,該類繼承于ActionMessages,只是用來處理錯誤信息,所以關鍵還要看ActionMessages類,ActionMessages的源代碼如下:

          //******* ActionMessages.java**************

          package org.apache.struts.action;

          import java.io.Serializable;

          /** 該類對信息進行封裝和序列化 */

          public class ActionMessage implements Serializable {

              /** 定義消息的關鍵字 */

              protected String key = null;

              /** 定義消息的值 */

              protected Object[] values = null;

              /** 定義resource */

              protected boolean resource = true;

              /** 構造函數 */

              public ActionMessage(String key) {

                  this(key, null);

              }

              /** 構造函數 */

              public ActionMessage(String key, Object value0) {

                  this(key, new Object[] { value0 });

              }

              /** 構造函數 */

              public ActionMessage(String key, Object value0, Object value1) {

                  this(key, new Object[] { value0, value1 });

              }

              /** 構造函數 */

              public ActionMessage(String key, Object value0, Object value1, Object value2) {

                  this(key, new Object[] { value0, value1, value2 });

              }

              /** 構造函數 */

              public ActionMessage(String key, Object value0, Object value1,

                  Object value2, Object value3) {

                  this(key, new Object[] { value0, value1, value2, value3 });

              }

              /** 構造函數 */

              public ActionMessage(String key, Object[] values) {

                  this.key = key;

                  this.values = values;

                  this.resource = true;

              }

              /** 構造函數 */

              public ActionMessage(String key, boolean resource) {

                  this.key = key;

                  this.resource = resource;

              }

              /** 獲得消息的關鍵字 */

              public String getKey() {

                  return (this.key);

              }

              /** 獲得消息的值 */

              public Object[] getValues() {

                  return (this.values);

              }

              /** 獲取Resource */

              public boolean isResource() {

                  return (this.resource);

              }

          }

          可以看出這里的ActionMessages類,類似于Java里面的Map,有關鍵字和值,只是這里進行了封裝,這和Spring的Model比較類似。

          11.2.6  DispatchAction(多動作控制器)

          前面講Action時,曾經講過所有繼承它的類都要實現execute方法,然后在此方法里進行相應的邏輯處理。它的局限性是頁面中只有一個按鈕,如果有多個就沒有辦法,因為每個按鈕提交后,都要執行execute方法。可能讀者就會有疑問了,如果頁面中有多個按鈕,該怎么操作呢?Struts提供的DispatchAction就可以實現這樣的功能,先來看一下DispatchAction的源代碼,看它是如何實現這樣的操作的,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;

          /** 該類用來實現多動作處理,繼承于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;

                      }

                  }

                  //獲取根據request獲取的參數

                  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);

                  }

                  // 反射調用該方法

                  return dispatchMethod(mapping, form, request, response, name);

              }

              /** 未指定方法名時調用的方法 */

              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);

              }

              /** 當提交的是取消按鈕時執行的方法 */

              protected ActionForward cancelled(ActionMapping mapping, ActionForm form,

                  HttpServletRequest request, HttpServletResponse response)

          圓角矩形: 下面一段代碼描述了利用Java反射機制實現方法調用的細節。        throws Exception {

                  return null;

              }

              /** 反射調用方法 */

              protected ActionForward dispatchMethod(ActionMapping mapping,

                  ActionForm form, HttpServletRequest request,

                  HttpServletResponse response, String name)

                  throws Exception {

                  //如果沒有指定方法名,則調用unspecified

                  if (name == null) {

                      return this.unspecified(mapping, form, request, response);

                  }

                  //指定被調用的方法

                  Method method = null;

                  try {

                      //根據方法名獲取該方法

                      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;

                  //定義參數

                  try {

                      Object[] args = { mapping, form, request, response };

                      //反射調用指定的方法

                      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);

              }

              /**返回參數*/

              protected String getParameter(ActionMapping mapping, ActionForm form,

                  HttpServletRequest request, HttpServletResponse response)

                  throws Exception {

                  // 從映射中獲取參數

                  String parameter = mapping.getParameter();

                  //如果參數為空

                  if (parameter == null) {

                      String message =messages.getMessage("dispatch.handler", mapping.getPath());

                      //記錄錯誤

                      log.error(message);

                      //拋出異常

                      throw new ServletException(message);

                  }

                  //返回參數

                  return parameter;

              }

              /** 根據方法名獲取方法 */

              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);

                  }

              }

              /** 根據參數值獲取方法名 */

              protected String getMethodName(ActionMapping mapping, ActionForm form,

                  HttpServletRequest request, HttpServletResponse response,

                  String parameter) throws Exception {

                  // 獲取方法名

                  return request.getParameter(parameter);

              }

          }

          前面了解了多動作的實現機理,下面將通過具體的示例來深入講解實現方式。具體實現思路是:仍然在前面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">

                  請輸入要提交的內容:<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", "您執行的是新增動作:" + 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", "您執行的是修改動作:" + 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", "您執行的是刪除動作:" + 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鍵后,瀏覽器即可出現提示用戶輸入的畫面,如圖11.19所示。

            輸入“dddd”,然后單擊“insert”按鈕,即可轉入輸出新增的畫面,如圖11.20所示。

                

                   圖11.19  提示用戶輸入的畫面                       圖11.20  輸出新增的畫面

            單擊“update”按鈕,即可轉入輸出修改的畫面,如圖11.21所示。

            單擊“delete”按鈕,即可轉入輸出刪除的畫面,如圖11.22所示。

                

                     圖11.21  輸出修改的畫面                        圖11.22  輸出刪除的畫面

          11.3  利用Struts實現用戶登錄的示例

          前面對Struts的核心類進行了講解,下面將講解一個利用Struts實現用戶登錄的示例,使讀者對Struts的運行機理有更深的了解。

          實現思路是:本示例仍然在前面myLogin工程的基礎上進行,首先新建一個登錄頁面,增加提交按鈕,接著新建一個ActionForm為User.java類,然后新建一個登錄的控制器,接著修改配置文檔,最后驗證上述功能。

          11.3.1  編寫實現登錄的頁面login.jsp

          新建一個登錄頁面login.jsp,增加提交按鈕,放在myLogin"jsp目錄下,login.jsp的示例代碼如下:

          <%@page contentType="text/html;charset=GBK"%>

          <html>

          <head><title>實現用戶登錄的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";

                  //將頁面提交的內容進行封裝

                  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);

                  //返回到登錄畫面,顯示相關信息

                  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>

                  <!--定義映射關系-->

                  <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>      

                  </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  登錄成功的畫面

          posted on 2009-03-06 10:32 rogerfan 閱讀(1945) 評論(0)  編輯  收藏 所屬分類: 【Java知識】【開源技術】
          主站蜘蛛池模板: 临澧县| 耿马| 马尔康县| 潼关县| 武隆县| 吉水县| 沙雅县| 宝鸡市| 四子王旗| 金溪县| 资讯 | 泗洪县| 阿荣旗| 焦作市| 绵竹市| 黑水县| 万荣县| 新巴尔虎右旗| 佛山市| 长寿区| 大方县| 揭西县| 宜兰市| 措美县| 佛山市| 栖霞市| 大石桥市| 时尚| 英德市| 兴隆县| 凤翔县| 桦甸市| 永平县| 象山县| 衡东县| 镇康县| 苏尼特右旗| 朝阳市| 郓城县| 永福县| 双流县|