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

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 域中存入值;

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 框架的数据封装

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等;

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>

参考资料

原文地址:https://www.cnblogs.com/linkworld/p/7710560.html