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 阅读(1937) 评论(0)  编辑  收藏 所属分类: 【Java知识】【开源技术】

只有注册用户登录后才能发表评论。


网站导航: