Struts2.0 封装请求数据和拦截器介绍

时间:2020-12-04 17:28:25

1. Struts2 框架中使用 Servlet 的 API 来操作数据

1.1 完全解耦合的方式

  1. Struts2 框架中提供了一个 ActionContext 类,该类中提供了一些方法:
    • static ActionContext getContext(): 获取 ActionContext 对象实例;
    • Map<Object> getParameters(): 获取请求参数, 相当于 request.getParameterMap();
    • Map<Object> getSession(): 获取代表 session 域的 Map 集合, 就相当于操作 session 域;
    • Map<Object> getApplication(): 获取代表 application 域的 Map 集合;
    • void put(String key, Object value): 向 request 域中存入值;

Struts2.0 封装请求数据和拦截器介绍

1.2 使用原生 Servlet 的API方式

  1. Struts2 框架提供了一个 ServletActionContext 类,该类中提供了一些静态的方法:
    • static getPageContext()
    • static getRequest()
    • static getResponse()
    • static getServletContext()
// 需求: 使用 Struts2 作为 Web 层完成客户的新增功能
// regist.jsp
<h1>注册</h1>
<form action="{pageContext.request.contextPath}/registAction" method="post">
用户名: <input type="text" name="username"/><br/>
密 码: <input type="password" name="password"/><br/>
<input type="submit" value="注册"/>
</form> // 完全解耦合的方式,操作数据 com.opensymphony.xwork2.ActionContext 包
ActionContext context = ActionContext.getContext();
// 获取到请求参数
Map<String,Object> map = context.getParameters();
// 遍历map, 获取数据
Set<String> keys = map.keySet();
for(String key : keys){
// Object 是数组
String[] val = (String[])map.get(key);
System.out.println(key+":"+Arrays.toString(val));
} // 向 request 域中存入数据
context.put("msg","request域中数据"); // 向 session 域中存入数据
context.getSession().put("msg","session域中数据"); // 向 application 域中存入数据
context.getApplication().put("msg","application域中数据"); // 使用原生 Servlet 的 API 方式 org.apache.struts2.ServletActionContext 包
// 获取 request 对象
HttpServletRequest request = ServletActionContext.getRequest();

2. 结果页面的跳转

2.1 结果页面存在两种方式

1. 全局结果页面
  • 如果 <package> 包中的一些action都返回 success,并且返回的页面都是同一个JSP页面,这样就可以配置全局的结果页面;
  • 全局结果页面针对当前包中的所有 Action, 如果局部还有结果页面,会优先局部的.
<package name="demo" extends="struts-default" namespace="/">

    // 添加图书和删除图书成功后,都会跳转到 suc.jsp 页面
<global-results>
<result>/demo2/suc.jsp</result>
</global-results> // 添加图书
<action name="addBook" class="cn.itcast.demo2.BookAction" method="add"/> // 删除图书
<action name="deleteBook" class="cn.itcast.demo2.BookAction" method="delete"/>
</package>
2. 局部结果页面
  • <result>/demo3/suc.jsp</result>

2.2 结果页面的类型

  1. <result>标签包含两个属性:
    • name: 逻辑视图的名称;
    • type: 跳转的类型; 常见的结果类型,可以从 struts-default.xml中查找;
      • dispatcher: 转发, 默认值;
      • redirect: 重定向;
      • chain: 多个 action 之间跳转, 从一个 Action 转发到另一个 Action;
      • redirectAction: 多个 action 之间跳转,从一个 Action 重定向到另一个 Action;
      • stream: 文件下载时,使用;
// 重定向到 Action
// Action 类的编写
public class Demo3Action extends ActionSupport{ public String save(){
System.out.println("save....");
return SUCCESS;
} public String update(){
System.out.println("update...");
return NONE;
}
} // struts.xml
<action name="demo3Action_*" class="com.itheima.demo.Demo3Action" method="{1}">
// 注意路径的编写方式
<result name="success" type="redirectAction">demo3Action_update</result>
</action>

3. Struts 框架的数据封装

Struts2.0 封装请求数据和拦截器介绍

3.1 概述

  1. Struts2 提供了两类数据封装的方式
    • 属性驱动;
    • 模型驱动(使用较多);

3.2 属性驱动

3.2.1 提供对应属性的 set 方法进行数据的封装(使用较多)
  1. 表单的哪些属性需要封装数据,那么在对应的 Action 类中提供该属性的 set 方法即可;
  2. 表单中提交的数据,最终调用 Action 类中的 setXxx 方法,赋值给全局变量;
  3. 注意点:
    • Struts2 的框架采用拦截器完成数据的封装;
    • 如果属性特别多,需要提供特别多的 set 方法,而且还需要手动将数据存入到对象中;
    • 这种情况下, Action 类就相当于一个 JavaBean. Action类既封装数据,又接收请求数据,耦合性较高,

      没有体现出 MVC 思想;
3.2.2 在页面上,使用 OGNL 表达式进行数据封装
  1. 在页面中使用OGNL表达式进行数据的封装,就可以直接把属性封装到某一个 JavaBean 的对象中;
  2. 在页面中定义一个 JavaBean, 并且提供 set 方法;
  3. 表单的写法: <input type="text" name="user.username"/>
  4. 注意:
    • 只提供一个 set 方法还不够,还需要提供 user 属性的 get 方法;
    • 先调用 get 方法,判断一下是否有 user 对象的实例对象;如果没有,就先创建 user 对象,并封装一个数据,

      然后调用set方法把拦截器创建的对象注入进来; 再调用 getUser 来封装数据;

3.3 模型驱动

  1. 手动实例化 JavaBean, 即: private User user = new User();
  2. 必须实现 ModelDriven 接口,实现 getModel() 的方法, 在 getModel() 方法中返回 user 即可!
// 属性驱动
// regist.jsp
<form action="{pageContext.request.contextPath}/regist.action" method="post">
用户名: <input type="text" name="username"/><br/>
密 码: <input type="password" name="password"/><br/>
<input type="submit" value="注册"/>
</form> // 属性驱动方式一, Action 类中提供对应属性的 set 方法
public class RegistAction extends ActionSupport{ // 定义属性
private String username;
private String password; // 只需要提供对应属性的 set 方法
// 拦截器调用 setXxx 方法,将属性封装,赋值给全局变量 username,password
public void setUsername(String username){
this.username = username;
}
public void setPassword(String password){
this.password = password;
} // regist 方法
public String regist(){ // 输出打印
System.out.println(username+"::"+password);
return NONE;
}
} // 属性驱动方式二, OGNL 表达式
// regist.jsp, 页面中使用 OGNL 表达式
<form action="{pageContext.request.contextPath}/regist.action" method="post"> // 此处 user.username 与 Action 类中的属性 user 一致
用户名: <input type="text" name="user.username"/><br/>
密 码: <input type="password" name="user.password"/><br/>
<input type="submit" value="注册"/>
</form> // 需要提供 JavaBean
// User.class
public class User{
private String username;
private String password; // 提供 get 和 set 方法
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;
} public String toString(){
....
}
} // Action 类
public class RegistAction extends ActionSupport{ // 定义属性
private User user; // 提供 get 和 set 方法
public User getUser(){
return user;
}
public void setUser(User user){
this.user = user;
} public String regist(){
System.out.println(user);
return NONE;
}
} // 模型驱动
// regist.jsp
<form action="{pageContext.request.contextPath}/regist.action" method="post">
用户名: <input type="text" name="username"/><br/>
密 码: <input type="password" name="password"/><br/>
<input type="submit" value="注册"/>
</form> // User.class
同上 // Action 类, 需要实现 ModelDriven 接口
public class RegistAction extends ActionSupport implements ModelDriven<User>{ // 手动实例化
private User user = new User(); // 实现 getModel() 方法
// 即获取模型对象
public User getModel(){
return user;
} public String regist(){
System.out.println(user);
return NONE;
}
}

3.4 Struts2 把数据封装到集合中

  1. 把数据封装到 Collection 中
    • 因为 Collection 接口都会有下标值,所有页面的写法会有一些区别,

      <input type="text" name="products[0].name"/>;
    • 在 Action 类中,需要提供 products 集合,并且提供 get 和 set 方法;
  2. 把数据封装到 Map 中
    • Map 集合是键值对的形式,页面的写法:

      <input type="text" name="map['one'].name"/>
    • Action 类中提供 map 集合,并且提供 get 和 set 方法;
// 向 List 集合中封装数据 (默认情况下, 采用属性驱动的方式)
// regist.jsp
<form action="{pageContext.request.contextPath}/regist.action" method="post">
用户名1: <input type="text" name="list[0].username"/><br/>
密 码1: <input type="password" name="list[0].password"/><br/> 用户名2: <input type="text" name="list[1].username"/><br/>
密 码2: <input type="password" name="list[1].password"/><br/>
<input type="submit" value="注册"/>
</form> // User.class
同上 // Action 类
public class RegistAction extends ActionSupport{ // 定义属性
private List<User> list; // 提供 get 和 set 方法
public List<User> getList(){
return list;
}
public void setList(List<User> list){
this.list = list;
} public String regist(){
for(User user : list){
System.out.println(user);
}
return NONE;
}
} // 向 Map 集合中封装数据
// regist.jsp
<form action="{pageContext.request.contextPath}/regist.action" method="post">
用户名1: <input type="text" name="map['one'].username"/><br/>
密 码1: <input type="password" name="map['one'].password"/><br/> 用户名2: <input type="text" name="map['two'].username"/><br/>
密 码2: <input type="password" name="map['two'].password"/><br/>
<input type="submit" value="注册"/>
</form> // User.class
同上 // Action 类
public class RegistAction extends ActionSupport{ // 定义属性
private Map<String,User> map; // 提供 get 和 set 方法
public Map<String,User> getMap(){
return map;
}
public void setMap(Map<String,User> map){
this.map = map;
} public String regist(){
System.out.println(map);
return NONE;
}
}

4. 拦截器技术

4.1 拦截器的概述

  1. 拦截器就是 AOP(Aspect-Oriented Programming)的一种实现. AOP 是指用于在某个方法或字段被访问之前,

    进行拦截,然后在之前或之后加入某些操作;
  2. 拦截器采用"责任链"模式
    • 在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链;
    • 责任链中每一个节点,都可以继续调用下一个节点,也可以组织流程继续执行;
  3. Struts2 中可以定义很多个拦截器,将多个拦截器按照特定顺序组成拦截器栈.

4.2 拦截器和过滤器的区别

  1. 拦截器是基于 java 反射机制的,而过滤器是基于函数回调的;
  2. 过滤器依赖于 Servlet 容器, 而拦截器不依赖于 Servlet 容器;
  3. 拦截器只能对 Action 请求起作用(即Action 中的方法),

    而过滤器是用于过滤从客户端发送到服务端请求的,其中包括CSS, JSP, JS等;

Struts2.0 封装请求数据和拦截器介绍

4.3 自定义拦截器和配置

4.3.1 自定义拦截器
  • 编写拦截器需要实现 interceptor 接口,实现接口中的三个方法;
  • 也可以继承 interceptor 接口的已知实现类 AbstractInterceptor;
  • 或者继承 interceptor 接口的已知实现类 MethodFilterInterceptor, 可以对指定方法放行;
// 编写拦截器
public String DemoInterceptor extends AbstractInterceptor { // intercept 方法: 在 action 执行之前,拦截
public String intercept(ActionInvocation invocation) throws Exception{ System.out.println("拦截器执行之前....."); // 执行下一个拦截器
String result = invocation.invoke(); System.out.println("拦截器执行之后....");
return result;
}
}
4.3.2 在 struts.xml 中配置拦截器
// 第一种方式:
// 在 <package> 包中定义拦截器,出现在 <package> 包的上方
<interceptors>
<interceptor name="loginInterceptor" class="cn.itcast.interceptor.LoginInterceptor"/>
</interceptors> // 在某个 action 中引入拦截器
<interceptor-ref name="loginInterceptor"/> // 注意:
// 如果引入了自定义的拦截器,Struts2 框架默认的拦截器就不会再执行了,所以需要引入 Struts2 默认的拦截器
<interceptor-ref name="defaultStack"/> // 第二种方式
// 在 <package> 包中定义拦截器的时候,自己直接定义一个拦截器栈
<interceptors>
<interceptor name="loginInterceptor" class="cn.itcast.interceptor.LoginInterceptor"/>
<interceptor-stack name="myStack">
<interceptor-ref name="loginInterceptor"/>
<interceptor-ref name="defaultStack"/>
</interceptor-stack>
</interceptors> // 在 action 中引入自定义拦截器栈
<action name="book_*" class="cn.itcast.action.BookAction" method="{1}">
<interceptor-ref name="myStack"/>
</action>

参考资料