通过ServletActionContext
访问
上面创建messageAction
类
package com.qijian.domain; import org.apache.struts2.ServletActionContext; import com.opensymphony.xwork2.ActionSupport; public class MessageAction extends ActionSupport{ public String execute() throws Exception{ ServletActionContext.getRequest().setAttribute("message","通过ServletActionContext类访问Servlet API"); return SUCCESS; } }
struts.xml
添加配置
<action name="message" class="com.qijian.domain.MessageAction"> <result name="success">/message.jsp</result> </action>
创建message.jsp
<div align="center">${requestScope.message }</div>
使用ServletActionContext类也可以在 Action 中访问 Servlet API。虽然如此,该 Action 依然与 Servlet API 直接耦合,这不利于程序的解耦。因此,建议在开发中优先使用 ActionContext,以避免和 Servlet API 耦合。
Action处理请求参数
属性驱动
属性驱动是指在 Action 中通过字段属性进行与页面之间的数据传递,通常使用时会包括两种情况:一种是与基本数据类型的属性对应,另一种是直接使用域对象
- 基本数据类型字段驱动方式的数据传递
在 Struts2 中,可以直接在 Action 中定义各种 Java 基本数据类型的字段,使这些字段与表单数据相对应,并利用这些字段进行数据传递,如下面的代码所示,定义了两个字符串字段 username 和 password,这两个字段分别用于对应页面上的用户名和密码这两个表单域
public class UserAction extends ActionSupport { private String username; // 用户名 private String password; // 密码 // 此处省略两个属性的getter和setter方法 private String execute() throws Exception { return SUCCESS; } }
2. 直接使用域对象字段驱动方式的数据传递
在基本数据类型字段驱动方式中,如果传入的数据很多,那么 Action 的属性也会变得很多,再加上属性对应的 getter/setter 方法,势必会导致 Action 非常臃肿。
为了解决这一问题,我们可以把属性的 getter/setter 方法从 Action 中提取出来单独作为一个域对象,并在相应的 Action 中直接使用这个域对象。此种方式中的域对象一般以 JavaBean 的形式实现,JavaBean 中所封装的属性要和页面中表单的属性一一对应。此时 JavaBean 将成为数据传递的载体,并可以在其他 Action 中使用。
user.java
package com.qijian.domain; public class User { private String username; private String password; // username的getter和setter方法 public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } // password的getter和setter方法 public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
UserLoginAction.java
package com.qijian.domain; import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.ActionSupport; public class UserLoginAction extends ActionSupport { private User user; // 定义User属性 // user属性的getter和setter方法 public User getUser() { return user; } public void setUser(User user) { this.user = user; } public String execute() throws Exception { // 获取Context对象 ActionContext context = ActionContext.getContext(); if ("admin".equals(user.getUsername()) && "123456".equals(user.getPassword())) { // 将用户名和密码放入session中 context.getSession().put("username", user.getUsername()); context.getSession().put("password", user.getPassword()); return SUCCESS; } else { context.getSession().put("error", "用户名或密码错误!"); return ERROR; } } }
配置struts
<action name="userlogin" class="com.qijian.domain.UserLoginAction"> <result name="success">/loginSuccess.jsp</result> <result name="error">/loginError.jsp</result> </action>
userLogin.jsp
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title>用戶登录页面</title> <style type="text/css"> input[type=text],input[type=password]{width:150px} </style> </head> <body> <div align="center"> <form action="userlogin" method="post"> 用户名:<input type="text" name="user.username"/><br/> 密 码:<input type="password" name="user.password"/><br/> <input type="reset" value="重置"/> <input type="submit" value="登录"/> </form> </div> </body> </html>
loginSuccess.jsp
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title>用戶登录成功页面</title> </head> <body> <p align="center"> 您的用戶名是<%=request.getAttribute("user.username") %> <br/> 密码是<%=request.getAttribute("user.password") %> </p> </body> </html>
loginError.jap
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title>错误页面</title> </head> <body> <p align="center"> ${error }<br/> <a href="userLogin.jsp">请单击此链接返回登录页面重新登录</a> </p> </body> </html>
需要注意的是,在使用域对象的属性驱动方式传值时,如果 JSP 页面是负责取值的,那么取值的格式必须为“对象名.属性名”;如果 JSP 页面是负责传值的,那么传值的格式可以为“模型对象名.属性名”。(有的教程说直接属性名也可以但是试了一下发现不行)
模型驱动
Action 还有另外一种方式处理请求参数,称为模型驱动(ModelDriven
)。
模型驱动方式要求 Action 需要通过实现 ModelDriven 接口接收请求参数,并且要重写 getModel()方法。getModel()方法返回的就是 Action 所使用的数据模型对象。
与属性驱动中直接使用域对象字段驱动方式的数据传递类似,模型驱动方式也是通过 JavaBean 模型进行数据传递的。只要是普通的 JavaBean,就可以充当模型部分,并且 JavaBean 中所封装的属性要与表单的属性一一对应,JavaBean 就是数据传递的载体。
package com.qijian.domain; import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.ActionSupport; public class UserLoginAction extends ActionSupport { private User user; // 定义User属性 // user属性的getter和setter方法 public User getUser() { return user; } public void setUser(User user) { this.user = user; } public String execute() throws Exception { // 获取Context对象 ActionContext context = ActionContext.getContext(); if ("admin".equals(user.getUsername()) && "123456".equals(user.getPassword())) { // 将用户名和密码放入session中 context.getSession().put("username", user.getUsername()); context.getSession().put("password", user.getPassword()); return SUCCESS; } else { context.getSession().put("error", "用户名或密码错误!"); return ERROR; } } }
提交表单如下
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title>用戶登录页面</title> <style type="text/css"> input[type=text],input[type=password]{width:150px} </style> </head> <body> <div align="center"> <%-- <form action="userlogin" method="post">--%> <%-- 用户名:<input type="text" name="user.username"/><br/>--%> <%-- 密 码:<input type="password" name="user.password"/><br/>--%> <%-- <input type="reset" value="重置"/>--%> <%-- <input type="submit" value="登录"/>--%> <%-- </form>--%> <form action="loginAction" method="post" name="form1"> 用户名:<input type="text" name="username"/><br/> 密码:<input type="password" name="password"/><br/> <input type="submit" value="登录"/> </form> </div> </body> </html>
成功页面
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title>用戶登录成功页面</title> </head> <body> <p align="center"> 您的用戶名是<%=request.getAttribute("username") %> <br/> 密码是<%=request.getAttribute("password") %> </p> </body> </html>
struts配置
<action name="loginAction" class="com.qijian.domain.LoginAction"> <result name="success">/loginSuccess.jsp</result> <result name="error">/loginError.jsp</result> </action>
使用ModelDriver 的方式后,表单中的文本域名称已经不需要添加 user 前缀,页面上的 username 会自动对应到这个 Model 的 username 属性。
与属性驱动相比,模型驱动不需要在 Action 类中定义与表单元素一一对应的所有属性及其各属性的 getter 和 setter 方法,这减少了 Action 类中的代码量。在项目应用中具体使用哪种驱动方法,现给出以下几点建议。
1)要统一整个系统中 Action 的驱动方法,即要么都使用属性驱动,要么都使用模型驱动。
2)如果持久层对象与表单中的属性是一一对应的关系,那么建议使用模型驱动,因为模型驱动方法使 Action 类中的代码更加整洁。
3)如果持久层对象与表单中的属性不是一一对应的关系,那么建议使用属性驱动,因为不是一一对应的关系时,系统中需要提供两个 JavaBean(一个对应表单提交的数据,一个用于持久层对象)。
总之,属性驱动的方法和模型驱动的方法各有优缺点,在实际开发中,需要根据项目实际情况选择使用哪种驱动方式。
Struts2 ResultType结果类型配置
配置Result
配置`<result>` 元素用于配置 Result 逻辑视图与物理视图之间的映射关系,它有两个可选属性 name 和 type。其中,name 属性用于指定逻辑视图的名称,默认值为 success;type 属性用于指定返回的视图资源的类型,不同的类型代表不同的结果输出,它的默认值是 dispatcher。
<action name="loginAction" class="com.mengma.action.LoginAction"> <result name="success" type="dispatcher"> <param name="location">/success.jsp</param> </result> </action>
上述代码为 Action 配置了一个 name 为 success 的 Result 映射,该映射的值可以是 JSP 页面,也可以是一个 Action 的 name 值;这里使用 param 子元素为其指定了 Result 映射对应的物理视图资源为success.jsp。
子元素的 name 属性有两个值,分别如下。
location:指定该逻辑视图所对应的实际视图资源。
parse:指定在逻辑视图资源名称中是否可以使用 OGNL(对象图导航语言)表达式。默认值为 true,表示可以使用,如果设为 false,则表示不支持。
简化:
<action name="loginAction" class="com.mengma.action.LoginAction"> <result>/success.jsp</result> </action>
需要注意的是,在 Result 配置中指定实际资源位置时,可以使用绝对路径,也可以使用相对路径。
- 绝对路径以斜杠“/”开头,例如<result>/success.jsp</result>,相当于当前 Web 应用程序的上下文路径。
- 相对路径不以斜杠“/”开头,例如<result>success.jsp</result>,相当于当前执行的 Action 路径。
预定义结果类型
当框架调用 Action 对请求进行处理后,就要向用户呈现一个结果视图。在 Struts2 中,预定义了多种 ResultType
(结果类型)展示结果视图。
一个结果类型就是实现了 com.opensymphony.xwork2.Result 接口的类,Struts2 把内置的 都放在 struts-default 包中,struts-default 包就是配置包的父包,这个包定义在 struts2-core-x.x.xx.jar 包的根目录下的 struts-default.xml 文件中,在该文件中可以找到相关的<result-type> 的定义,其代码如下所示:
<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="freemarker" class="org.apache.struts2.views.freemarker.FreemarkerResult"/> <result-type name="httpheader" class="org.apache.struts2.dispatcher.HttpHeaderResult"/> <result-type name="redirect" class="org.apache.struts2.dispatcher.ServletRedirectResult"/> <result-type name="redirectAction" class="org.apache.struts2.dispatcher.ServletActionRedirectResult"/> <result-type name="stream" class="org.apache.struts2.dispatcher.StreamResult"/> <result-type name="velocity" class="org.apache.struts2.dispatcher.VelocityResult"/> <result-type name="xslt" class="org.apache.struts2.views.xslt.XSLTResult"/> <result-type name="plainText" class="org.apache.struts2.dispatcher.PlainTextResult" /> <result-type name="postback" class="org.apache.struts2.dispatcher.PostbackResult" /> </result-types>
在上述代码中,每个 <result-type>元素都是一种视图技术或者跳转方式的封装,其中 name 属性指出在 <result>元素中如何引用这种视图技术或者跳转方式,它对应着 元素的 type 属性。class 属性表示这种结果类型的对应类。
表 1 列举了 Struts2 中预定义的全部 11 种结果类型,其中 dispatcher 是默认的结果类型,主要用于与 JSP 整合。在这全部 11 种结果类型中,dispatcher 和 redirect 是比较常用的结果类型。
需要注意的是,redirect 与 dispatcher 结果类型非常相似,所不同的是 dispatcher 结果类型是将请求转发到 JSP 视图资源,而 redirect 结果类型是将请求重定向到 JSP 视图资源。如果重定向了请求,那么将丢失所有参数,包括 Action 的处理结果。
dispatcher结果类型
dispatcher 是 Struts2 的默认结果类型,它用于表示转发到指定结果资源。
由于 Struts2 在后台使用 RequestDispatcher 的 forward() 方法转发请求,所以在用户的整个请求/响应过程中,保持的是同一个请求对象,即目标 JSP/Servlet 接收到的请求/响应对象与最初的 JSP/Servlet 的请求/响应对象相同。
dispatcher 结果类型的对应类是 org.apache.struts2.dispatcher.ServletDispatcherResult,该类有 location 和 parse 两个属性,可以通过 struts.xml 配置文件中的 元素的 子元素设置,代码如下所示:
<result name="success" type="dispatcher"> <param name="location">/success.jsp</param> <param name="parse">true</param> </result>
location 参数用于指定 Action 执行完毕后要转向的目标资源;parse 参数是一个布尔类型的值,默认是 true,表示解析 location 参数中的 OGNL 表达式,如果为 false,则不解析。
redirect结果类型
redirect 结果类型用于重定向到指定的结果资源,该资源可以是 JSP 文件,也可以是 Action 类。使用 redirect 结果类型时,系统将调用 HttpServletResponse 的 sendRedirect() 方法将请求重定向到指定的 URL。
redirect 结果类型的对应类是 org.apache.struts2.dispatcher.ServletRedirectResult。在使用 redirect 时,用户要完成一次和服务器之间的交互,浏览器需要发送两次请求,请求过程如图 1 所示
使用 redirect 结果类型的工作过程如下。
1)浏览器发出一个请求,Struts2框架调用对应的Action实例对请求进行处理。
2)Action返回success结果字符串,Struts2框架根据这个结果选择对应的结果类型,这里使用的是redirect结果类型。
3)ServletRedirectResult在内部使用HttpServletResponse的sendRedirect()方法将请求重新定向到目标资源。
4)浏览器重新发起一个针对目标资源的新请求。
5)目标资源作为响应呈现给用户。
例如可对上面的例子进行配置
<action name="login" class="com.mengma.action.LoginAction"> <result name="success" type="redirect">/success.jsp</result> <result name="error" type="dispatcher">/error.jsp</result> </action>
将成功登录的结果类型设置为 redirect,它表示当 Action 处理请求后会重新生成一个请求。将错误的结果类型设置为 dispatcher,这也是结果类型的默认值。
注意:
- redirect是利用的重定向,由于使用 redirect 重定向到其他资源时,将重新产生一个请求,并且原来的请求内容和参数将全部丢失,所以页面中的用户名和密码没有显示。
- 当用户名和密码错误时,使用的是 dispatcher 结果类型,此时页面将跳转到 error.jsp,由于是请求转发行为,所以地址栏中显示的还是 login 的请求信息,但页面中显示的是 error.jsp 中的内容
拦截器简介
在 Struts2 框架中,拦截器是其重要的组成部分,Struts2 的很多功能(数据校验、对象类型转换、文件上传等)都是构建在拦截器之上的。
当多个拦截器组合在一起时就形成了拦截器链(Interceptor Chain)或拦截器栈(Interceptor Stack)。
拦截器链就是指对应各个功能的拦截器按照一定的顺序排列在一起形成的链,而拦截器链组成的集合就是拦截器栈。当有适配连接器栈的访问请求进来时,这些拦截器就会按照之前定义的顺序被调用。
在通常情况下,拦截器都是以代理方式调用的,它在一个 Action 执行前后进行拦截,围绕着 Action 和 Result 的执行而执行,其工作方式如图 1 所示。
从图 1 中可以看出,Struts2 拦截器的实现原理与 Servlet 过滤器的实现原理类似,它以链式执行,对真正要执行的方法(execute())进行拦截。
在执行 Action 的 execute() 方法之前会执行一次拦截,在 Action 和 Result 执行之后,拦截器会再次执行(与先前的调用顺序相反)。在此链式执行的过程中,每一个拦截器都可以直接返回,从而终止余下的拦截器、Action 及 Result 的执行。
struts2拦截器的配置与使用
拦截器
要使用拦截器,首先要对它进行配置。拦截器的配置是在 struts.xml 文件中完成的,它通常以 标签开头,以 标签结束。定义拦截器的语法格式如下所示:
<interceptor name="interceptorName" class="interceptorClass"> <param name="paramName">paramValue</param> </interceptor>
元素的 name 属性用于指定拦截器的名称,class 属性用于指定拦截器的实现类。有时,在定义拦截器时需要传入参数,这时需要使用 标签,其中 name 属性用于指定参数的名称,paramValue 表示参数的值
拦截器栈
在实际的项目开发中,经常需要在 Action 执行之前执行多个拦截动作,如登录日志记录、权限管理等。
为了方便代码管理和程序的执行,开发者通常会将这些拦截器组成一个拦截器栈,在使用时,可以将栈内的多个拦截器当成一个整体引用。当拦截器栈被附加到一个 Action 上时,在执行 Action 之前必须先执行拦截器栈中的每一个拦截器。
定义拦截器栈使用 元素和 子元素,当配置多个拦截器时,需要使用 元素指定多个拦截器,配置语法如下所示:
<interceptors> <interceptor-stack name="interceptorStackName"> <interceptor-ref name="interceptorName"/> ... </interceptor-stack> </interceptors>
interceptorStackName
值表示配置的拦截器栈的名称;interceptorName
值表示拦截器的名称。除此之外,在一个拦截器栈中还可以包含另一个拦截器栈,示例代码如下所示:
<package name="default" namespace="/" extends="struts-default"> <!--声明拦截器--> <interceptors> <interceptor name="interceptor1" class="interceptorClass"/> <interceptor name="interceptor2" class="interceptorClass"/> <!--定义一个拦截器栈myStack,该拦截器栈中包含两个拦截器和一个拦截器栈--> <interceptor-stack name="myStack"> <interceptor-ref name="defaultStack"/> <interceptor-ref name="interceptor1"/> <interceptor-ref name="interceptor2"/> </interceptor-stack> </interceptors> </package>
定义的拦截器栈的名称是myStack
,在myStack
栈中,除了引用了两个自定义的拦截器 interceptor1 和 interceptor2 以外,还引用了一个内置拦截器栈defaultStack
,这个拦截器是必须要引入的。
默认拦截器
如果想对一个包下的 Action 使用相同的拦截器,则需要为该包中的每个 Action 都重复指定同一个拦截器,这样写显然过于繁琐。为了解决此问题,Struts2 中支持使用默认拦截器,它可以对其指定的包中的所有 Action 都起到拦截作用。
一旦为某一个包指定了默认拦截器,并且该包中的 Action 未显示指定拦截器,则会使用默认拦截器。反之,若此包中的 Action 显示的指定了某个拦截器,则该默认拦截器将会被屏蔽。此时,如果还想使用默认拦截器,则需要用户手动配置该默认拦截器的引用。
配置默认拦截器需要使用 元素,此元素为 元素的子元素。其语法格式如下所示:
<default-interceptor-ref name="拦截器(栈)的名称"/>
name 属性的值必须是已经存在的拦截器或拦截器栈的名称。下面用该语法格式配置一个默认拦截器,示例代码如下
<package name="default" namespace="/" extends="struts-default"> <!--声明拦截器--> <interceptors> <interceptor name="interceptor1" class="interceptorClass"/> <interceptor name="interceptor2" class="interceptorClass"/> <!--定义一个拦截器栈myStack,该拦截器栈中包含两个拦截器和一个拦截器栈--> <interceptor-stack name="myStack"> <interceptor-ref name="defaultStack"/> <interceptor-ref name="interceptor1"/> <interceptor-ref name="interceptor2"/> </interceptor-stack> </interceptors> <!--配置包下的默认拦截器,既可以是拦截器,也可以是拦截器栈--> <default-interceptor-ref name="myStack"/> <action name="login" class="com.mengma.action.LoginAction"> <result name="input">/login.jsp</result> </action> </package>
指定了包下面的默认拦截器为一个拦截器栈,该拦截器栈将会作用于包下所有的 Action。
注意:每一个包下只能定义一个默认拦截器,如果需要多个拦截器作为默认拦截器,则可以将这些拦截器定义为一个拦截器栈,再将这个拦截器栈作为默认拦截器即可。
内键拦截器
自定义拦截器
1 , 实现接口
通常开发人员所编写的自定义拦截器类都会直接或间接地实现 com.opensymphony.xwork2.interceptor.Interceptor
接口。Interceptor
接口中的主要代码如下
public interface Interceptor extends Serializable{ void init(); void destroy(); String intercept(ActionInvocation invocation) throws Exception; }
接口共提供了以下三个方法。
1)void init()
该方法在拦截器被创建后会立即被调用,它在拦截器的生命周期内只被调用一次。可以在该方法中对相关资源进行必要的初始化。
2)void destroy()
该方法与 init() 方法相对应,在拦截器实例被销毁之前,将调用该方法释放和拦截器相关的资源,它在拦截器的生命周期内,也只被调用一次。
3)String intercept(ActionInvocation invocation)throws Exception
该方法是拦截器的核心方法,用于添加真正执行拦截工作的代码,实现具体的拦截操作,它返回一个字符串作为逻辑视图,系统根据返回的字符串跳转到对应的视图资源。每拦截一个动作请求,该方法就会被调用一次。
该方法的 ActionInvocation 参数包含了被拦截的 Action 的引用,可以通过该参数的 invoke() 方法,将控制权转给下一个拦截器或者转给 Action 的 execute() 方法。
2,继承AbstractIntercepter
在实际开发过程中,更常用的一种方式是继承抽象拦截器类 AbstractIntercepter。
AbstractIntercepter 类实现了 Interceptor 接口,并且提供了 init() 方法和 destroy() 方法的空实现。使用时,可以直接继承该抽象类,而不用实现那些不必要的方法。AbstractInterceptor 类中定义的方法如下
public abstract class AbstractInterceptor implements Interceptor{ public void init(){} public void destroy(){} public abstract String intercept (ActionInvocation invocation) throws Exception; }
AbstractInterceptor 类已经实现了 Interceptor 接口的所有方法,一般情况下,只需继承 AbstractInterceptor 类,实现 interceptor() 方法就可以创建自定义拦截器。
需要注意的是,只有当自定义的拦截器需要打开系统资源时,才需要覆盖 AbstractInterceptor 类的 init() 方法和 destroy() 方法。与实现 Interceptor 接口相比,继承 AbstractInterceptor 类的方法更为简单。
自定义拦截器权限控制案例
项目结构
web.xml配置
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"> <!-- 定义filter --> <filter> <!-- filter名字 --> <filter-name>struts2</filter-name> <!-- filter的实现类,此处是Struts2的核心过滤器 --> <filter-class> org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter </filter-class> </filter> <filter-mapping> <!-- filter的名字,必须是filter元素中已经声明过的过滤器的名字 --> <filter-name>struts2</filter-name> <!-- 定义filter负责拦截的URL地址 --> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 首页 --> <welcome-file-list> <welcome-file>main.jsp</welcome-file> </welcome-file-list> </web-app>
the code of user.java
package com.mengma.domain; public class User { private String username; // 用户名 private String password; // 密码 public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
创建LoginAction
package com.mengma.action; import com.mengma.domain.User; import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.ActionSupport; import com.opensymphony.xwork2.ModelDriven; public class LoginAction extends ActionSupport implements ModelDriven<User> { private static final long serialVersionUID = -8493698886438630994L; private User user = new User(); @Override public User getModel() { return user; } public String execute() throws Exception { // 获取ActionContext ActionContext actionContext = ActionContext.getContext(); if ("admin".equals(user.getUsername()) && "123456".equals(user.getPassword())) { // 将用户存储在session中 actionContext.getSession().put("user", user); return SUCCESS; } else { actionContext.put("msg", "用户名或密码错误,请重新登录!"); return INPUT; } } }
BookAction 类
package com.mengma.action; import com.opensymphony.xwork2.ActionSupport; public class BookAction extends ActionSupport { private static final long serialVersionUID = 5640989517690867879L; // 购买图书 public String buy() { return SUCCESS; } }
自定义拦截器的使用过程
- 用户自定义的拦截器类,必须实现 Interceptor 接口或继承 AbstractInterceptor 类。
- 需要在 Struts.xml 中定义自定义的拦截器。
- 在 Struts.xml 中的 Action 中使用拦截器。
PrivilegeInterceptor.java
package com.mengma.interceptor; import com.opensymphony.xwork2.Action; import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.AbstractInterceptor; public class PrivilegeInterceptor extends AbstractInterceptor { private static final long serialVersionUID = 193664972753450682L; @Override public String intercept(ActionInvocation invocation) throws Exception { // 得到 ActionContext ActionContext actionContext = invocation.getInvocationContext(); // 获取User对象 Object user = actionContext.getSession().get("user"); if (user != null) { return invocation.invoke(); // 继续向下执行 } else { actionContext.put("msg", "您还未登录,请先登录!"); return Action.LOGIN; // 如果用户不存在,则返回login值 } } }
struts.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" "http://struts.apache.org/dtds/struts-2.3.dtd"> <struts> <package name="struts2" namespace="/" extends="struts-default"> <!-- 声明拦截器 --> <interceptors> <interceptor name="privilege" class="com.mengma.interceptor.PrivilegeInterceptor" /> <interceptor-stack name="myStack"> <interceptor-ref name="defaultStack" /> <interceptor-ref name="privilege" /> </interceptor-stack> </interceptors> <!-- 用户登录操作 --> <action name="login" class="com.mengma.action.LoginAction"> <result>/main.jsp</result> <result name="input">/login.jsp</result> </action> <!-- 关于book操作 --> <action name="book_*" class="com.mengma.action.BookAction" method="{1}"> <result>/success.jsp</result> <result name="login">/login.jsp</result> <!-- 在action中使用自定义拦截器 --> <interceptor-ref name="myStack" /> </action> </package> </struts>
主页 main.jsp
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>主页</title> </head> <body> <table border="0"> <tr> <td>《SSH框架整合实战教程》</td> <td><a href="/struts2demo03/book_buy">购买</a></td> </tr> </table> </body> </html>
登录页面 login.jsp
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title>登录</title> <style type="text/css"> input[type=text],input[type=password]{width:150px} </style> </head> <body> <div align="center"> <form action="/struts2demo03/login.action" method="post"> <table> <tr> <td><label style="text-align:right;">用戶名:</label></td> <td><input type="text" name="username"></td> <td><span style="color:#F00">${requestScope.msg }</span></td> </tr> <tr> <td><label style="text-align:right;">密 码:</label></td> <td><input type="password" name="password"></td> </tr> <tr> <td align="right" colspan="2"><input type="submit" value="登录"></td> </tr> </table> </form> </div> </body> </html>
操作成功页面 success.jsp
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title>成功页面</title> </head> <body> 尊敬的会员${user.username },您已成功购买商品,祝您购物愉快! </body> </html>
运行截图
Struts2标签库的分类
<s:if>、<s:elseif>、<s:else>标签
语法
<s:if test="表达式1"> 标签体 </s:if> <s:elseif test="表达式2"> 标签体 </s:elseif> <s:else> 标签体 </s:else>
<s:iterator>标签
<s:iterator> 标签主要用于对集合中的数据进行迭代,它可以根据条件遍历集合中的数据。<s:iterator> 标签的属性及相关
如果在 <s:iterator> 标签中指定 status 属性,就可以通过该属性获取迭代过程中的状态信息,如元素数、当前索引值等。通过 status 属性获取信息的方法如表 2 所示(假设其属性值为 st)
案例:
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <%@taglib prefix="s" uri="/struts-tags" %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>控制标签</title> </head> <body> <center> <table border="1px" cellpadding="1"> <s:iterator var="name" value="{'apple','orange','pear','banana'}" status="st"> <s:if test="#st.odd"> <tr style="background-color:white;"> <td><s:property value="name"/></td> </tr> </s:if> <s:else> <tr style="background-color:grey;"> <td><s:property value="name"/></td> </tr> </s:else> </s:iterator> </table> </center> </body> </html>
引入了 Struts2 的标签库,然后在
标签内,使用 <s:iterator> 标签循环输出集合中的,并将通过该标签 status 属性的 odd 方法获取的值作为 <s:if> 和 <s:else> 标签的判断条件,对表格进行控制显示。
status属性:可选属性,该属性在迭代时会产生一个IteratorStatus对象,该对象可以判断当前元素的位置,包含了以下属性方法
int getCount(); 迭代元素个数
int getIndex(); 迭代元素当前索引
boolean getFirst(); 是否为第一个
boolean getEven(); 是否为偶
boolean getLast(); 是否最后一个
bolean getOdd();? ? 是否为奇
了解更多点击
表格的奇数行变为白色,偶数行变为灰色。这是因为在 controlTags.jsp 中使用遍历集合时,通过标签判断其所在索引的奇偶从而改变表格的颜色。
<s:property>标签
<s:property> 标签的作用是输出指定的值,通常输出的是 value 属性指定的值,<s:property> 标签的属性及属性说明如下。
- value:可选属性,指定需要输出的属性值,如果没有指定该属性,则默认输出 ValueStack 栈顶的值(关于值栈内容会在后面教程中进行讲解)。
- id:可选属性,指定该元素的标识。
- default:可选属性,如果要输出的属性值为 null,则显示 default属性的指定值。
- escape:可选属性,指定是否忽略 HTML 代码。默认值是 true,即忽略输出值中的 HTML 代码。
案例:
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <%@taglib prefix="s" uri="/struts-tags" %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>property标签</title> </head> <body> 输出字符串: <s:property value="'this is a string'"/><br/> 输出默认值: <s:property value="" default="default_value"/><br/> 忽略HTML代码: <s:property value="'<h2>www.w3school.com.cn</h2>'" escape="true"/><br/> 不忽略HTML代码: <s:property value="'<h2>www.w3school.com.cn</h2>'" escape="false"/><br/> </body> </html>
<s:a>标签
<s:a> 标签与 HTML 中的 标签类似,主要用于构造 HTML 页面中的超链接
格式:
<s:a href="链接地址"></s:a> <s:a namespace="" action="">www.baidu.com</s:a>
<s:debug>标签
<s:debug> 标签用于输出服务端对象(如 request、application、ValueStack 等)中的信息
<s:debug> 标签只有一个 id 属性,表示 <s:debug> 标签的一个引用,通常不使用该属性。在使用 <s:debug> 标签后,网页中会生成一个 Debug 的链接,单击该链接,网页中将输出各种服务器对象的信息
<s:include>标签
<s:include> 标签用于在当前页面中包含另一个 Web 资源(如 HTML、JSP、Servlet 等)。该标签有两个属性 id 和 value。其中 id 是可选属性,表示该标签的引用;value 是必填属性,用于指定被包含的 Web 资源文件。
在 <s:include> 标签中还可以指定多个 <s:param/> 子标签给被包含的 Web 资源传递请求参数。下面通过案例演示 <s:include> 标签的使用。
被包含页面( file.jsp)
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <%@taglib prefix="s" uri="/struts-tags" %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>property标签</title> </head> <body> <h2>这是被包含页面includefile.jsp</h2><br/> 传递的参数为:<%out.print(request.getParameter("username")); %> </body> </html>
包含页面
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@taglib prefix="s" uri="/struts-tags"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>includeTags</title> </head> <body> <h3>这是包含页面includeTags.jsp</h3><br/> <s:include value="file.jsp"> <s:param name="username" value="'小韩'"/> </s:include> </body> </html>
<s:param>
<s:param> 标签主要用于为其他标签提供参数,通常要与其他标签一起使用。在上一部分使用 <s:include> 标签时,就使用了 <s:param> 标签给被包含的页面传递参数。<s:param> 标签有两种用法,具体如下。
一种用法是通过标签体指定参数值,用法如下所示:
<s:param name="color">red</s:param>
另一种用法是使用 value 属性指定参数值,用法如下所示:
<s:param name="color" value="'red'"/>
上述两种用法的功能一样,不同点在于使用 value 属性设置参数值时,需要添加单引号,而使用标签体设置参数值时,不需要添加单引号。
注意:在使用 value 属性指定参数时,如果不添加单引号,则表示该值为一个引用对象,如果该对象不存在,则为其属性赋值为 null。
主题