Struts2——拦截器

一、基础

参考:https://mp.weixin.qq.com/s/witX9zNQvH7_qOjk8De3IA

1.1 含义

含义:Interceptor。拦截器是Struts的概念,它与过滤器是类似的。可以近似于看作是过滤器。都是AOP思想的实现。

1.2 作用

1、Struts为我们实现了很多的功能(如数据自动封装,文件上传功能)。
2、Struts为我们提供的这些功能都是通过拦截器完成的。
如:

<!-- 数据自动封装通过ParametersInterceptor拦截器。-->
<interceptor name="params" class="com.opensymphony.xwork2.interceptor.ParametersInterceptor"/>
<!-- 文件上传通过FileUploadInterceptor拦截器。-->
<interceptor name="fileUpload" class="org.apache.struts2.interceptor.FileUploadInterceptor"/>

3、拦截器的设计就是基于组件设计的应用!

1.3 struts-default.xml

作用:定义了Struts的所有拦截器(约32个)。在启动服务器的时候会自动装载这个文件,因此我们才可以在Action中使用到Struts为我们提供的功能【数据自动封装、文件上传】。
拦截器栈:存放多个拦截器的引用。为了方便对拦截器的引用,Struts2提供了拦截器栈的定义。
若要引用多个拦截器,则拦截器都放在栈里,在外边引用拦截器栈即可!
注意:Struts2默认执行的是默认拦截器栈,一旦用户有指定执行哪些拦截器,那么默认的拦截器栈就不会被执行!

<interceptor-stack name="defaultStack">
	<interceptor-ref name="exception"/>
	<interceptor-ref name="alias"/>
	<interceptor-ref name="servletConfig"/>
	<interceptor-ref name="i18n"/>
	<interceptor-ref name="prepare"/>
	<interceptor-ref name="chain"/>
	<interceptor-ref name="scopedModelDriven"/>
	<interceptor-ref name="modelDriven"/>
	<interceptor-ref name="fileUpload"/>
	<interceptor-ref name="checkbox"/>
	<interceptor-ref name="multiselect"/>
	<interceptor-ref name="staticParams"/>
	<interceptor-ref name="actionMappingParams"/>
	<interceptor-ref name="params">
		<param name="excludeParams">dojo\..*,^struts\..*,^session\..*,^request\..*,^application\..*,^servlet(Request|Response)\..*,parameters\...*</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-ref name="debugging"/>
</interceptor-stack>

二、自定义拦截器

2.1 编写拦截器类

方式一:实现Interceptor接口。
方式二:继承AbstractInterceptor类。

public class MyInterceptor implements Interceptor {
    @Override
    public void destroy() {
    }
    @Override
    public void init() {
    }
    // intercept()与过滤器中的doFilter()方法相似。
    @Override
    public String intercept(ActionInvocation actionInvocation) throws Exception {
        // 调用invoke()方法,代表着放行执行下一个拦截器,如果没有拦截器了,那么就执行Action的业务代码
        actionInvocation.invoke();
        return null;
    }
}

2.2 在struts.xml中配置

注意:配置了自定义拦截器,则struts默认的拦截器栈是不会执行的。若想使用,则必须声明!

<package name="xxx" extends="struts-default" >
	<!--注册拦截器栈,但是没有被执行->
	<interceptors>
		<!--注册自定义拦截器-->
		<interceptor name="MyInterceptor" class="TestAction"/>
		<!--配置自定义拦截器栈。配置了自定义拦截器,默认拦截器栈就不会被执行,若想使用,则需配置-->
		<interceptor-stack name="mystack">			
			<interceptor-ref name="defalutStack"/><!--引用默认的拦截器栈,一定要放在第一行-->		
			<interceptor-ref name="MyInterceptor"/><!--引用自定义的拦截器-->
		</interceptor-stack>
	</interceptors>
	
	<!--使用执行拦截器栈-->
	<default-interceptor-ref name="mystack"/>
	
	<action name="TestAction" class="TestAction" method="execute">
		<result name="success">/index.jsp</result>
	</action>
</package>

2.3 拦截器的执行顺序

拦截器的执行顺序:
1、服务器开启时,会执行拦截器的init()方法。
2、访问Action时,Action实例被创建。
3、创建完Action实例时,会调用拦截器的interceptor()方法。
4、最后,执行Action的execute()方法
数据自动封装的执行顺序:
1、服务器启动时,加载配置文件的信息。
2、初始化默认的拦截器栈。
3、当用户访问Action时,创建Action的实例。拿到Action具体的信息【成员变量、setter和getter】。
4、执行拦截器具体的内容,根据Action具体的信息,把web端的数据封装到Action上
5、最后在execute()就可以得到封装后的数据了!
在这里插入图片描述

三、拦截器应用案例1

功能:是否已经登录。
需求:
1、当用户登陆成功,跳转到显示用户的JSP页面中。
2、当用户登陆失败,重新返回登陆界面。
3、若用户直接访问显示用户的JSP页面,那么返回到登陆界面
在这里插入图片描述
需求分析:使用过滤器。获取用户的请求URL,判断URL是不是为list.jsp。若是,则返回登陆界面。

3.1 搭建配置环境

3.1.1 导入jar包

导入c3p0开发包:c3p0-0.9.2-pre1.jar
导入mysql开发包:mysql-connector-java-5.17-bin.jar
dbUtils开发包:commons-dbutils-1.2.jar
8个struts2需要用到的开发包:
——commons-fileupload-1.2.2.jar
——commons-io-2.0.1.jar
——commons-lang3-3.1.jar
——freemarker-2.3.19.jar
——javassist-3.11.0.GA.jar
——ognl-3.0.5.jar
——struts2-core-2.3.4.1.jar
——xwork-core-2.3.4.1.jar
导入JSTL开发包:
——jstl.jar
——standard.jar

3.1.2 编写工具类

导入c3p0.xml文件
写数据库连接池工具类
Utils2DB.java

public class Utils2DB {
    private static ComboPooledDataSource comboPooledDataSource = null ;
    static{
    	// 会自动寻找配置文件,节点为mysql的数据库。
    	comboPooledDataSource = new ComboPooledDataSource();
    }
    public static Datasource getDatasource(){
    	return comboPooledDataSource;
    }
    public static Connection getConnection(){
    	//...
    	return conn;
    }
}

创建数据库表,导入数据。

3.2 entity层

public class User {
    private String id ;
    private String username;
    private String cellphone;
    private String email;
    private String password;
}

3.3 DAO层

public class UserDao {
    public User login(User user) {
        try {
            String sql = "SELECT * FROM user WHERE username = ? AND password = ?";
            QueryRunner queryRunner = new QueryRunner(Utils2DB.getDataSource());
            return (User) queryRunner.query(sql, new BeanHandler(User.class), new Object[]{user.getUsername(), user.getPassword()});
        } catch (SQLException e) {
            new RuntimeException("登陆失败了!");
        }
        return null;
    }
    public List<User> getAll() {
        try {
            String sql = "SELECT * FROM user";
            QueryRunner queryRunner = new QueryRunner(Utils2DB.getDataSource());
            return (List<User>) queryRunner.query(sql, new BeanListHandler(User.class));
        } catch (SQLException e) {
            new RuntimeException("登陆失败了!");
        }
        return null;
    }
}

3.4 Service层

public class Service {
    UserDao userDao = new UserDao();
    public User login(User user) {
        return userDao.login(user);

    }
    public List<User> getAll() {
        return userDao.getAll();
    }
}

3.5 JSP页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>登陆页面</title>
</head>
<body>

<form action="${pageContext.request.contextPath}/user_login" method="post">
    <input type="text" name="username"><br>
    <input type="password" name="password"><br>
    <input type="submit" value="登陆"><br>
</form>
</body>
</html>

3.6 Action层

public class UserAction {
    /****************1.封装数据********************/
    private User user;
    // setter/getter方法。
    /***************2.调用Service*******************/
    Service service = new Service();
    //登陆
    public String login() {
        User user = service.login(this.user);
        if (user == null) {
            return "input";
        } else {
            //将user的信息存到Session域对象中
            Map<String, Object> session = ActionContext.getContext().getSession();
            session.put("user", user);       
            return "login";//登陆成功
        }
    }
    //查看user信息
    public String list() {
        List<User> users = service.getAll();//拿到所有用户的信息
        //存到request域对象中
        Map<String, Object> request = ActionContext.getContext().getContextMap();
        request.put("users", users);
        return "list";
    }
}

注册action。
struts.xml

 <package name="xxx" extends="struts-default" >
	<action name="user_*" class="zhongfucheng.action.UserAction" method="{1}" >
		<!--如果登陆成功,重定向到Action中,执行list业务方法-->
		<result name="login" type="redirectAction">user_list</result>
		<!--如果是list,那么跳转到list.jsp页面-->
		<result name="list" >/WEB-INF/list.jsp</result>
	</action>
</package>

3.7 拦截器层

目标:只有用户调用的是login方法时或者已经登陆的情况下,才能跳转到对应的显示页面。
第一步:判断是否请求的login.jsp页面。若是,则放行。若否,则执行第二步。
第二步:判断是否已经登录。若是,则放行。若否,则返回input视图。
MyInterceptor.java

public class MyInterceptor  extends AbstractInterceptor{
    @Override
    public String intercept(ActionInvocation actionInvocation) throws Exception {        
        ActionProxy proxy = actionInvocation.getProxy();//得到正在执行的代理对象        
        String method = proxy.getMethod();//通过代理对象得到正在执行的方法
        //如果方法的名字不是login,那么就让他们返回到login页面上
        if (!method.equals("login")) {            
            Object user = ActionContext.getContext().getSession().get("user");//查看用户是否登陆了
            //如果没有登陆,回到login页面
            if (user == null) {
                return "input";
            } else {                
                return actionInvocation.invoke(); //登陆了,放行。即让它访问具体的用户信息页面
            }
        } else {
            //若访问login方法,则放行。那么就让它执行
            return actionInvocation.invoke();
        }
    }
}

struts.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
        "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
        "http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
    <package name="my" extends="struts-default">
    	
        <interceptors>
            <!--配置自定义的拦截器-->
            <interceptor name="Interceptor1" class="interceptor.MyInterceptor"/>
            <!--配置拦截器栈,把默认的拦截器栈都加载自定义的拦截器栈中-->
            <interceptor-stack name="myStack">
                <interceptor-ref name="Interceptor1"/>
                <interceptor-ref name="defaultStack"/>
            </interceptor-stack>
        </interceptors>
        
        <!--让Struts执行拦截器-->
        <!--【执行拦截器:第一种写法: 当前包下所有的acntion都执行myStack栈】-->
        <default-interceptor-ref name="myStack"/>
        <!--第二种写法: 只是在这一个Action中执行myStack栈
        <interceptor-ref name="defaultStackt"/>
        <interceptor-ref name="loginCheck"/>
        -->
        <!-- 第三种写法:执行用户栈(与第二种写法一样, 只在当前aciton中执行自定义栈) -->
        <!-- <interceptor-ref name="myStack"/>-->
        
        <action name="user_*" class="action.UserAction" method="{1}">
            <!--登陆成功,重定向到Action中,执行list业务方法-->
            <result name="login" type="redirectAction">user_list</result>
            <!--是list,那么跳转到list.jsp页面-->
            <result name="list">/WEB-INF/list.jsp</result>
            <!--是直接访问Action或者没有用户登陆,返回login页面-->
            <result name="input">/login.jsp</result>
        </action>
    </package>
</struts>

四、拦截器应用案例2

目的:防止表单重复提交。

4.1 Servlet方案

1、在Servlet上生成独一无二的token,保存在Session域中,并交给JSP页面。
2、JSP页面在提交表单数据的时候,把token放在隐藏域中…一起带过去给Servlet。
3、Servlet判断用户有没有带token值过来,判断token的值是否和Session的相匹配。
4、如果用户是第一次提交的话,那么就允许用户的请求,接着就把保存在Session中的token值去除。
5、等用户想要再次提交的时候,Servlet发现Session中并没有token了,所以不搭理用户的请求。

4.2 Struts方案

Struts简化4.1中的书写代码步骤,不需要另外写一个组件来生成token值,struts2标签就有这么一个功能。
通过拦截器来实现。

<interceptor name="token" class="org.apache.struts2.interceptor.TokenInterceptor"/>

在这里插入图片描述

4.3 Struts方案代码编写

4.3.1 DAO层

UserDao.java

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import java.sql.SQLException;
import java.util.List;
public class UserDao {
    public void add(User user) {
        try {
            String sql = "INSERT INTO user(id,username,cellphone,password,address) VALUES (?,?,?,?,?)";
            QueryRunner queryRunner = new QueryRunner(Utils2DB.getDataSource());
            queryRunner.update(sql, new Object[]{user.getId(), user.getUsername(), user.getCellphone(), user.getPassword(),user.getAddress()});
        } catch (SQLException e) {
            new RuntimeException("登陆失败了!");
        }
    }
    public User findUser(String id) {
        try {
            String sql = "SELECT * FROM user WHERE id=?";
            QueryRunner queryRunner = new QueryRunner(Utils2DB.getDataSource());
            return (User) queryRunner.query(sql, new BeanHandler(User.class), new Object[]{id});
        } catch (SQLException e) {
            new RuntimeException("登陆失败了!");
        }
        return null;
    }
    public List<User> getAll() {
        try {
            String sql = "SELECT * FROM user";
            QueryRunner queryRunner = new QueryRunner(Utils2DB.getDataSource());
            return (List<User>) queryRunner.query(sql, new BeanListHandler(User.class));
        } catch (SQLException e) {
            new RuntimeException("登陆失败了!");
        }
        return null;
    }
    public void  updateUser(User user) {
        try {
            String sql = "UPDATE user SET username=?,password=?,cellphone=? WHERE id=?";
            QueryRunner queryRunner = new QueryRunner(Utils2DB.getDataSource());
            queryRunner.update(sql, new Object[]{user.getUsername(), user.getPassword(), user.getCellphone(), user.getId()});
        } catch (SQLException e) {
            new RuntimeException("登陆失败了!");
        }
    }
}

4.3.2 service层

UserService.java

import my.utils.WebUtils;
public class Service {
    UserDao userDao = new UserDao();
    public void add(User user) {
        user.setId(WebUtils.makeId());
        user.setAddress("广州");
        userDao.add(user);
    }
    public User findUser(String id) {
        return userDao.findUser(id);
    }
    public List<User> getAll() {
        return userDao.getAll();
    }
    public void  updateUser(User user) {
        userDao.updateUser(user);
    }
}

4.3.3 视图层

在需要提交的表单上使用token标签。
login.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
</head>
<body>
<form action="${pageContext.request.contextPath}/user_register" method="post">
	<s:token></s:token>
    <table border="1">
        <tr>
            <td>用户名:<input type="text" name="username"></td>
        </tr>
        <tr>
            <td> 密码:<input type="password" name="password"></td>
        </tr>
        <tr>
            <td>电话:<input type="text" name="cellphone"></td>
        </tr>
        <tr>
            <td><input type="submit" value="提交"></td>
        </tr>
    </table>
</form>
</body>
</html>

list.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
    <title>列出下载页面</title>
</head>
<body>
<table border="1" align="center">
    <tr>
        <td>用户id</td>
        <td>用户姓名</td>
        <td>用户密码</td>
        <td>用户电话</td>
        <td>操作</td>
    </tr>
    <s:if test="#request.users!=null">
        <c:forEach items="${users}" var="user">
            <tr>
                <td>${user.id}</td>
                <td>${user.username}</td>
                <td>${user.password}</td>
                <td>${user.cellphone}</td>
                <td><a href="${pageContext.request.contextPath}/user_updatePage?id=${user.id}">修改</a></td>
            </tr>
        </c:forEach>
    </s:if>
</table>
</body>
</html>

updatePage.jsp

<form action="${pageContext.request.contextPath}/user_update">
	<s:token></s:token>
    <table border="1">
        <tr>
            <td>用户名<s:textfield name="username"/></td>
        </tr>
        <tr>
            <td>密码 <s:textfield name="password" /></td>
        </tr>
        <tr>
            <td>电话<s:textfield name="cellphone"/></td>
        </tr>
        <s:hidden name="id"/>

        <tr>
            <td><input type="submit" value="修改"></td>
        </tr>
    </table>
</form>

4.3.4 action层

UserAction.java

	//这里一定要实例化
    User user = new User();
    // setter/getter方法
    @Override
    public User getModel() {
        return user;
    }
    /*******调用service********/
    Service service = new Service();
    
    public String register() throws Exception {
        service.add(user);
        //注册成功,就跳转到list()方法,list方法就跳转到查看所有用户页面了!
        return list();
    }
    public String updatePage() throws Exception {
        //得到用户带过来的id,根据id查找对象
       User user222 = service.findUser(user.getId());
        ActionContext.getContext().getValueStack().push(user222);
        return "updatePage";
    }

4.3.5 配置拦截器

token拦截器默认是不会启动的,需要我们手动配置。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
        "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
        "http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
    <constant name="struts.ui.theme" value="simple"/>
    <package name="my" extends="struts-default">
        <action name="user_*" class="my.action.UserAction" method="{1}">
            <interceptor-ref name="defaultStack"/>
            <interceptor-ref name="token">
                <!-- 要拦截的方法! -->
                <param name="includeMethods">register</param>
            </interceptor-ref>
            <!--如果是list,那么就跳转到list的JSP页面-->
            <result name="list"> /list.jsp</result>
            <!--请求跳转到修改页面-->
            <result name="updatePage">/update.jsp</result>
            <!--如果校验成功,跳转到login.jsp页面回显-->
            <result name="success">/login.jsp</result>
            <result name="redirectList" type="redirect">/user_list</result>
        </action>
    </package>
    <include file="config.xml"/>
</struts>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值