SpringBoot实现过滤器、拦截器与切片的实现和区别

Q:使用过滤器、拦截器与切片实现每个请求耗时的统计,并比较三者的区别与联系

过滤器Filter

过滤器概念

Filter是J2E中来的,可以看做是 Servlet 的一种“加强版”,它主要用于对用户请求进行预处理和后处理,拥有一个典型的 处理链 。Filter也可以对用户请求生成响应,这一点与Servlet相同,但实际上很少会使用Filter向用户请求生成响应。使用Filter完整的流程是:Filter对用户请求进行预处理,接着将请求交给Servlet进行预处理并生成响应,最后Filter再对服务器响应进行后处理。

过滤器作用

在JavaDoc中给出了几种过滤器的作用

 * Examples that have been identified for this design are<br>
 * 1) Authentication Filters, 即用户访问权限过滤
 * 2) Logging and Auditing Filters, 日志过滤,可以记录特殊用户的特殊请求的记录等
 * 3) Image conversion Filters
 * 4) Data compression Filters <br>
 * 5) Encryption Filters <br>
 * 6) Tokenizing Filters <br>
 * 7) Filters that trigger resource access events <br>
 * 8) XSL/T filters <br>
 * 9) Mime-type chain Filter <br>

对于第一条,即使用Filter作权限过滤,其可以这么实现:定义一个Filter,获取每个客户端发起的请求URL,与当前用户无权限访问的URL列表(可以是从DB中取出)作对比,起到权限过滤的作用。

过滤器实现方式

自定义的过滤器都必须实现 javax.Servlet.Filter 接口,并重写接口中定义的三个方法:

1、void init(FilterConfig config)
用于完成Filter的初始化。

2、void destory()
用于Filter销毁前,完成某些资源的回收。

3、void doFilter(ServletRequest request,ServletResponse response,FilterChain chain)
实现过滤功能,即对每个请求及响应增加的额外的预处理和后处理。,执行该方法之前,即对用户请求进行预处理;执行该方法之后,即对服务器响应进行后处理。值得注意的是, chain.doFilter() 方法执行之前为预处理阶段,该方法执行结束即代表用户的请求已经得到控制器处理。因此,如果再 doFilter 中忘记调用 chain.doFilter() 方法,则用户的请求将得不到处理。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

// 必须添加注解,springmvc通过web.xml配置
@Component
public class TimeFilter implements Filter {
 private static final Logger LOG = LoggerFactory.getLogger(TimeFilter.class);

 @Override
 public void init(FilterConfig filterConfig) throws ServletException {
  LOG.info("初始化过滤器:{}", filterConfig.getFilterName());
 }

 @Override
 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
  LOG.info("start to doFilter");
  long startTime = System.currentTimeMillis();
  chain.doFilter(request, response);
  long endTime = System.currentTimeMillis();
  LOG.info("the request of {} consumes {}ms.", getUrlFrom(request), (endTime - startTime));
  LOG.info("end to doFilter");
 }

 @Override
 public void destroy() {
  LOG.info("销毁过滤器");
 }

 private String getUrlFrom(ServletRequest servletRequest){
  if (servletRequest instanceof HttpServletRequest){
   return ((HttpServletRequest) servletRequest).getRequestURL().toString();
  }

  return "";
 }
}

从代码中可看出,类 Filter 是在 javax.servlet.* 中,因此可以看出,过滤器的一个很大的局限性在于, 其不能够知道当前用户的请求是被哪个控制器(Controller)处理的 ,因为后者是spring框架中定义的。

在SpringBoot中注册第三方过滤器

对于SpringMvc,可以通过在 web.xml 中注册过滤器。但在SpringBoot中不存在 web.xml ,此时如果引用的某个jar包中的过滤器,且这个过滤器在实现时没有使用 @Component 标识为Spring Bean,则这个过滤器将不会生效。此时需要通过java代码去注册这个过滤器。以上面定义的 TimeFilter 为例,当去掉类注解 @Component 时,注册方式为:

@Configuration
public class WebConfig {
 /**
  * 注册第三方过滤器
  * 功能与spring mvc中通过配置web.xml相同
  * @return
  */
 @Bean
 public FilterRegistrationBean thirdFilter(){
  ThirdPartFilter thirdPartFilter = new ThirdPartFilter();
  FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean() ;

  filterRegistrationBean.setFilter(thirdPartFilter);
  List<String > urls = new ArrayList<>();
  // 匹配所有请求路径
  urls.add("/*");
  filterRegistrationBean.setUrlPatterns(urls);

  return filterRegistrationBean;
 }
}

相比使用 @Component 注解,这种配置方式有个优点,即可以自由配置拦截的URL。

拦截器Interceptor

拦截器概念

拦截器,在AOP(Aspect-Oriented Programming)中用于在某个方法或字段被访问之前,进行拦截,然后在之前或之后加入某些操作。拦截是AOP的一种实现策略。

拦截器作用

  1. 日志记录:记录请求信息的日志,以便进行信息监控、信息统计、计算PV(Page View)等
  2. 权限检查:如登录检测,进入处理器检测检测是否登录
  3. 性能监控:通过拦截器在进入处理器之前记录开始时间,在处理完后记录结束时间,从而得到该请求的处理时间。(反向代理,如apache也可以自动记录);
  4. 通用行为:读取cookie得到用户信息并将用户对象放入请求,从而方便后续流程使用,还有如提取Locale、Theme信息等,只要是多个处理器都需要的即可使用拦截器实现。

拦截器实现

通过实现 HandlerInterceptor 接口,并重写该接口的三个方法来实现拦截器的自定义:

1、preHandler(HttpServletRequest request, HttpServletResponse response, Object handler)
方法将在 请求处理之前 进行调用。SpringMVC中的 Interceptor 同Filter一样都是 链式调用 。每个Interceptor的调用会依据它的声明顺序依次执行,而且最先执行的都是Interceptor中的preHandle方法,所以可以在这个方法中进行一些前置初始化操作或者是对当前请求的一个预处理,也可以在这个方法中进行一些判断来决定请求是否要继续进行下去。该方法的返回值是布尔值Boolean 类型的,当它返回为false时,表示请求结束,后续的Interceptor和Controller都不会再执行;当返回值为true时就会继续调用下一个Interceptor 的preHandle 方法,如果已经是最后一个Interceptor 的时候就会是调用当前请求的Controller 方法。

2、 postHandler(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
在当前 请求进行处理之后 ,也就是Controller 方法调用之后执行,但是它会在DispatcherServlet 进行视图返回渲染之前被调用,所以我们可以在这个方法中对Controller 处理之后的ModelAndView 对象进行操作。

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

@Component
public class TimeInterceptor implements HandlerInterceptor {
 private static final Logger LOG = LoggerFactory.getLogger(TimeInterceptor.class);
 @Override
 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
  LOG.info("在请求处理之前进行调用(Controller方法调用之前)");
  request.setAttribute("startTime", System.currentTimeMillis());
  HandlerMethod handlerMethod = (HandlerMethod) handler;
  LOG.info("controller object is {}", handlerMethod.getBean().getClass().getName());
  LOG.info("controller method is {}", handlerMethod.getMethod());

  // 需要返回true,否则请求不会被控制器处理
  return true;
 }

 @Override
 public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
  LOG.info("请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后),如果异常发生,则该方法不会被调用");
 }

 @Override
 public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
  LOG.info("在整个请求结束之后被调用,也就是在DispatcherServlet 渲染了对应的视图之后执行(主要是用于进行资源清理工作)");
  long startTime = (long) request.getAttribute("startTime");
  LOG.info("time consume is {}", System.currentTimeMillis() - startTime);
 }

与过滤器不同的是,拦截器使用 @Component 修饰后,还需要通过实现 WebMvcConfigurer 手动注册:

// java配置类
@Configuration
public class WebConfig implements WebMvcConfigurer {
 @Autowired
 private TimeInterceptor timeInterceptor;

 @Override
 public void addInterceptors(InterceptorRegistry registry){
  registry.addInterceptor(timeInterceptor);
 }
}

切片Aspect

切片概述

相比过滤器,拦截器能够知道用户发出的请求最终被哪个控制器处理,但是拦截器还有一个明显的不足,即不能够获取request的参数以及控制器处理之后的response。所以就有了切片的用武之地了。

切片实现

切片的实现需要注意 @Aspect , @Component 以及 @Around 这三个注解的使用,详细查看官方文档:传送门

@Aspect
@Component
public class TimeAspect {
 private static final Logger LOG = LoggerFactory.getLogger(TimeAspect.class);

 @Around("execution(* me.ifight.controller.*.*(..))")
 public Object handleControllerMethod(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
  LOG.info("切片开始。。。");
  long startTime = System.currentTimeMillis();

  // 获取请求入参
  Object[] args = proceedingJoinPoint.getArgs();
  Arrays.stream(args).forEach(arg -> LOG.info("arg is {}", arg));

  // 获取相应
  Object response = proceedingJoinPoint.proceed();

  long endTime = System.currentTimeMillis();
  LOG.info("请求:{}, 耗时{}ms", proceedingJoinPoint.getSignature(), (endTime - startTime));
  LOG.info("切片结束。。。");
  return null;
 }
}

过滤器、拦截器以及切片的调用顺序

如下图,展示了三者的调用顺序Filter->Intercepto->Aspect->Controller。相反的是,当Controller抛出的异常的处理顺序则是从内到外的。因此我们总是定义一个注解 @ControllerAdvice 去统一处理控制器抛出的异常。如果一旦异常被 @ControllerAdvice 处理了,则调用拦截器的 afterCompletion 方法的参数 Exception ex 就为空了。

实际执行的调用栈也说明了这一点:

而对于过滤器和拦截器详细的调用顺序如下图:

过滤器和拦截器的区别

最后有必要再说说过滤器和拦截器二者之间的区别:

Filter Interceptor
实现方式 过滤器是基于函数回调 基于Java的反射机制的
规范 Servlet规范 Spring规范
作用范围 对几乎所有的请求起作用 只对action请求起作用

除此之外,相比过滤器,拦截器能够“看到”用户的请求具体是被Spring框架的哪个控制器所处理。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • spring boot设置过滤器、监听器及拦截器的方法

    前言 其实这篇文章算不上是springboot的东西,我们在spring普通项目中也是可以直接使用的 设置过滤器: 以前在普通项目中我们要在web.xml中进行filter的配置,但是只从servlet 3.0后,我们就可以在直接在项目中进行filter的设置,因为她提供了一个注解@WebFilter(在javax.servlet.annotation包下),使用这个注解我们就可以进行filter的设置了,同时也解决了我们使用springboot项目没有web.xml的尴尬,使用方法如下所示 @

  • 解决Spring boot2.0+配置拦截器拦截静态资源的问题

    第一次遇到这个问题的时候,简直是一脸蒙逼,写了一个拦截器以后,静态资源就不能访问了,到处查找才知道是版本问题 解决办法: 第一步:定义一个类实现 实现WebMvcConfigurer的类中拦截器中添加放行资源处添加静态资源文件路径: @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(sessionInterceptor).addPathPatterns("/&

  • 在spring-boot工程中添加spring mvc拦截器

    1. 认识拦截器 Spring MVC的拦截器(Interceptor)不是Filter,同样可以实现请求的预处理.后处理.使用拦截器仅需要两个步骤: 实现拦截器 注册拦截器 1.1 实现拦截器 实现拦截器可以自定义实现HandlerInterceptor接口,也可以通过继承HandlerInterceptorAdapter类,后者是前者的实现类.下面是拦截器的一个实现的例子,目的是判断用户是否登录.如果preHandle方法return true,则继续后续处理. public class L

  • 详谈springboot过滤器和拦截器的实现及区别

    前言 springmvc中有两种很普遍的AOP实现: 1.过滤器(Filter) 2.拦截器(Interceptor) 本篇面对的是一些刚接触springboot的人群 所以主要讲解filter和interceptor的简单实现和它们之间到底有什么区别 (一些复杂的功能我会之后发出文章,请记得关注) Filter的简单实现 字面意思:过滤器就是过滤的作用,在web开发中过滤一些我们指定的url 那么它能帮我们过滤什么呢? 那功能可就多了: 比如过拦截掉我们不需要的接口请求 修改请求(reques

  • Springboot引入拦截器并放行swagger代码实例

    这篇文章主要介绍了Springboot引入拦截器并放行swagger代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 Springboot引入拦截器 自定义的拦截器类 Interceptor package cn.zytao.taosir.auth.config; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import j

  • SpringBoot拦截器实现登录拦截的方法示例

    源码 GitHub:https://github.com/291685399/springboot-learning/tree/master/springboot-interceptor01 SpringBoot拦截器可以做什么 可以对URL路径进行拦截,可以用于权限验证.解决乱码.操作日志记录.性能监控.异常处理等 SpringBoot拦截器实现登录拦截 pom.xml: <?xml version="1.0" encoding="UTF-8"?> &

  • spring boot配置拦截器代码实例

    这篇文章主要介绍了spring boot配置拦截器代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 首先引入web模块的依赖: 复制代码 <!-- spring boot web 组件 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</ar

  • SpringBoot解决跨域请求拦截问题代码实例

    前言 同源策略:判断是否是同源的,主要看这三点,协议,ip,端口. 同源策略就是浏览器出于网站安全性的考虑,限制不同源之间的资源相互访问的一种政策. 比如在域名https://www.baidu.com下,脚本不能够访问https://www.sina.com源下的资源,否则将会被浏览器拦截. 注意两点: 1.必须是脚本请求,比如AJAX请求. 但是如下情况不会产生跨域拦截 <img src="xxx"/> <a href='xxx"> </a&

  • SpringBoot实现过滤器拦截器的耗时对比

    目录 过滤器的方式 拦截器的方式 三种方式 下面为大家一一对应 过滤器的方式 拦截器的方式 过滤器的方式 这种方式简单点 但是可配置性不高 注意:一定得扫描到spring容器中 创建一个类 实现 filter接口 init:该方法是对filter对象进行初始化的方法,仅在容器初始化filter对象结束后被调用一次,参数FilterConfig可以获得filter的初始化参数: doFilter:可以对request和response进行<u>预处理</u>.其中FilterChai

  • SpringBoot实现过滤器、拦截器与切片的实现和区别

    Q:使用过滤器.拦截器与切片实现每个请求耗时的统计,并比较三者的区别与联系 过滤器Filter 过滤器概念 Filter是J2E中来的,可以看做是 Servlet 的一种"加强版",它主要用于对用户请求进行预处理和后处理,拥有一个典型的 处理链 .Filter也可以对用户请求生成响应,这一点与Servlet相同,但实际上很少会使用Filter向用户请求生成响应.使用Filter完整的流程是:Filter对用户请求进行预处理,接着将请求交给Servlet进行预处理并生成响应,最后Filt

  • Springboot如何利用拦截器拦截请求信息收集到日志详解

    目录 1.需求 2.问题 2.获取 1)导入依赖为了获取客户端类型.操作系统类型.ip.port 2)封装获取body字符串的工具类 3)拦截器类 4)继承 HttpServletRequestWrapper类 5)过滤器类 6)拦截器过滤器配置类 总结 1.需求 最近在工作中遇到的一个需求,将请求中的客户端类型.操作系统类型.ip.port.请求方式.URI以及请求参数值收集到日志中,网上找资料说用拦截器拦截所有请求然后收集信息,于是就开始了操作: 2.问题 试了之后发现当请求方式为POST,

  • SpringBoot配置自定义拦截器实现过程详解

    目录 1. HttpServletRequest包装类 2. 使用Filter将request传递下去 3. 添加拦截器 4. 全局异常处理器 5. 配置拦截器 1. HttpServletRequest包装类 因为HttpServletRequest只能读取一次,所以需要对request进行包装,变成可重复读的request. package net.lesscoding.interceptor; import javax.servlet.ReadListener; import javax.

  • SpringBoot之HandlerInterceptor拦截器的使用详解

    前言 平常项目开发过程中,会遇到登录拦截,权限校验,参数处理,防重复提交等问题,那拦截器就能帮我们统一处理这些问题. 一.实现方式 1.1 自定义拦截器 自定义拦截器,即拦截器的实现类,一般有两种自定义方式: 定义一个类,实现org.springframework.web.servlet.HandlerInterceptor接口. 定义一个类,继承已实现了HandlerInterceptor接口的类,例如org.springframework.web.servlet.handler.Handle

  • springboot+mybatis-plus基于拦截器实现分表的示例代码

    目录 前言 一.设计思路 二.实现思路 三.代码实现 接口描述 核心组成部分 1.本地线程工具类 2.注解部分 3.拦截器实现 四.测试 后记 前言 最近在工作遇到数据量比较多的情况,单表压力比较大,crud的操作都受到影响,因为某些原因,项目上没有引入sharding-jdbc这款优秀的分表分库组件,所以打算简单写一个基于mybatis拦截器的分表实现 一.设计思路 在现有的业务场景下,主要实现的目标就是表名的替换,需要解决的问题有 如何从执行的方法中,获取对应的sql并解析获取当前执行的表名

  • Springboot自定义mybatis拦截器实现扩展

    前言 相信大家对拦截器并不陌生,对mybatis也不陌生. 有用过pagehelper的,那么对mybatis拦截器也不陌生了,按照使用的规则触发sql拦截,帮我们自动添加分页参数 . 那么今天,我们的实践 自定义mybatis拦截器也是如此, 本篇文章实践的效果: 针对一些使用 单个实体类去接收返回结果的 mapper方法,我们拦截检测,如果没写 LIMIT 1 ,我们将自动帮忙填充,达到查找单条数据 效率优化的效果. ps: 当然,跟着该篇学会了这个之后,那么可以扩展的东西就多了,大家按照自

  • 在springboot中使用拦截器的步骤详解

    目录 在springboot中使用拦截器 1. 定义拦截器 2. 使用JavaConfig注册拦截器 3. 定义控制器,测试拦截器 4. 总结 在springboot中使用拦截器 拦截器Interceptor,是SpringMVC中的核心内容,利用spring的AOP(Aspect Oriented Programming, 面向切面编程)特性,可以很方便的对用户的业务代码进行横向抽取,根据具体业务需求对应用功能进行增强.在SpringBoot中使用Interceptor,同时采用全注解开发,涉

  • SpringBoot实现登录拦截器的方法详解

    在项目目录下建立两个包:inter 与contsfig 在inter新建层中实现HandlerInterceptor的继承类 package com.example.gameboxadminserver.inter; import com.example.gameboxadminserver.entity.User; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.

  • 一文详解Java过滤器拦截器实例逐步掌握

    目录 一.过滤器与拦截器相同点 二.过滤器与拦截器区别 三.过滤器与拦截器的实现 四.过滤器与拦截器相关面试题 一.过滤器与拦截器相同点 1.拦截器与过滤器都是体现了AOP的思想,对方法实现增强,都可以拦截请求方法. 2.拦截器和过滤器都可以通过Order注解设定执行顺序 二.过滤器与拦截器区别 在Java Web开发中,过滤器(Filter)和拦截器(Interceptor)都是常见的用于在请求和响应之间进行处理的组件.它们的主要区别如下: 运行位置不同:过滤器是运行在Web服务器和Servl

随机推荐