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"
};
/** 映射描述 */
protected String servletMapping = null; // :FIXME: - multiples?
/** 定义Servlet的名称 */
protected String servletName = null;
/** 覆写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()方法,用来进行一些销毁的动作,如释放相关的模组、日志等,接下来就要进行一些初始化的工作,代码如下所示:
……
/**处理一些初始化动作*/
public void init() throws ServletException {
final String configPrefix = "config/";
//获取配置文档的前缀为config的长度
final int configPrefixLength = configPrefix.length() - 1;
try {
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
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);
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);
//如果没有映射,则返回
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);
}
//如果实例的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();
//如果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
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 + "'");
}
//加入到session中
session.setAttribute(Globals.LOCALE_KEY, locale);
}
}
/** 处理映射 */
protected ActionMapping processMapping(HttpServletRequest request,
HttpServletResponse response, String path)
throws IOException {
//获取ActionMapping
ActionMapping mapping = (ActionMapping) moduleConfig.findActionConfig(path);
//判断request里是否有映射
if (mapping != null) {
request.setAttribute(Globals.MAPPING_KEY, mapping);
//如果有,就返回
return (mapping);
}
//获取ActionConfig
ActionConfig[] configs = moduleConfig.findActionConfigs();
//获取映射,存入request中
for (int i = 0; i < configs.length; i++) {
if (configs[i].getUnknown()) {
mapping = (ActionMapping) configs[i];
request.setAttribute(Globals.MAPPING_KEY, mapping);
//返回映射
return (mapping);
}
}
//如果没有映射,则返回一个消息
String msg = getInternal().getMessage("processInvalid");
//记录这个错误消息
log.error(msg + " " + path);
response.sendError(HttpServletResponse.SC_NOT_FOUND, msg);
return null;
}
/** 处理组件 */
protected HttpServletRequest processMultipart(HttpServletRequest request) {
//如果获取的request方法不是post
if (!"POST".equalsIgnoreCase(request.getMethod())) {
return (request);
}
//获取contentType
String contentType = request.getContentType();
//如果contentType的类型是multipart/form-data,则封装
if ((contentType != null)&& contentType.startsWith("multipart/form-data")) {
//对request进行封装
return (new MultipartRequestWrapper(request));
} else {
return (request);
}
}
/** 处理NoCache */
protected void processNoCache(HttpServletRequest request,
HttpServletResponse response) {
//设定Nocache
if (moduleConfig.getControllerConfig().getNocache()) {
//设定头
response.setHeader("Pragma", "No-cache");
response.setHeader("Cache-Control", "no-cache,no-store,max-age=0");
response.setDateHeader("Expires", 1);
}
}
/** 处理路径*/
protected String processPath(HttpServletRequest request,
HttpServletResponse response)
throws IOException {
String path;
//获取path
path = (String) request.getAttribute(INCLUDE_PATH_INFO);
//如果path为空
if (path == null) {
path = request.getPathInfo();
}
//如果不为空,则返回这个path
if ((path != null) && (path.length() > 0)) {
return (path);
}
//获取servlet 的path
path = (String) request.getAttribute(INCLUDE_SERVLET_PATH);
//如果为空
if (path == null) {
path = request.getServletPath();
}
//如果不为空
String prefix = moduleConfig.getPrefix();
//获取前缀
if (!path.startsWith(prefix)) {
String msg = getInternal().getMessage("processPath");
//记录错误信息
log.error(msg + " " + request.getRequestURI());
response.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
//返回空
return null;
}
//截取path
path = path.substring(prefix.length());
//根据/和.进行截取
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,
(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)
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 登录成功的画面