学习内容

Struts 2配置文件

Action的配置

Result的配置

属性驱动与模型驱动

能力目标

熟练进行Struts 2配置

熟练使用属性驱动和模型驱动

本章简介

上一章我们初步学习了Struts 2框架,包括Struts 2体系结构和运行流程,并通过登录案例介绍了使用Struts 2进行开发的基本步骤。

本章将深入学习Struts 2框架,主要内容是Struts 2的配置文件,包括Action的配置、Result的配置等等,只有掌握了配置文件,才能更好的使用和扩展Struts 2的功能。

核心技能部分

4.1 Struts 2的配置文件

通过上一章的学习,我们知道Struts 2框架默认的配置文件是struts.xml,该文件通常放在WEB-INF\classes目录下,该目录下的struts.xml会自动被Struts 2框架加载。本节将详细介绍该配置文件各元素的含义和使用。

4.1.1 中文乱码处理

在Struts 2中,如果客户端请求中包含有中文数据,那就很容易出现中文乱码问题。当然我们会有很多方法来解决这个问题,在struts.xml中可以通过<constant>配置更简单的解决中文乱码问题,参考代码如下所示。

<struts>
<constant name="struts.i18n.encoding" value="utf-8"></constant>
<package name="admin" extends="struts-default">
<action name="login" class="com.zy.AdminAction">
<result name="success" type="dispatcher">/index.jsp</result>
<result name="error" type="redirect">/fail.jsp</result>
</action>
</package>
</struts>

在struts.xml中,<constant>元素用来配置常量,配置时必须指定两个属性:name和value。上述代码中的struts.i18n.encoding是Struts 2中已经存在的常量属性,用来配置Web应用程序默认的编码集,相当于调用HttpServletRequset对象的setCharacterEncoding方法。

4.1.2 包配置

在Java项目中,类通常都需要“包(package)”来进行组织和管理。同样的道理,struts.xml中众多的Action也需要进行统一的组织和管理,所以Struts 2的配置文件也引入了“包(package)”,并通过“包(package)”来管理Action和拦截器。

在struts.xml文件中,package元素用来配置包,配置时通常需要指定以下三个属性:

name:必需属性,指定包的名字,不允许重复。

extends:可选属性,指定要继承的包,可以继承其它包中定义的action、拦截器等。

namespace:可选属性,定义该包的命名空间(命名空间将在下一节讲述)。

<struts>
<package name="admin" extends="struts-default">
<action name="login" class="com.zy.AdminAction">
<result name="success" type="dispatcher">/index.jsp</result>
<result name="error" type="redirect">/fail.jsp</result>
</action>
</package>
</struts>

在上述代码中,我们使用package元素配置了一个包,名字是“admin”,并继承了“struts-default”包。

通过使用extends,你可以指定本package继承另外一个package的所有的配置。当某个package继承了另外一个package的所有配置,那么你就无需对父package中已经声明过的配置定义做再次的定义。 
   同时,如果重复定义父package中已声明过的配置定义,那么这些重复定义声明将覆盖父package中的相关定义。

上例中,我们让admin包继承了struts-default包,那么这个包又在哪里呢?struts-default是Struts 2默认的包,该包定义在struts-defalut.xml文件中。该文件是Struts 2框架自带的配置文件,为框架提供诸多默认配置并在运行时自动被加载。

现在我们打开struts2-core-2.1.8.1.jar,在jar包中可以找到struts-default.xml文件。打开struts-default.xml后,内容如下:

<?xml version="1.0" encoding="UTF-8"?><struts>  <bean class="com.opensymphony.xwork2.ObjectFactory" name="xwork" />  <bean type="com.opensymphony.xwork2.ObjectFactory" name="struts" class="org.apache.struts2.impl.StrutsObjectFactory" />  <bean type="com.opensymphony.xwork2.ActionProxyFactory" name="xwork" class="com.opensymphony.xwork2.DefaultActionProxyFactory"/>  <bean type="com.opensymphony.xwork2.ActionProxyFactory" name="struts" class="org.apache.struts2.impl.StrutsActionProxyFactory"/>  <!-- 省略了其他的bean节点的定义 -->  <!--  Only have static injections -->  <bean class="com.opensymphony.xwork2.ObjectFactory" static="true" />  <bean class="com.opensymphony.xwork2.util.XWorkConverter" static="true" />  <!-- 省略了其他的静态注入的定义 -->  <package name="struts-default" abstract="true">  <result-types>  <result-type name="chain" class="com.opensymphony.xwork2.ActionChainResult"/>  <result-type name="dispatcher" class="org.apache.struts2.dispatcher.ServletDispatcherResult" default="true"/>  <result-type name="redirect" class="org.apache.struts2.dispatcher.ServletRedirectResult"/>  <!-- 省略了其他的ResultType的定义 -->  </result-types>  <interceptors>  <interceptor name="autowiring" class="com.opensymphony.xwork2.spring.interceptor.ActionAutowiringInterceptor"/>  <interceptor name="params" class="com.opensymphony.xwork2.interceptor.ParametersInterceptor"/>  <!-- 省略了其他的Interceptor的定义 -->  <!-- Basic stack -->  <interceptor-stack name="basicStack">  <interceptor-ref name="exception"/>  <interceptor-ref name="servletConfig"/>  <interceptor-ref name="prepare"/>  <interceptor-ref name="checkbox"/>  <interceptor-ref name="params"/>  <interceptor-ref name="conversionError"/>  </interceptor-stack>  <!-- A complete stack with all the common interceptors in place.  Generally, this stack should be the one you use, though it  may do more than you need. Also, the ordering can be  switched around (ex: if you wish to have your servlet-related  objects applied before prepare() is called, you'd need to move  servlet-config interceptor up.  This stack also excludes from the normal validation and workflow  the method names input, back, and cancel. These typically are  associated with requests that should not be validated.  -->  <interceptor-stack name="defaultStack">  <interceptor-ref name="exception"/>  <interceptor-ref name="alias"/>  <interceptor-ref name="servletConfig"/>  <interceptor-ref name="prepare"/>  <interceptor-ref name="i18n"/>  <interceptor-ref name="chain"/>  <interceptor-ref name="debugging"/>  <interceptor-ref name="profiling"/>  <interceptor-ref name="scopedModelDriven"/>  <interceptor-ref name="modelDriven"/>  <interceptor-ref name="fileUpload"/>  <interceptor-ref name="checkbox"/>  <interceptor-ref name="staticParams"/>  <interceptor-ref name="params">  <param name="excludeParams">dojo\..*</param>  </interceptor-ref>  <interceptor-ref name="conversionError"/>  <interceptor-ref name="validation">  <param name="excludeMethods">input,back,cancel,browse</param>  </interceptor-ref>  <interceptor-ref name="workflow">  <param name="excludeMethods">input,back,cancel,browse</param>  </interceptor-ref>  </interceptor-stack>  <!-- 省略了其他的interceptor-stack节点的定义 -->  </interceptors>  <default-interceptor-ref name="defaultStack"/>  </package>  </struts>

在上面的内容中,我们看到定义了一个包 名字是struts-default,其中引用了一系列的系统拦截器,并定义了一些拦截器栈和默认拦截器,admin包就是继承了这个struts-default包,也就继承到了里面的默认拦截器的配置,通过第一章的学习,我们知道action运行前后会有一系列的拦截器自动运行,也就是因为继承了struts-default包的缘故!

因此,正常情况下,自定义的action在配置的时候要确保直接或者间接的继承到struts-default,否则action可能无法正常工作。因为struts2很多核心功能都是拦截来实现的,如,从请求中把请求参数封闭到action,文件上传和数据验证等都是通过拦截器实现的,struts-default定义了这些拦截器和Result类型,可以这么说,当包继承了struts-default才能使用struts2提供的核心功能,

4.1.3 命名空间配置

在实际应用中,同一个struts.xml文件中可能会出现同名的Action,为了便于管理,Struts 2通过命名空间来区分同名Action,即Struts 2通过Action的逻辑名和其所在的命名空间来标识一个Action。一个命名空间中不能存在同名的Action,不同的命名空间可以存在同名的Action。在struts.xml文件中,通过给包(package)指定namespace属性来为Action设置命名空间。

<struts>
<package name="admin" extends="struts-default" namespace="/admin">
<action name="query" class="com.zy.QueryAction">
<result name="success" type="dispatcher">/list.jsp</result>
</action>
</package>
</struts>

在上述代码中,我们配置了一个名字是“admin”的包,并指定其命名空间为“/admin”,这时在访问包中名字是“query”的action时,应该这样写:

http://localhost:8080/web应用名/admin/query.action

在URL中必须指明命名空间的名字,如果一个包没有配置命名空间,那默认为“”。此时就应该这样写:

http://localhost:8080/web应用名/query.action

4.1.4 包含配置

在实际应用中,Struts 2配置文件可能会变的十分庞大,这不利于管理和维护。这时我们可以把一个Struts 2配置文件拆分成若干个配置文件,这样也有利用团队协作开发。

<struts>
<package name="admin" extends="struts-default">
<action name="login" class="com.zy.AdminAction">
<result name="success" type="dispatcher">/index.jsp</result>
<result name="error" type="redirect">/fail.jsp</result>
</action>
<action name="query" class="com.zy.QueryAction">
<result name="success" type="dispatcher">/list.jsp</result>
</action>
<action name="add" class="com.zy.AddAction">
<result name="success" type="redirect">query.action</result>
<result name="error" type="redirect">/error.jsp</result>
</action>
</package>
</struts>    

上述代码配置了三个Action,下面我们把这个配置文件拆分成两个,分别是struts1.xml和struts2.xml,代码如下所示。

struts1.xml

<struts>
<package name="p1" extends="struts-default">
<action name="login" class="com.zy.AdminAction">
<result name="success" type="dispatcher">/index.jsp</result>
<result name="error" type="redirect">/fail.jsp</result>
</action>
</package>
</struts>
struts2.xml
<struts>
<package name="p2" extends="struts-default">
<action name="query" class="com.zy.QueryAction">
<result name="success" type="dispatcher">/list.jsp</result>
</action>
<action name="add" class="com.zy.AddAction">
<result name="success" type="redirect">query.action</result>
<result name="error" type="redirect">/error.jsp</result>
</action>
</package>
</struts>
拆分之后,在struts.xml文件中通过include元素来包含struts1.xml和struts2.xml,代码如下所示。
<struts>
<include file="struts1.xml"/>
<include file="struts2.xml"/>
</struts>

include元素的file属性用来设置配置文件的路径。

4.1.5 struts.properties

Struts 2框架有两个配置文件,一个是我们前面常用的struts.xml,另一个是struts.properties。这个文件是struts2框架的全局属性配置文件。

struts.properties是一个标准的Properties文件,该文件是由一系列的key-value(键值对)组成,每个key就是一个Struts 2的属性,该key对应的value就是一个Struts 2的属性值。该文件通常放在Web应用的WEB-INF/classes目录下,Struts 2框架会自动加载这个文件。

下面将该文件常用的配置属性详细地列举出来。

struts.i18n.encoding

设置Web应用的默认编码集。此属性对于处理中文请求参数很有用,通常设置为GB2312或者utf-8。

struts.multipart.parser

该属性设置处理multipart/form-data的MIME类型请求的框架,该属性支持cos、pell和jakarta等属性值,即分别对应使用cos的文件上传框架、pell上传及common-fileupload文件上传框架,该属性的默认值为jakarta。文件上传会在后面的章节中介绍。

struts.multipart.saveDir

该属性设置上传文件的临时保存路径,该属性的默认值是javax.servlet.context.tempdir。

struts.multipart.maxSize

该属性设置Struts 2文件上传中整个请求内容允许的最大字节数。

struts.action.extension

该属性设置需要Struts 2处理的请求后缀,该属性的默认值是action,即所有匹配*.action的请求都由Struts 2处理。如果用户需要指定多个请求后缀,则多个后缀之间以英文逗号(,)隔开。

struts.configuration.xml.reload

该属性设置当struts.xml文件改变后,系统是否自动重新加载该文件。该属性的默认值是false。

struts.custom.i18n.resources

该属性指定Struts 2应用所需要的国际化资源文件,如果有多个国际化资源文件,则多个资源文件的文件名以英文逗号(,)隔开。

struts.configuration.files

该属性指定Struts 2框架默认加载的配置文件,如果需要指定默认加载多个配置文件,则多个配置文件的文件名之间以英文逗号(,)隔开。该属性的默认值为struts-default.xml,struts-plugin.xml,struts.xml,看到该属性值后大家应该明白为什么Struts 2框架会默认加载struts.xml文件了。

struts.properties文件的内容均可在struts.xml中以<constant>元素进行配置。如果这两个文件中的配置有冲突,那么将以struts.properties文件的内容为准,这与struts的搜索顺序有关。

sturt2中搜索加载常量的顺序是:

struts-default.xml

struts-plugin.xml

struts.xml

sturts.propreties

web.xml

如果在struts.xml中做了主题的配置:

<constant name="struts.ui.theme" value="xhtml"></constant>

同时在struts.properties中做了主题的配置:

struts.ui.theme=simple

并使用struts2标签开发页面如下:

<s:form action="">

<s:textfield name="uname" label="用户名"></s:textfield>

<s:password name="paw" label="密码"></s:password>

<s:submit value="登录"></s:submit>

</s:form>

运行程序效果如图4.1.1所示:

图4.1.1 主题配置

显然 Struts.properties中的主题配置效力高于struts.xml。

4.2 Struts 2的Action

在使用Struts 2框架进行开发时,开发者需要编写大量的Action,这是应用的核心。Action的主要作用有三个:

Ø 封装客户端请求数据

Ø 处理业务

Ø 返回结果

在上一章我们说过Struts 2的Action可以不用继承任何类和实现任何接口,它可以作为一个普通的JavaBean来使用。但是在实际应用中,我们仍需要一些接口和类来简化Action。

1. Action接口

为了使开发者编写的Action更加规范,Struts 2提供了Action接口,下面是该接口的源代码。

package com.opensymphony.xwork2;
public interface Action {public static final String SUCCESS = "success";public static final String NONE = "none";public static final String ERROR = "error";public static final String INPUT = "input";public static final String LOGIN = "login";public String execute() throws Exception;
}

由上述代码可知,Action接口中定义了五个字符串常量:SUCCESS、NONE、ERROR、INPUT、LOGIN。我们在execute方法中返回的字符串就来自这里。自定义的Action可以不实现该接口,开发者可以在execute方法中自定义返回字符串。

2. ActionSupport类

我们自定义的Action通常继承com.opensymphony.xwork2.ActionSupport类,此类实现了一些有用的接口(包括上面的Action接口),提供了国际化、数据验证等很多实用功能。该类的内容会在后续章节中学习。

4.2.1 动态方法调用

在上一章的任务实训部分,我们使用Struts 2实现了对管理员的增、删、改、查等操作。在实现过程中,每个请求都对应一个Action,例如:添加管理员对应的是AddAction,查询管理员对应的是QueryAction,删除管理员对应的是DelAction。在实际应用中,类似的情况经常出现,即:随着项目规模的扩大,我们不得不管理大量的Action,Struts 2框架提供了动态方法调用来解决这个问题,也就是说我们可以在一个Action中自定义不同的方法来处理多个请求。

DMI(Dynamic Method Invocation)即动态方法调用,这时不能只请求某个Action,还要使用感叹号(!)来标识出要调用的方面名,语法如下所示:

语法

Action的逻辑名!Action中的方法名 .action

示例4.1

下面我们把上一章的实训任务通过动态方法调用进行优化,这时只需要一个Action,代码如下所示:

public class AdminAction {
private int id;
private String logName;
private String logPwd1;
private AdminDao ad=new AdminDao();
public String addAdmin() {   //添加管理员
if(ad.addAdmin(logName, logPwd1))
return "success";
else
return "fail";
}
public String queryAdmin() {   //查询管理员
List<Admin> adminList=ad.getAllAdmin();
Map req=(Map)ActionContext.getContext().get("request");
req.put("adminList", adminList);
return "list";
}
public String delAdmin(){    //删除管理员
if(ad.delAdmin(id))
return "success";
else
return "fail";
}
//省略getter和setter方法
}

在上述代码中,AdminAction没有继承任何类和实现任何接口,我们在该类中自定义了三个方法分别实现对管理员的增加、删除和查询,返回字符串也是自定义的。

提示

Action中的自定义方法必须是无参的,返回类型必须是String

下面是struts.xml的代码,这时的配置文件就精简了许多。

<struts>
<constant name="struts.i18n.encoding" value="utf-8"></constant>
<package name="admin" extends="struts-default">
<action name="admin" class="com.zy.AdminAction">
<resultname="success" type="redirect">admin!queryAdmin.action</result>
<result name="fail" type="redirect">/error.jsp</result>
<result name="list" type="dispatcher">/list.jsp</result>
</action>
</package>
</struts>
addAdmin.html视图页面的表单修改如下,其他代码不变。
<form action="admin!addAdmin.action" method="post">
... ...
</form>

list.jsp视图页面的【删除】超链接修改如下,其他代码不变。

<a href="admin!delAdmin.action?id=<%=admin.getId() %>">删除</a>

通过动态方法调用优化后的项目更加精炼,方便以后的维护。

4.2.2 method属性

DMI在实际应用中可能会出现安全隐患,因为一旦某人知道了Action的名字和其中的方法名,他就可以通过URL进行任意的调用。Struts 2框架提供了一种更加安全的方式来实现DMI,在配置Action时可以通过method属性来指定需要调用的方法。

示例4.2

下面我们通过method属性来修改示例4.1,struts.xml代码如下所示:

<struts>
<constant name="struts.i18n.encoding" value="utf-8"></constant>
<package name="admin" extends="struts-default">
<action name="query" class="com.zy.AdminAction" method="queryAdmin">
<result name="fail" type="redirect">/error.jsp</result>
<result name="list" type="dispatcher">/list.jsp</result>
</action>
<action name="add" class="com.zy.AdminAction" method="addAdmin">
<result name="success" type="redirect">query.action</result>
<result name="fail" type="redirect">/error.jsp</result>
</action>
<action name="del" class="com.zy.AdminAction" method="delAdmin">
<result name="success" type="redirect">query.action</result>
<result name="fail" type="redirect">/error.jsp</result>
</action>
</package>
</struts>   

在上述代码中,我们配置了三个<action>,并分别设置了method属性,属性的值就是Action中的方法名。虽然通过method属性实现动态方法调用可以避免安全隐患,但是这种方法会导致struts.xml中的<action>越来越多,不利于管理和维护,为了解决这个问题我们可以使用通配符。

4.2.3 通配符的使用

在struts.xml文件中可以通过通配符简化<action>的配置,<action>元素的name属性支持通配符,通配符用星号(*)表示,用于匹配0到多个字符串。

示例4.3

下面我们把示例4.2通过通配符进行优化,代码如下所示。

<struts>
<package name="admin" extends="struts-default">
<action name="*" class="com.zy.AdminAction" method="{1}">
<result name="success" type="redirect">queryAdmin.action</result>
<result name="fail" type="redirect">/error.jsp</result>
<result name="list" type="dispatcher">/list.jsp</result>
</action>
</package>
</struts>

name属性的值是“*”,表示允许这个Action可以匹配任何以“.action”结束的URL。method属性的值是“{1}”,表示该属性的值是name属性值中的第一个“*”。例如:

http://localhost:8080/Struts6/queryAdmin.action

当使用上面的URL请求时,“*”和“{1}”就变成了“queryAdmin”,正好是Action中实现添加管理员的方法名。

http://localhost:8080/Struts6/delAdmin.action?id=40

当使用上面的URL请求时,“*”和“{1}”就变成了“delAdmin”,正好是Action中实现删除管理员的方法名。

通过通配符实现动态方法调用不仅避免了安全隐患,还简化了struts.xml的配置,建议在实际应用中使用这种方法进行开发。

4.2.4 配置默认Action

如果请求的Action不存在,那么页面上可能会呈现HTTP 404错误,为了避免这种情况的发生,Struts 2框架可以指定一个默认的Action,如果没有一个Action匹配客户端请求,那么这个默认的Action就会被执行。

在struts.xml中,通过<default-action-ref />元素来配置默认的Action。每个<default-action-ref />元素配置一个默认的Action。

<struts>
<package name="admin" extends="struts-default">
<action name="defaultAction">
<result>/error.jsp</result>
</action>
... ...
<default-action-ref name="defaultAction"/>
</package>
</struts>

如果action元素的class属性省略了,表示将使用默认的ActionSupport类。

4.3 Result配置

Struts 2的Action处理完用户请求后会返回一个字符串,该字符串表示逻辑视图名。struts.xml文件通过<result>元素配置逻辑视图名,并实现与物理视图资源的映射。<result>元素的配置包含两部分:由name属性设置逻辑视图名,由type属性设置视图类型。

常用的结果类型有dispatcher类型和redirect类型。

Ø dispatcher类型相当于“转发”,request、session等对象都会被转发到视图页面。

Ø redirect类型相当于“重定向”,将会丢失request、session等对象。

如果不设置type属性,默认的类型是dispatcher。

4.3.1 动态结果

在某些情况下,事先并不能确定使用哪个结果视图,必须在程序运行期间才能确定,这时如何在struts.xml中进行配置呢?我们可以在配置时使用表达式,在程序运行时,由框架根据表达式的值来确定要使用哪个结果视图,这就是动态结果。

下面我们通过一个案例来演示动态结果的用法,完善登录案例,如果是普通用户就跳转到user.jsp,如果是管理员就跳转到admin.jsp。用户的身份必须在程序运行过程中才能确定。

示例4.4

需要在原来的Action中增加一个属性用来标识用户的身份,并提供getter/setter方法,代码如下所示。

public class AdminAction {
//省略其他属性
private String flag;
public String login()
{
if(user.isAdmin())
flag="admin";
else
flag="user";
return "success";
}
public String getFlag() {
return flag;
}
public void setFlag(String flag) {
this.flag = flag;
}//省略其他业务方法//省略其他getter和setter方法
}

如果是普通用户,那么flag属性的值被设置为“user”,如果是管理员,那么flag属性的值被设置为“admin”。

下面我们看一下在struts.xml文件中如何配置,代码如下所示。

<struts>
<package name="admin" extends="struts-default">
<action name="login" class="com.zy.AdminAction">
<result name="success" type="dispatcher">/${flag}.jsp</result>
<result name="error" type="redirect">/fail.jsp</result>
</action>
</package>
</struts>

注意上述代码中的加粗部分,我们把视图页面的名字用“${ flag }”代替,“${ flag }”表达式可以获得Action中flag属性的值。在程序运行时,如果是管理员登录,该表达式的值为admin,即跳转到admin.jsp;如图是普通用户登录,该表达式的值为user,即跳转到user.jsp。

4.3.2 全局结果

在之前的项目中,我们都是把<result>元素配置到了<action>元素内部,这些<result>不能被其他Action使用。但是某些<result>可能是公用的,即多个Action都需要同一个<result>,这时我们可以通过配置全局结果来满足需求。

全局结果定义在包(package)中,而不是某个<action>元素内部,包(package)中的所有Action都可以使用这个结果。

<struts>
<package name="admin" extends="struts-default">
<global-results>
<result name="error" type="redirect">/error.jsp</result>
</global-results>
<action name="login" class="com.zy.AdminAction">
<result name="success" type="dispatcher">/index.jsp</result>
</action>
</package>
</struts>

在struts.xml文件中使用<global-results>元素配置全局结果,可以配置多个全局结果。

4.4 属性驱动与模型驱动

4.4.1 属性驱动

在Struts 1中,ActionForm用来封装客户端请求数据。在Struts 2中,没有了ActionForm,使用Action就可以封装客户端请求数据,例如登录案例中的Action,代码如下所示。

public class AdminAction extends ActionSupport {
private int id;
private String name;
private String pwd;
public String execute()
{
}
//省略getter和setter方法
}
与之对应的表单如下所示。
<form action="login.action" method="post"><table width="397" height="169" border="0" align="center"><tr><td colspan="2" align="center">管理员登录</td></tr><tr><td width="163" align="right">登录名称:</td><td width="218"><input name="name" type="text" class="txt"></td></tr><tr><td align="right">登录密码:</td><td><input name="pwd" type="password" class="txt"></td></tr><tr><td colspan="2" align="center"><input type="submit" value="提交"></td></tr>
</table>
</form>

注意上述代码中的加粗部分,表单中每一个元素的name都对应Action中的一个属性,程序在运行时,Struts 2通过Action的属性来封装客户端请求数据,这种方式称之为属性驱动。我们前面所编写的案例采用的都是这种方式。

4.4.2 模型驱动

属性驱动使得Action既要封装客户端请求数据,还要处理业务,这就造成Action承担了过多的职责,分工也不够清晰。更好的解决办法就是采用单独的Model(模型)来封装请求数据,这就是模型驱动。模型驱动中的Model(模型)其实就是一个实体类或POJO,专门用来封装客户端请求数据。

示例4.5

下面我们把前面的登录案例改成模型驱动。首先需要一个封装登录表单的实体类Admin作为模型,代码如下所示。

public class Admin {

private String name;  //登录名称

private String pwd;   //登录密码

//省略getter和setter方法

}

Action就不需要分散的属性来封装表单数据了,只需要一个Admin类型的属性即可,代码如下所示。

public class LoginAction extends ActionSupport {
private Admin admin;
public String execute() {
AdminDao ad=new AdminDao();
if(ad.checkLogin(admin.getName(), admin.getPwd()))
return SUCCESS;
else
return ERROR;
}
public Admin getAdmin() {
return admin;
}
public void setAdmin(Admin admin) {
this.admin = admin;
}
}
与之对应的表单需要进行如下修改。
<form action="login.action" method="post"><table width="397" height="169" border="0" align="center"><tr><td colspan="2" align="center">管理员登录</td></tr><tr><td width="163" align="right">登录名称:</td><td width="218"><input name="admin.name" type="text" class="txt"></td></tr><tr><td align="right">登录密码:</td><td><input name="admin.pwd" type="password" class="txt"></td></tr><tr><td colspan="2" align="center"><input type="submit" value="提交"></td></tr>
</table>
</form>

注意上述代码中的加粗部分,在使用模型驱动时,表单元素name属性的值由两部分组成,第一部分是Action中模型对象的名字,第二部分是模型对象的属性名。如果在实际应用中需要在页面上输出模型对象中属性的值,则可以使用“${ admin.name }”进行输出。

本章总结

Struts 2的配置文件

(1)中文乱码处理。可以通过名为struts.i18n.encoding的常量配置字符集。

(2)包配置。包的常用属性有name, extends和namaspaces。

(3)包含配置。

Struts 2的Action

(1)动态方法调用。

(2)Method参数和通配符的使用。

Result配置

属性驱动和模型驱动

任务实训部分

1:查询图书

训练技能点

Ø method属性

Ø 通配符

需求说明

在第二章的核心任务部分我们讲解了一个查询图书的案例,现在要求使用Struts 2进行

重构,并应用method属性和通配符进行优化处理

  实现步骤

(1) 创建对应图书表的实体类Book.java

(2) 创建实现数据库连接和关闭的工厂类DaoFactory.java

(3) 创建实现图书查询功能的Dao类BookDao.java

(4) 创建实现图书查询业务的Action类QueryAction.java,参考代码如下所示。

public class QueryAction{
private String keywords;
private BookDao bd=new BookDao();
private List bookList=new ArrayList();
private ActionContext ac=ActionContext.getContext();
private Map req=(Map)ac.get("request");
public String queryByName()   //按书名查询
{
bookList=bd.getBooks("name", keywords);
req.put("bookList", bookList);
return "success";
}
public String queryByAuthor()  //按作者查询
{
bookList=bd.getBooks("author", keywords);
req.put("bookList", bookList);
return "success";
}
public String queryByPublisher()  //按出版社查询
{
bookList=bd.getBooks("publish", keywords);
req.put("bookList", bookList);
return "success";
}
//省略getter和setter方法
}

(5) 创建实现查询界面的视图query.html,参考代码如下所示。

<body><form name="f" method="post"><table width="330" height="94" border="0" align="center"><tr><td align="center">关键字:<input type="text" name="keywords"></td></tr><tr><td align="center"><input name="rd" type="radio" value="queryByName" checkedοnclick="change(this.value)">按书名查询<input name="rd" type="radio" value="queryByAuthor"οnclick="change(this.value)">按作者查询<input name="rd" type="radio" value="queryByPublisher"οnclick="change(this.value)">按出版社查询</td></tr><tr><td align="center"><input type="button" value="查询"οnclick="submitForm()"></td></tr></table></form></body>
<script>
var v="queryByName";
function change(val)
{
v=val;
}
function submitForm()
{
f.action=v+".action";
f.submit();
}
</script>

由于本案例要使用动态方法调用,所以在该页面我们需要使用JS动态合成请求URL,运行效果如图4.2.1所示。

图4.2.1 查询界面

(6) 编辑struts.xml,参考代码如下所示。

<struts>
<constant name="struts.i18n.encoding" value="utf-8"></constant>
<package name="book" extends="struts-default">
<action name="*" class="com.zy.QueryAction" method="{1}">
<result name="success" type="dispatcher">/list.jsp</result>
</action>
</package>
</struts>

(7) 显示查询结果的视图页面list.jsp这里不再多述,运行效果如图4.2.2所示。

图4.2.2 查询结果

2:实现简易计算器

训练技能点

动态方法调用

模型驱动

需求说明

使用动态方法调用和模型驱动优化上一章的实训任务4

  实现步骤

(1) 创建实体类Calculator.java,参考代码如下所示。

public class Calculator {
private double num1;
private double num2;
private double result;//省略getter和setter方法
}

(2) 创建Action类CalculatorAction.java,参考代码如下所示。

public class CalculatorAction {
private Calculator cal;
public String jia()      //加法运算
{
cal.setResult(cal.getNum1()+cal.getNum2());
return "result";
}
public String jian()     //减法运算
{
cal.setResult(cal.getNum1()-cal.getNum2());
return "result";
}
public String cheng()    //乘法运算
{
cal.setResult(cal.getNum1()*cal.getNum2());
return "result";
}
public String chu()      //除法运算
{
cal.setResult(cal.getNum1()/cal.getNum2());
return "result";
}
//省略getter和sertter方法
}

(3) 创建计算器的视图页面,参考代码如下所示。

<form name="frm" method="post"><table width="298" border="0" align="center"><tr><td colspan="2" align="center">简易计算器</td></tr><tr><td width="76" align="center">第一个数</td><td width="206"><input type="text" name="cal.num1"value="${cal.num1}"></td></tr><tr><td align="center">第二个数</td><td><input type="text" name="cal.num2" value="${cal.num2}"></td></tr><tr><td colspan="2" align="center"><input type="button"  value="+" οnclick="submitForm('jia')"><input type="button"  value="-" οnclick="submitForm('jian')"><input type="button"  value="*" οnclick="submitForm('cheng')"><input type="button"  value="/" οnclick="submitForm('chu')"></td></tr><tr><td align="center">结果</td><td><input type="text" name="result" readonlyvalue="${cal.result}"></td></tr></table>
</form></body>
<script>
function submitForm(op)
{
frm.action="cal_"+op+".action";
frm.submit();
}
</script>

由于要使用动态方法调用和通配符,所以在页面中我们使用JS合成请求URL。同时我们使用EL表达式输出了Action中属性的值。

(4) 配置struts.xml,代码如下所示。

<package name="calculator" extends="struts-default">
<action name="cal_*" class="com.test.CalculatorAction" method="{1}">
<result name="result" type="dispatcher">/calculator.jsp</result>
</action>
</package> 

图4.2.3 计算器

3:模型驱动

训练技能点

模型驱动

需求说明

把上一章的前三个实训任务使用模型驱动进行优化

巩固练习

一、选择题

1. 以下关于<action>元素的说法错误的是()。

A. name属性是必须的

B. name属性不能唯一确定一个Action

C. method属性表示调用Action中的哪个方法

D. 通配符除了星号(*)还有下划线(_)

2. 以下关于<result>元素的说法错误的是()。

A.  type属性指定结果类型

B.  name属性指定结果的逻辑名

C.  name属性的值必须是Action接口中的某个常量

D. 默认的类型是dispatcher

3. Struts 2的结果类型有()。

A.  dispatcher

B. redirect

C.  chain

D.  success

4. DMI的正确写法是()。

A. Action的逻辑名!Action中的方法名 .action

B. Action的逻辑名_Action中的方法名 .action

C. Action的逻辑名!Action中的方法名 .do

D. Action的逻辑名_Action中的方法名 .do

5. 以下关于Struts 2配置文件说法正确的是()。

A. struts.properties是必须的配置文件

E. struts.properties通过标签元素进行配置

F. 只能同时使用struts.properties与struts.xml中的一个

G. struts.properties中的配置通常都可以在struts.xml中进行配置

二、上机练习

把上一章的课后上机练习使用模型驱动和动态方法调用进行重构。运行效果图如下。

图4.3.1 增加【修改】超链接

图4.3.2 密码修改页面

Struts 2配置详解相关推荐

  1. struts2配置详解(struts.xml配置详解)

    本文转自:http://www.cnblogs.com/fmricky/archive/2010/05/20/1740479.html struts.xml是我们在开发中利用率最高的文件,也是Stru ...

  2. struts.xml配置详解

    2019独角兽企业重金招聘Python工程师标准>>> 1. 在struts2中配置常量的方式有三种: 在struts.xml文件中配置         在web.xml文件中配置  ...

  3. struts.properties配置详解

    Struts 2框架有两个核心配置文件,其中struts.xml文件主要负责管理应用中的Action映射,以及该Action包含的Result定义等.除此之外,Struts 2框架还包含struts. ...

  4. struts2 的struts.properties配置详解

    Struts 2框架有两个核心配置文件,其中struts.xml文件主要负责管理应用中的Action映射,以及该Action包含的Result定义等.除此之外,Struts 2框架还包含     st ...

  5. 6、struts.properties配置详解

    在Struts2的核心包中org\apache\struts2文件夹下可以找到Struts2的一个默认配置文件default.properties.有时为了调试方便我们可以更改部分默认内容.具体的方法 ...

  6. spring之旅第四篇-注解配置详解

    spring之旅第四篇-注解配置详解 一.引言 最近因为找工作,导致很长时间没有更新,找工作的时候你会明白浪费的时间后面都是要还的,现在的每一点努力,将来也会给你回报的,但行好事,莫问前程!努力总不会 ...

  7. struts2+hibernate+spring配置详解

    #struts2+hibernate+spring配置详解 struts2+hibernate+spring配置详解 哎 ,当初一个人做好难,现在终于弄好了,希望自学这个的能少走些弯路. 以下是自己配 ...

  8. SpringMVC基础--spring MVC配置详解

    牧涛 --<-<-<@态度决定一切→_→... 博客园 首页 新闻 新随笔 联系 管理 订阅 随笔- 171  文章- 3  评论- 79  spring MVC配置详解 现在主流的 ...

  9. struts-config配置详解

    struts-config配置详解 <!-- struts-config是整个xml的根元素,其他元素必须被包含其内 --> <struts-config>  <!-- ...

最新文章

  1. HDU 3065病毒侵袭持续中 AC自动机
  2. JTextPane设置颜色出现的问题
  3. 微信圈子将于12月28日停止运营,网友:不是微信朋友圈?
  4. Java基础-IO流
  5. 广东计算机等级考试一级试题,广东省计算机等级考试(一级)仿真试题
  6. (转)马云的江湖 PK 史玉柱的兵法
  7. 逻辑回归与线性回归的区别与联系
  8. mysql 数据库导出导入到本地文件
  9. 一种特殊的多语言解决方案
  10. UCGUI窗体管理及消息处理机制分析
  11. 最有效率地戒掉晚睡强迫症(熬夜强迫症、假象失眠症等等)
  12. a轮融资1亿多不多_A轮融资一共就三件重要的事情 B轮最重要的两件事
  13. 【计算机网络】TCP糊涂窗口综合症
  14. 《OpenGL ES 3.x游戏开发(下卷)》一2.1 飘扬的旗帜
  15. ProxySQL 排错 Max connect timeout reached while reaching hostgroup 10 after 10000ms
  16. 最近超火的100句土味情话合集,拿去撩妹撩汉吧!
  17. 新冠疫情下的化工企业数字化转型
  18. 【LeetCode 1000】 Minimum Cost to Merge Stones
  19. CVE-2022-1388——F5 BIG-IP iControl REST 身份认证绕过漏洞
  20. 超市商品管理系统(c语言)

热门文章

  1. 速度优化_网站打开速度慢?如何优化?
  2. android 禁用dlsym_Android7.0对dlopen的改变——读取私有.so结果变化
  3. vue office在线编辑_VUE和Antv G6实现在线拓扑图编辑
  4. 0-1 背包问题的 4 种解决方法算法策略
  5. leetcode--1025. 除数博弈
  6. 【解决办法】你目前是以 ***的身份登录。请注销,然后使用你用于阅读组织电子邮件的帐户登录 Outlook
  7. 【测试点2超时问题】1046 Shortest Distance (20 分)_21行代码AC
  8. KeyError: 报错_python系列学习笔记
  9. Web前端开发笔记——第二章 HTML语言 第十节 画布标签、音视频标签
  10. hibernate连接mysql配置文件 分享