过滤器(Filter)和 拦截器(Interceptor)_过滤器和拦截器-程序员宅基地

技术标签: Powered by 金山文档  servlet  

一、引言

  在开发过程中我们经常会遇到 过滤器(Filter) 和 拦截器(Interceptor),找个也是面试中容易被问道的一个问题,两者的使用和作用又颇为相似,是个容易被大家混淆的问题,在此总结下,希望能对大家有所帮助。

二、Filter 过滤器介绍

  1. Filter定义

  Filter是sun公司中servlet2.3后增加的一个新功能,在javaEE中定义了一个接口 javax.servlet.Filter来描述过滤器。

  Filter可以认为是Servlet的一种“加强版”,它主要用于对用户请求进行预处理,也可以对HttpServletResponse进行后处理,是个典型的处理链。Servlet中的过滤器Filter是实现了javax.servlet.Filter接口的服务器端程序.它依赖于servlet容器,在实现上,基于函数回调,它可以对几乎所有请求进行过滤。Filter也可以对用户请求生成响应,这一点与Servlet相同,但实际上很少会使用Filter向用户请求生成响应。

  它是随你的web应用启动而启动的,只初始化一次,以后就可以拦截相关请求,只有当你的web应用停止或重新部署的时候才销毁。

  1. Filter工作原理

  Servlet中的过滤器Filter是实现了javax.servlet.Filter接口的服务器端程序,其工作原理是,只要你在web.xml文件配置好要拦截的客户端请求,它都会帮你拦截到请求,此时你就可以对请求或响应(Request、Response)统一处理等。

  使用Filter完整的流程是:Filter对用户请求进行预处理,接着将请求交给Servlet进行处理并生成响应,最后Filter再对服务器响应进行后处理。

  当服务器启动,会创建Filter对象,并调用init方法,只调用一次。

  当访问资源,路径与Filter的拦截路径匹配,会执行Filter中的doFilter方法,

  当服务器关闭时,会调用Filter的destroy方法来进行销毁操作。

  1. Filter应用场景

  在过滤器中修改字符编码(CharacterEncodingFilter)、在过滤器中修改HttpServletRequest的一些参数(XSSFilter(自定义过滤器)),如:过滤低俗文字、危险字符、敏感词过滤、响应信息压缩、控制权限、控制转向、做一些业务逻辑判断等

  1. Filter代码实战

public class TestFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        //对filter进行一个初始化
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
        //doFilter()前的代码是对用户请求进行预处理
        System.out.println("我是Filter,执行filterChain.doFilter之前。");
        //执行Filter链中的下一个Filter
        filterChain.doFilter(request, response);
        //doFilter()后的代码是对用户请求进行后处理
        System.out.println("我是Filter,执行filterChain.doFilter之后。");
    }

    @Override
    public void destroy() {
        //对filter进行一个销毁
    }
}

配置方式1 :在web-xml 中进行配置

<filter>
    <description>测试过滤器</description>
    <filter-name>TestFilter</filter-name>
    <filter-class>com.test.filtes.TestFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>TestFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping> 

配置方式2:通过@WebFilter注解配置

@WebFilter(urlPatterns = "/test001")
public class TestFilter1 implements Filter {
    @Override
    public void init(javax.servlet.FilterConfig filterConfig) throws ServletException {
        System.out.println("TestFilter1 init");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        //在DispatcherServlet之前执行
        System.out.println("doFilter before");
        filterChain.doFilter(servletRequest, servletResponse);
        // 在视图页面返回给客户端之前执行,但是执行顺序在Interceptor之后
        System.out.println("doFilter after");
    }

    @Override
    public void destroy() {
        System.out.println("TestFilter destroy");
    }
}
//2.在启动类添加@ServletComponentScan

@SpringBootApplication
@ServletComponentScan
public class TestbootApplication {

    public static void main(String[] args) {
        SpringApplication.run(TestbootApplication.class, args);
    }
}

PS: 多个filter 实现@WebFilter,难以控制filter 的过滤顺序,与其包名和文件名有关,慎用。

配置方式3 :通过@Bean来配置

//1.初始化Filter
@Component
public class TestFilter2 implements Filter{
    @Override
    public void init(javax.servlet.FilterConfig filterConfig) throws ServletException {
        System.out.println("Filter2 init");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        //在DispatcherServlet之前执行
        System.out.println("doFilter2 before");
        filterChain.doFilter(servletRequest, servletResponse);
        // 在视图页面返回给客户端之前执行,但是执行顺序在Interceptor之后
        System.out.println("doFilter2 after");
    }

    @Override
    public void destroy() {
        System.out.println("Filter2 destroy");
    }
}
@Component
public class TestFilter3 implements Filter{
    @Override
    public void init(javax.servlet.FilterConfig filterConfig) throws ServletException {
        System.out.println("Filter3 init");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        //在DispatcherServlet之前执行
        System.out.println("doFilter3 before");
        filterChain.doFilter(servletRequest, servletResponse);
        // 在视图页面返回给客户端之前执行,但是执行顺序在Interceptor之后
        System.out.println("doFilter3 after");
    }

    @Override
    public void destroy() {
        System.out.println("Filter3 destroy");
    }
}

//2.注册到config
@Configuration
public class FilterConfig {

    @Bean
    public FilterRegistrationBean testFilter3RegistrationBean() {
        FilterRegistrationBean registration = new FilterRegistrationBean(new TestFilter2());
        registration.addUrlPatterns("/hello world");
        registration.setOrder(1); // 值越小越靠前,此处配置有效
        return registration;
    }

    @Bean
    public FilterRegistrationBean testFilter4RegistrationBean() {
        FilterRegistrationBean registration = new FilterRegistrationBean(new TestFilter3());
        registration.addUrlPatterns("/hello world");
        registration.setOrder(2);
        return registration;
    }
}

三、Interceptor 拦截器介绍(只针对spring介绍)

  1. Interceptor 定义

  拦截器(Interceptor)和Servlet无关,它依赖于Web框架,在SpringMVC中就依赖于SpringMVC框架,由SpringMVC框架实现。在Struts2中同理,它是一种可以让你在Action执行之前和Result执行之后进行一些功能处理的机制。

  在实现上,基于Java的反射机制,属于面向切面编程(AOP)的一种运用。可以用于在某个方法或者字段被访问之前,进行拦截,然后在之前或者之后加入某些统一的处理方法。就是在action的前、后、甚至抛出异常时进行处理,比如动态代理就是拦截器的简单实现。

  拦截器将很多service或者Controller中共有的行为提炼出来,在某些方法执行的前后执行,提炼为通用的处理方式,让被拦截的方法都能享受这一共有的功能,让代码更加简洁,同时,当共有的功能需要发生调整、变动的时候,不必修改很多的类或者方法,只要修改这个拦截器就可以了,可复用性很强。

  SpringMVC的拦截器基于HandlerInterceptor接口来实现,所以只要实现HandlerInterceptor接口或者继承它的实现类。

  1. Interceptor 工作原理

拦截器的实现分为两步:

第一步,创建一个普通的拦截器,实现 HandlerInterceptor 接口,并重写接口中的相关方法;

第二步,将上一步创建的拦截器加入到 Spring Boot 的配置文件中。

preHandler(HttpServletRequest request, HttpServletResponse response, Object handler) 方法在请求处理之前被调用。如果已经是最后一个 Interceptor 的时候就会调用当前请求的 Controller 方法。

postHandler(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) 方法在当前请求处理完成之后,所以我们可以在这个方法中对 Controller 处理之后的 ModelAndView 对象进行操作。

afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handle, Exception ex) 方法需要在当前对应的 Interceptor 类的 preHandle 方法返回值为 true 时才会执行。顾名思义,该方法将在整个请求结束之后,也就是在 DispatcherServlet 渲染了对应的视图之后执行。此方法主要用来进行资源清理

  1. Interceptor 应用场景

  1. 日志记录:记录请求操作日志(用户ip,访问时间等)

  1. 权限检查:判断用户是否有权限访问资源,如校验token 日志记录

  1. 性能监控:记录请求->响应时间,preHandle:记录开始时间,afterCompletion:记录结束时间,开始时间相减去结束时间

  1. 登录验证:判断用户是否登录

  1. sign校验,封禁校验等

  1. 处理cookie,主题,国际化,本地化等

  1. filter可以实现的功能intercepter基本上都能实现

  1. Interceptor 代码实战

配置方式1 :在springmvc.xml 中进行配置

public class BaseInterceptor implements HandlerInterceptor{
    public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1,
                      Object arg2) throws Exception {
    System.out.println("Before...");
    System.out.println("返回true继续执行,返回false则结束请求");
    return true;
    }
 
    public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1,
                      Object arg2, ModelAndView arg3) throws Exception {
    System.out.println("渲染试图前,action执行后...");
    }
 
    public void afterCompletion(HttpServletRequest arg0, HttpServletResponse
                      arg1, Object arg2, Exception arg3)
    System.out.println("整个请求结束之后,进行资源清理等");
}
<mvc:interceptors>
    <!-- 如果直接配置bean,代表对所有请求都拦截 -->
    <bean name="baseInterceptor" class="com.test.interceptor.BaseInterceptor" />
    <mvc:interceptor>
           <!-- /**:拦截所有,/*拦截一层目录结构请求 -->
        <mvc:mapping path="/**"/>
        <!-- 不拦截的请求 -->
        <mvc:exclude-mapping path="/testRequestEntity"/>
        <!-- 自定义的拦截器引用 -->
        <ref bean="baseInterceptor"></ref>
    </mvc:interceptor>
<mvc:interceptors/>

配置方式2 :WebConfig将拦截器注册添加到拦截器链

/**
 * 自定义拦截器
 */
@Component
public class MyInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        // 统一拦截(查询token)
        String token = request.getHeader("token");
        if(StringUtils.isEmpty(token)){
            // return  false;
        }

        System.out.println("preHandle方法在控制器的处理请求方法调用之后,解析视图之前执行");
        return true; // 返回 true: 正常执行   false:该请求停止向下运行
    }

    @Override
    public  void  postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle方法在控制器的处理请求方法调用之后,解析视图之前执行");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
        System.out.println("Time Taken afterCompletion ");

    }

}
@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(myInterceptor()).addPathPatterns("/**");
        // .excludePathPatterns("/user/get")/.addPathPatterns(). 可以排除和指定设置
    }

    @Bean
    public MyInterceptor myInterceptor() {
        return new MyInterceptor();
    }
}

四、过滤器(Filter)和拦截器(Interceptor) 、servlet、controller的执行顺序

五、过滤器(Filter)和拦截器(Interceptor)区别

过滤器(Filter)和拦截器(Interceptor)区别

过滤器(Filter)

拦截器(Interceptor)

总结

定义位置

Filter定义在java.servlet包下

接口HandlerInterceptor定义在org.springframework.web.servlet包下

配置位置

配置在web.xml中

配置在springmvc.xml中

作用位置

Filter在只在 Servlet 前后起作用,Filter 通常不考虑servlet 的实现

拦截器能够深入到方法前后、异常抛出前后等,因此拦截器的使用具有更大的弹性。允许用户介入(hook into)请求的生命周期

在Spring构架的程序中,要优先使用拦截器。几乎所有 Filter 能够做的事情, interceptor 都能够轻松的实现

使用范围

Filter 是 Servlet 规范规定的

而拦截器既可以用于Web程序,也可以用于Application、Swing程序中。

遵循规范

Filter 是遵循 Servlet 规范

而拦截器是在 Spring容器内的,是Spring框架支持的。

与spring关系

Filter 不能够使用 Spring 容器资源

Interceptor 是被 Spring 调用

Spring 中使用 interceptor 更容易

调用方

Filter 是被 Server(like Tomcat) 调用

Interceptor 是被 Spring 调用

因此 Filter 总是优先于 Interceptor 执行

实现方式

Filter 基于函数回掉

Interceptor基于java反射

更多消息资讯,请访问昂焱数据。https://www.ayshuju.com/home

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/tszc95/article/details/129250183

智能推荐

银河麒麟V10操作系统之root密码重置_银河麒麟root默认密码-程序员宅基地

文章浏览阅读3w次,点赞16次,收藏96次。从kingbase工程师那拷贝了一个已经安装了kingbase数据库环境的虚拟机,只有一个kingbase普通账户,root密码位置,且该账户未加入到sudo组中,无法执行新软件等的安装和部署。为了满足需要,我们需要重置root密码。_银河麒麟root默认密码

Opencv3安装踩坑(SLAM十四讲)_/usr/include/opencv-程序员宅基地

文章浏览阅读5.5k次,点赞3次,收藏57次。因为之前玩ros,使用了sudo apt-get install libopencv-dev 安装了opencv2,而slam十四讲的opencv版本为3。因此需要重新安装opencv3(在与opencv2共存的条件下)首先检查已有的opencv版本:pkg-config opencv --modversion如果是2版本,则需要安装3版本1. 安装过程1.安装依赖(如果有依赖则不必..._/usr/include/opencv

【Spring6】| Bean的生命周期(五步、七步、十步法剖析)-程序员宅基地

文章浏览阅读8.4k次,点赞34次,收藏148次。【Spring6】| Bean的生命周期(五步、七步、十步法剖析)_bean的生命周期

Appium(六)Toast 定位 + Android版本问题的解决以及 Selenium 的安装+屏幕截图_you could still use other supported backends in or-程序员宅基地

文章浏览阅读2.8k次。页面的错误提示 Toast,比如:网络加载问题出现的错误、用户名或者密码输入错误、银行卡密码输入次数提示等错误,这些错误的内容一般都会以浮动的方式显示,而且他们显示的时间非常的有限,可能是几秒钟的时间就会消失掉,不会被点击,无法获取到他们的焦点,但是在 Android 中有一个叫做 Appium Toast 的工具,在 Android 1.6.x 多的时候就已经开始支持识别 Toas..._you could still use other supported backends in order to automate older andr

关于idea将模块修改为Resources报错:Two modules in a project cannot share the same content root.-程序员宅基地

文章浏览阅读1.8k次。点击: file-->Project Structure-->选择:Models----> 右击文件点击Delete最后修改即可_two modules in a project cannot share the same content root

异步FIFO设计-程序员宅基地

文章浏览阅读590次,点赞26次,收藏26次。同步后的写指针与读指针进行比较,如果它们相等或满足其他预定的条件,就表明FIFO为空,从而产生空逻辑信号。产生空状态信号时,实际FIFO中有数据,相当于提前判断了空状态信号,此时不再进行读FIFO数据操作也是安全的。此时经常使用多余的1bit分别当做读写地址的拓展位,来区分读写地址相同的时候,FIFO的状态是空还是满状态。(格雷码是一种二进制编码方式,其相邻的两个数值只有一个位的差异,这种特性使得格雷码在变化时只涉及到一个位的翻转,从而减少了由于多位同时变化可能带来的不稳定性和错误。

随便推点

MatconvNet:尝试将 SCRIPT vl_nnconv 作为函数执行_尝试将 script vl_nnconv 作为函数执行:-程序员宅基地

文章浏览阅读5k次,点赞7次,收藏7次。运行matconvnet遇到下面问题:尝试将 SCRIPT vl_nnconv 作为函数执行:L:\Fine-tuning-CNN\matlab\cnnimageretrieval\matlab\vl_nnconv.m出错 dagnn.Conv/forward (line 11)outputs{1} = vl_nnconv(...出错 dagnn.Layer/forwardAdvanced (line 85)outputs = obj.forward(inputs, {net.params(_尝试将 script vl_nnconv 作为函数执行:

Thymeleaf怎么显示request中绑定的数据?_thyme 显示request的值-程序员宅基地

文章浏览阅读1.1k次。Thymeleaf怎么显示request中绑定的数据?后端代码:@Controllerpublic class test { @RequestMapping({"/test"}) public String test(HttpServletRequest httpServletRequest){ //使用setAttribute存入数据 httpServletRequest.setAttribute("zhang" , "zhangjiahong"); _thyme 显示request的值

VSCode之Markdown自动生成目录#TOC#解决目录不整齐问题_vscode markdown 表格显示不全-程序员宅基地

文章浏览阅读3.1k次,点赞8次,收藏3次。一 、下载插件(1)在扩展里,搜索“Markdown”,在列表里选择Markdown的插件。(2)例如“Markdown TOC”,这是一个专门生产目录的插件。点击安装二、生成目录在你想添加目录的地方右击选择“Markdown TOC:Insert/Update”三、可能出现的问题及解决VSCode中Markdown目录显示异常TOC标签格式异常出现如下auto的文字原因:默..._vscode markdown 表格显示不全

MediaServerStudioEssentials2017R2版本安装_media server studio community 2017-程序员宅基地

文章浏览阅读1.1k次。安装依赖sudo apt-get install -y preload libpciaccess-dev libpthread-stubs0-devsudo apt-get install -y compizconfig-settings-managersudo apt-get install -y subversion git git-svn gcc g++ make cmake nasms_media server studio community 2017

第9讲:使用ajax技术实现增删改查及分页显示功能(jQuery)_ajax实现修改功能-程序员宅基地

文章浏览阅读1.2k次。本讲内容首先讲解jQuery对ajax的支持,分别讲解$.post,$.get,$.ajax等方法,这些方法的参数,使用方法及区别。最后对ajax的综合应用举例:在同一个页面实现新增,修改,删除学校资料,分页列表等功能,前端使用html静态页面,使用MySQL数据库,后台使用servlet技术实现。_ajax实现修改功能

找回word文件的两种密码_word文档保护默认密码是多少-程序员宅基地

文章浏览阅读773次。Word文档的密码也有两种:一种是打开密码,一种是编辑限制两种密码加密后的效果也是不一样的:设置了打开密码的Word文档,是在打开文件的时候需要输入密码,保护文件内容不被其他人看到。当我们输入了正确的word密码,进入到文件之后,就一些都正常了,可以正常阅读、正常编辑word文件。设置了编辑限制的Word文档,打开文件的时候不需要输入密码,打开之后能够查看Word文档内容,但是想要编辑WORD文件的时候,保护文件内容不被修改,需要输入正确的Word密码,将限制编辑取消才能够正常编辑Word文档。两种密码如果_word文档保护默认密码是多少

推荐文章

热门文章

相关标签