草庐IT

day13-自定义拦截器

liyuelian 2023-03-28 原文

自定义拦截器

1.什么是拦截器

说明:

拦截器与过滤器的区别

SpringMVC 的拦截器(Interceptor)与 Java Servlet 的过滤器(Filter)类似,它主要用于拦截用户的请求并做相应的处理,通常应用在权限验证、记录请求信息的日志、判断用户是否登录等功能上。

  1. SpringMVC 也可以使用拦截器对请求进行拦截处理,用户可以自定义拦截器来实现特定的功能

  2. 自定义的拦截器必须实现 HandlerInterceptor 接口

  3. 自定义拦截器的三个方法:

(1)preHandle():该方法在业务处理器处理请求之前被调用,在该方法中对用户请求 request 进行处理

(2)postHandle():该方法在目标方法处理完请求之后执行

(3)afterCompletion():该方法在完全处理完请求之后被调用,可以在该方法中进行一些资源清理的操作

2.自定义拦截器执行流程分析图

● 自定义拦截器执行流程说明

拦截器中方法的执行顺序是 preHandle -> Controller -> postHandle -> afterCompletion,只有preHandle返回true,才会执行后面的方法

  1. 如果 preHandle 方法返回 false, 则不再执行目标方法及之后的拦截方法,可以在该方法指定返回页面
  2. postHandle 在目标方法被执行后执行,可以在 postHandle 方法中访问到目标方法返回的 ModelAndView 对象
  3. 若 preHandle 返回 true, 则 afterCompletion 方法在渲染视图之后被执行
  4. 若 preHandle 返回 false, 则 afterCompletion 方法不会被调用
  5. 配置拦截器时可以指定该拦截器对哪些请求生效,哪些请求不生效,如果不指定则默认对所有目标方法生效

3.应用实例

3.1快速入门

完成一个自定义拦截器,学习如何配置拦截器和拦截器的运行流程。

(1)创建拦截器 MyInterceptor01.java

package com.li.web.interceptor;

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @author 李
 * @version 1.0
 */
//需要标识为component注入到spring容器中
@Component
public class MyInterceptor01 implements HandlerInterceptor {
    /**
     * 1.preHandle 方法在目标方法执行前被执行
     * 2.如果 preHandle 方法返回了false,则目标方法不再被执行
     * 3.preHandle方法可以获取到 request,response,handler
     * 4.如果该方法返回了 false,你可以指定跳转到哪个页面
     * @param request
     * @param response
     * @param handler 要执行的处理器
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
                             Object handler) throws Exception {
        System.out.println("===MyInterceptor01--preHandle()===");
        return true;
    }

    /**
     * 1.在目标方法执行后,会执行 postHandle 方法
     * 2.postHandle 方法可以获取到目标方法返回的 modelAndView
     * @param request
     * @param response
     * @param handler
     * @param modelAndView
     * @throws Exception
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response,
                           Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("===MyInterceptor01--postHandle()===");
    }

    /**
     * 1.afterCompletion 方法在视图渲染后被执行
     * 2.在 afterCompletion 方法中可以进行一些资源清理工作
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("===MyInterceptor01--afterCompletion()===");
    }
}

(2)在 spring 的容器文件中配置拦截器

<!--配置自定义拦截器-->
<mvc:interceptors>
    <!--
     1.第一种配置方式
     2.直接使用ref引用到对应的 myInterceptor01
     3.这种方式会拦截所有的目标方法
    -->
    <ref bean="myInterceptor01"/>
</mvc:interceptors>

(3)测试 Controller

package com.li.web.interceptor;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * @author 李
 * @version 1.0
 */
@Controller
public class TestHandler {
    @RequestMapping(value = "/hi")
    public String hi() {
        System.out.println("===TestHandler--hi()===");
        return "success";
    }

    @RequestMapping(value = "/hello")
    public String hello() {
        System.out.println("===TestHandler--hello()===");
        return "success";
    }
}

(4)interceptor.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>测试自定义拦截器</title>
</head>
<body>
<h1>测试自定义拦截器</h1>
<a href="<%=request.getContextPath()%>/hi">测试自定义拦截器-hi</a><br/><br/>
<a href="<%=request.getContextPath()%>/hello">测试自定义拦截器-hello</a>
</body>
</html>

(5)启动 tomcat,访问 interceptor.jsp,分别点击两个超链接

后台输出如下:

可以看到方法的执行顺序为:preHandler-->目标方法-->postHandle-->afterCompletion,并且拦截器对Controller 所有的方法都进行了拦截。

3.2注意事项和使用细节

  1. 默认配置是对所有的目标方法都进行拦截,也可以指定拦截某个目标方法,如在 spring 容器文件中配置:

    <!--配置自定义拦截器-->
    <mvc:interceptors>
        <!--
        1.第二种配置方式
        2.mvc:mapping path="/hi" 指定要拦截的目标方法的路径
        3.ref bean="myInterceptor01" 指定对哪个拦截器进行配置
        -->
        <mvc:interceptor>
            <mvc:mapping path="/hi"/>
            <ref bean="myInterceptor01"/>
        </mvc:interceptor>
    </mvc:interceptors>
    
  2. mvc:mapping 支持通配符,同时可以指定不对哪些目标方法进行拦截,例如:

    <!--配置自定义拦截器-->
    <mvc:interceptors>
        <!--
        1.第三种配置方式
        2.mvc:mapping path="/h*" 表示拦截 /h 开头的路径的目标方法
        3.mvc:exclude-mapping path="/hello" 表示不拦截指定路径的目标方法
        4.ref bean="myInterceptor01" 指定对哪个拦截器进行配置
        -->
        <mvc:interceptor>
            <!--h开头的目标方法都被拦截-->
            <mvc:mapping path="/h*"/>
            <mvc:exclude-mapping path="/hello"/>
            <ref bean="myInterceptor01"/>
        </mvc:interceptor>
    </mvc:interceptors>
    
  3. 拦截器需要配置才生效,不配置是不生效的

  4. 如果 preHandle() 方法返回了 false,就不会执行目标方法(前提是目标方法指定了拦截),你可以在该方法中根据业务指定要跳转的页面

3.3Debug执行流程

(1)在自定义拦截器 MyInterceptor01 的 preHandle 方法中打上断点,点击 debug

(2)浏览器访问页面 interceptor.jsp,点击第一个链接,访问目标方法 hi()

(3)后台光标跳转到断点处,此时 preHandle 方法已经拿到了目标方法,说明 preHandle 方法在目标方法前执行

(4)在目标方法 hi 中添加断点,点击 resume,光标跳转到断点处

(5)在拦截器的 postHandle 方法中添加断点,点击 resume,光标如期跳转到该断点处。在该方法中可以拿到目标方法对应的 ModelAndView 对象,其中 view 就是指定的要返回的页面,model 就是目标方法的数据。

(6)ModelAndView 对象在 DispatcherServlet 的 resolveViewName 方法进行解析,返回视图,视图在 render 方法中进行视图渲染

(7)在拦截器的 afterCompletion 方法中打上断点,点击 resume,光标跳转到该断点处

(8)这一个拦截流程就结束了,再次点击 resume,就会向客户端返回数据

4.多个拦截器

4.1多个拦截器执行流程示意图

4.2实例演示1-执行流程

4.2.1代码实现

(1)在3.1中快速入门中已经创建了一个拦截器,现在创建第二个拦截器 MyInterceptor02.java

package com.li.web.interceptor;

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @author 李
 * @version 1.0
 */
@Component
public class MyInterceptor02 implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("===MyInterceptor02--preHandle()===");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("===MyInterceptor02--postHandle()===");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("===MyInterceptor02--afterCompletion()===");
    }
}

(2)在 spring 的容器文件中配置拦截器

<!--配置自定义拦截器-->
<mvc:interceptors>
    <mvc:interceptor>
        <!--h开头的目标方法都被拦截-->
        <mvc:mapping path="/h*"/>
        <mvc:exclude-mapping path="/hello"/>
        <ref bean="myInterceptor01"/>
    </mvc:interceptor>
    <!--
    第二个拦截器
    多个拦截器在执行时,按照配置的顺序执行
    -->
    <mvc:interceptor>
        <mvc:mapping path="/h*"/>
        <ref bean="myInterceptor02"/>
    </mvc:interceptor>
</mvc:interceptors>

(3)interceptor.jsp 不变

(4)完成测试,浏览器访问 jsp 页面,点击访问目标方法 hi

(5)后端输出如下:与分析的流程一致。

(6)如果把第二个拦截器的 preHandle 方法返回值改为 false,重新访问目标方法测试,后台输出如下:

4.2.2注意事项和使用细节

  1. 如果第一个拦截器的 preHandle() 返回 false,后面都不再执行,包括目标方法

  2. 如果第二个拦截器的 preHandle() 返回 false,就直接执行第一个拦截器的 afterCompletion() 方法,如果拦截器更多,规则类似。

  3. 以上两条规则,都是在目标方法符合被拦截条件的前提下。

4.3实例演示2-跳转至指定页面

需求:如果用户提交的数据有禁用词(比如:病毒),则在第一个拦截器就返回,不执行目标方法,跳转至执行页面

4.3.1代码实现

(1)修改 MyInterceptor01.java 的 preHandle 方法

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
                         Object handler) throws Exception {
    System.out.println("===MyInterceptor01--preHandle()===");
    //获取用户提交的关键字
    String keyword = request.getParameter("keyword");
    if ("病毒".equals(keyword)) {
        //请求转发到指定页面
        request.getRequestDispatcher("/WEB-INF/pages/warning.jsp")
                .forward(request, response);
        return false;
    }
    System.out.println("keyword=" + keyword);
    return true;
}

(2)warning.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>警告</title>
</head>
<body>
<h1>你的信息中含有违规词</h1>
</body>
</html>

(3)在 postman 中进行测试

测试1:无违禁词

后台输出:

测试2:含违禁词

后台输出:

5.练习

  1. 将之前的 SpringMVC 文件上传、自定义拦截器相关代码和案例过一遍
  2. 简述 SpringMVC 自定义拦截器工作流程,并画出示意图
  3. debug 自定义拦截器源码,加深理,梳理流程

有关day13-自定义拦截器的更多相关文章

  1. ruby - Facter::Util::Uptime:Module 的未定义方法 get_uptime (NoMethodError) - 2

    我正在尝试设置一个puppet节点,但ruby​​gems似乎不正常。如果我通过它自己的二进制文件(/usr/lib/ruby/gems/1.8/gems/facter-1.5.8/bin/facter)在cli上运行facter,它工作正常,但如果我通过由ruby​​gems(/usr/bin/facter)安装的二进制文件,它抛出:/usr/lib/ruby/1.8/facter/uptime.rb:11:undefinedmethod`get_uptime'forFacter::Util::Uptime:Module(NoMethodError)from/usr/lib/ruby

  2. ruby-on-rails - Rails 3.2.1 中 ActionMailer 中的未定义方法 'default_content_type=' - 2

    我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer

  3. ruby-on-rails - form_for 中不在模型中的自定义字段 - 2

    我想向我的Controller传递一个参数,它是一个简单的复选框,但我不知道如何在模型的form_for中引入它,这是我的观点:{:id=>'go_finance'}do|f|%>Transferirde:para:Entrada:"input",:placeholder=>"Quantofoiganho?"%>Saída:"output",:placeholder=>"Quantofoigasto?"%>Nota:我想做一个额外的复选框,但我该怎么做,模型中没有一个对象,而是一个要检查的对象,以便在Controller中创建一个ifelse,如果没有检查,请帮助我,非常感谢,谢谢

  4. ruby - 主要 :Object when running build from sublime 的未定义方法 `require_relative' - 2

    我已经从我的命令行中获得了一切,所以我可以运行rubymyfile并且它可以正常工作。但是当我尝试从sublime中运行它时,我得到了undefinedmethod`require_relative'formain:Object有人知道我的sublime设置中缺少什么吗?我正在使用OSX并安装了rvm。 最佳答案 或者,您可以只使用“require”,它应该可以正常工作。我认为“require_relative”仅适用于ruby​​1.9+ 关于ruby-主要:Objectwhenrun

  5. ruby - 在 Ruby 中有条件地定义函数 - 2

    我有一些代码在几个不同的位置之一运行:作为具有调试输出的命令行工具,作为不接受任何输出的更大程序的一部分,以及在Rails环境中。有时我需要根据代码的位置对代码进行细微的更改,我意识到以下样式似乎可行:print"Testingnestedfunctionsdefined\n"CLI=trueifCLIdeftest_printprint"CommandLineVersion\n"endelsedeftest_printprint"ReleaseVersion\n"endendtest_print()这导致:TestingnestedfunctionsdefinedCommandLin

  6. ruby - 定义方法参数的条件 - 2

    我有一个只接受一个参数的方法:defmy_method(number)end如果使用number调用方法,我该如何引发错误??通常,我如何定义方法参数的条件?比如我想在调用的时候报错:my_method(1) 最佳答案 您可以添加guard在函数的开头,如果参数无效则引发异常。例如:defmy_method(number)failArgumentError,"Inputshouldbegreaterthanorequalto2"ifnumbereputse.messageend#=>Inputshouldbegreaterthano

  7. ruby - 如何在 Grape 中定义哈希数组? - 2

    我使用Ember作为我的前端和GrapeAPI来为我的API提供服务。前端发送类似:{"service"=>{"name"=>"Name","duration"=>"30","user"=>nil,"organization"=>"org","category"=>nil,"description"=>"description","disabled"=>true,"color"=>nil,"availabilities"=>[{"day"=>"Saturday","enabled"=>false,"timeSlots"=>[{"startAt"=>"09:00AM","endAt"=>

  8. ruby - 获取模块中定义的所有常量的值 - 2

    我想获取模块中定义的所有常量的值:moduleLettersA='apple'.freezeB='boy'.freezeendconstants给了我常量的名字:Letters.constants(false)#=>[:A,:B]如何获取它们的值的数组,即["apple","boy"]? 最佳答案 为了做到这一点,请使用mapLetters.constants(false).map&Letters.method(:const_get)这将返回["a","b"]第二种方式:Letters.constants(false).map{|c

  9. ruby - 这两个 Ruby 类初始化定义有什么区别? - 2

    我正在阅读一本关于Ruby的书,作者在编写类初始化定义时使用的形式与他在本书前几节中使用的形式略有不同。它看起来像这样:classTicketattr_accessor:venue,:datedefinitialize(venue,date)self.venue=venueself.date=dateendend在本书的前几节中,它的定义如下:classTicketattr_accessor:venue,:datedefinitialize(venue,date)@venue=venue@date=dateendend在第一个示例中使用setter方法与在第二个示例中使用实例变量之间是

  10. ruby-on-rails - 如何生成传递一些自定义参数的 `link_to` URL? - 2

    我正在使用RubyonRails3.0.9,我想生成一个传递一些自定义参数的link_toURL。也就是说,有一个articles_path(www.my_web_site_name.com/articles)我想生成如下内容:link_to'Samplelinktitle',...#HereIshouldimplementthecode#=>'http://www.my_web_site_name.com/articles?param1=value1¶m2=value2&...我如何编写link_to语句“alàRubyonRailsWay”以实现该目的?如果我想通过传递一些

随机推荐