谈谈 Spring 的过滤器和拦截器

spring · 浏览次数 : 8

小编点评

**2.2.2 如何实现创建 Interceptor 类,实现HandlerInterceptor接口,重写 3 个方法,加@Component注解。@Componentpublic class MyHandlerInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { //请求开始时间 long startTime = System.currentTimeMillis(); request.setAttribute("startTime", startTime); System.out.println("startTime : " + new Date(startTime)); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { long startTime = (Long)request.getAttribute("startTime"); long endTime = System.currentTimeMillis(); // 统计耗时 long executeTime = endTime - startTime; System.out.println("executeTime : " + executeTime + "ms"); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { HandlerInterceptor.super.afterCompletion(request, response, handler, ex); }} }** **2.3. MethodInterceptor 拦截器** **2.3.1. 简介** MethodInterceptor 是 AOP 中的拦截器,它拦截的目标是方法,可以不是 Controller 中的方法。在对一些普通的方法上的拦截可以使用该拦截器,这是 HandlerInterceptor 无法实现的。可用来进行方法级别的身份认证、授权以及日志记录等,也可基于自定义注解实现一些通用的方法增强功能。 **2.3.2. 如何实现MethodInterceptor 是基于 AOP 实现的,所以根据不同的代理有多种实现方式,更多的实现方式和原理我将在整理 Spring AOP 的时候详细接受。这里我将介绍通过BeanNameAutoProxyCreator自动代理实现拦截。该类是基于 Bean 名称的自动代理,可以针对特定的Bean进行个性化的 AOP 配置。 **2.3.3. 示例** ```java @Component public class MyMethodInterceptor implements MethodInterceptor { @Override public Object invoke(MethodInvocation invocation) throws Throwable { System.out.println("进入拦截,方法执行前,拦截方法是:" + invocation.getMethod().getName()); Object result = invocation.proceed(); System.out.println("方法执行后"); return result; } } ``` **3. 配置自动代理,加@Configuration注解并创建自动代理BeanNameAutoProxyCreator。** ```java @Configuration public class MyMethodConfigurer { @Resource private MyMethodInterceptor myMethodInterceptor; @Bean public BeanNameAutoProxyCreator beanNameAutoProxyCreator() { // 使用BeanNameAutoProxyCreator来创建代理 BeanNameAutoProxyCreator beanNameAutoProxyCreator = new BeanNameAutoProxyCreator(); // 指定一组需要自动代理的Bean名称,Bean名称可以使用*通配符 beanNameAutoProxyCreator.setBeanNames("user*\"); //设置拦截器名称,这些拦截器是有先后顺序的 beanNameAutoProxyCreator.setInterceptorNames("myMethodInterceptor\"); return beanNameAutoProxyCreator; } } ```

正文

前言

我们在进行 Web 应用开发时,时常需要对请求进行拦截或处理,故 Spring 为我们提供了过滤器和拦截器来应对这种情况。那么两者之间有什么不同呢?本文将详细讲解两者的区别和对应的使用场景。

(本文的代码实现首先是基于 SpringBoot,Spring 的实现方式仅简单描述)




image.png

1. 过滤器

1.1. 什么是过滤器


过滤器(Filter),是 Servlet 规范规定的,在 Servlet 前执行的。用于拦截和处理 HTTP 请求和响应,可用于身份认证、授权、日志记录和设置字符集(CharacterEncodingFilter)等场景。


过滤器位于整个请求处理流程的最前端,因此在请求到达 Controller 层前,都会先被过滤器处理。


过滤器可以拦截多个请求或响应,一个请求或响应也可以被多个过滤器拦截


1.2. 如何创建过滤器


Filter 的生命周期对应的三个关键方法:

方法 说明
init() 当请求发起时,会调用 init() 方法初始化 Filter 实例,仅初始化一次。若需要设置初始化参数的时可调用该方法。
doFilter() 拦截要执行的请求,对请求和响应进行处理。
destroy() 请求结束时调用该方法销毁 Filter 的实例。

下面将介绍二种方法创建 Filter。

1.2.1 实现 Filter 接口


1.创建 Filter 处理类,实现javax.servlet.Filter接口,加上@WebFilter注解配置拦截 Url,但是不能指定过滤器执行顺序,也可通过web.xml配置。

@WebFilter(urlPatterns = "/*")
public class MyFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // 用于完成 Filter 的初始化
        Filter.super.init(filterConfig);
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        
        System.out.println("过滤器已经拦截成功!!!");

        // 执行该方法之前,即对用户请求进行预处理;执行该方法之后,即对服务器响应进行后处理。
        chain.doFilter(request,response);
    }

    @Override
    public void destroy() {
        // 用于 Filter 销毁前,完成某些资源的回收;
        Filter.super.destroy();
    }
}

2.在启动类添加注解@ServletComponentScan ,让 Spring 可以扫描到。

@SpringBootApplication
@ServletComponentScan
public class MyFilterDemoApplication {

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

}

3.创建 Controller 发起 Url 请求。
@RestController
public class MyFilterController {

    @GetMapping("/testFilter")
    public String testFilter(){
        return "Hello World";
    }
}

拦截结果

image.png

1.2.2. 通过@Component 注解


1.创建 Filter 处理类,实现javax.servlet.Filter接口,加@Component注解。

  • 可以使用@Order注解保证过滤器执行顺序,不加则按照类名排序。
  • 过滤器不能指定拦截的url , 只能默认拦截全部
@Component
@Order(1)
public class MyComponentFilter1 implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        Filter.super.init(filterConfig);
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        
        System.out.println("我是过滤器1已经拦截成功!!!");
        chain.doFilter(request,response);
    }

    @Override
    public void destroy() {
        Filter.super.destroy();
    }
}

@Component
@Order(2)
public class MyComponentFilter2 implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        Filter.super.init(filterConfig);
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException 
    
        System.out.println("我是过滤器2已经拦截成功!!!");
        chain.doFilter(request,response);
    }

    @Override
    public void destroy() {
        Filter.super.destroy();
    }
}

2-3 步骤同 1.2.1,结果如下。

image.png


2. 拦截器

2.1. 什么是拦截器

拦截器(Interceptor),和Servlet无关,由Spring框架实现。可用于身份认证、授权、日志记录、预先设置数据以及统计方法的执行效率等。


一般基于 Java 的反射机制实现,属于AOP的一种运用。

目前了解的 Spring 中的拦截器有:

  • HandlerInterceptor
  • MethodInterceptor

2.2. HandlerInterceptor 拦截器

2.2.1简介


HandlerInterceptor 类似 Filter,拦截的是请求地址 ,但提供更精细的的控制能力,这里注意下必须过DispatcherServlet 的请求才会被拦截。


它允许你在请求处理前、处理后以及视图渲染完成前执行自定义逻辑,可以用来对请求地址做一些认证授权、预处理,也可以计算一个请求的响应时间等,还可以处理跨域(CORS)问题


简单的执行流程描述:

  1. 请求到达 DispatcherServlet,然后发送至 Interceptor,执行 preHandler;
  2. 请求到达 Controller,请求结束后,执行 postHandler。

2.2.2如何实现

  1. 创建 Interceptor 类,实现HandlerInterceptor接口,重写 3 个方法,加@Component注解。

image.png

@Component
public class MyHandlerInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        
        //请求开始时间
        long startTime = System.currentTimeMillis();
        request.setAttribute("startTime", startTime);
        System.out.println("startTime : " +  new Date(startTime));
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        
        long startTime = (Long)request.getAttribute("startTime");
        long endTime = System.currentTimeMillis();
        // 统计耗时
        long executeTime = endTime - startTime;
        System.out.println("executeTime : " + executeTime + "ms");

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }
}

2.配置拦截器,实现WebMvcConfigurer接口,加@Configuration注解并重写addInterceptors方法。

@Configuration
public class MyWebConfigurer implements WebMvcConfigurer {

    @Resource
    private MyHandlerInterceptor myHandlerInterceptor;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        List<String> patterns = new ArrayList<>();

        patterns.add("/test/handlerInterceptor");

        registry.addInterceptor(myHandlerInterceptor)
                .addPathPatterns(patterns) // 需要拦截的请求
                .excludePathPatterns(); // 不需要拦截的请求
    }
}

拦截结果如下:

image.png

Spring 项目如何实现?

可通过使用mvc:interceptors标签来声明需要加入到 SpringMVC 拦截器链中的拦截器。


2.3. MethodInterceptor 拦截器

2.3.1. 简介

MethodInterceptor 是 AOP 中的拦截器,它拦截的目标是方法,可以不是 Controller 中的方法。


在对一些普通的方法上的拦截可以使用该拦截器,这是 HandlerInterceptor 无法实现的。

可用来进行方法级别的身份认证、授权以及日志记录等,也可基于自定义注解实现一些通用的方法增强功能

2.3.2. 如何实现

MethodInterceptor 是基于 AOP 实现的,所以根据不同的代理有多种实现方式,更多的实现方式和原理我将在整理 Spring AOP 的时候详细接受。

这里我将介绍通过BeanNameAutoProxyCreator自动代理实现拦截。该类是基于 Bean 名称的自动代理,可以针对特定的Bean进行个性化的 AOP 配置。

1.创建简单的需要拦截的方法。

public interface UserService {
    public String getUser();
}
@Component
public class UserServiceImpl implements UserService{

    @Override
    public String getUser() {
        return "我是福星";
    }
}


2.创建 Interceptor 类,实现MethodInterceptor接口,重写invoke方法,加@Component注解。

@Component
public class MyMethodInterceptor implements MethodInterceptor {

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("进入拦截,方法执行前,拦截方法是:" + invocation.getMethod().getName());
        Object result = invocation.proceed();
        System.out.println("方法执行后");
        return result;
    }

}

3.配置自动代理,加@Configuration注解并创建自动代理BeanNameAutoProxyCreator

@Configuration
public class MyMethodConfigurer {
    @Resource
    private MyMethodInterceptor myMethodInterceptor;


    @Bean
    public BeanNameAutoProxyCreator beanNameAutoProxyCreator() {
        // 使用BeanNameAutoProxyCreator来创建代理
        BeanNameAutoProxyCreator beanNameAutoProxyCreator = new BeanNameAutoProxyCreator();

        // 指定一组需要自动代理的Bean名称,Bean名称可以使用*通配符
        beanNameAutoProxyCreator.setBeanNames("user*");

        //设置拦截器名称,这些拦截器是有先后顺序的
        beanNameAutoProxyCreator.setInterceptorNames("myMethodInterceptor");
        return beanNameAutoProxyCreator;
    }

}

发起请求后,调用该方法前会进行拦截。

image.png


3. 总结

过滤器一般用于对 Servlet 请求和响应进行通用性的处理,通常关注请求和响应内容,而不涉及具体的业务逻辑。而拦截器用于对 SpringMVC 的请求和响应进行特定的业务处理,通常与控制器层的请求处理有关。


不论是过滤器和拦截器,都可以有多个。执行顺序上拦截器是由配置中的顺序决定,而过滤器可通过@Component+@Order决定,也可由web.xml文件中的配置顺序决定。


总的来说,拦截器的使用更加灵活,Filter 能做的事情,拦截器也能做。Filter 一般用于对 URL 请求做编码处理、过滤无用参数、安全校验(比如登陆态校验),如果涉及业务逻辑上的,还是建议用拦截器。

与谈谈 Spring 的过滤器和拦截器相似的内容:

谈谈 Spring 的过滤器和拦截器

我们在进行 Web 应用开发时,时常需要对请求进行拦截或处理,故 Spring 为我们提供了过滤器和拦截器来应对这种情况。那么两者之间有什么不同呢?本文将详细讲解两者的区别和对应的使用场景。

面试官:谈谈对SpringAI的理解?

Spring AI 已经发布了好长时间了,目前已经更新到 1.0 版本了,所以身为 Java 程序员的你,如果还对 Spring AI 一点都不了解的话,那就有点太落伍了。 言归正传,那什么是 Spring AI?如何快速进行 Spring AI 开发呢? 1.什么是Spring AI? Sprin

谈谈为什么要分库分表?

由于数据库的承载能力是有限的,当业务增长量达到一定规模后,数据库的性能就会达到瓶颈。于是产生了分库分表的解决方案,本文将详细讲解什么是分库分表,以及分库分表的原因和可能产生的问题。

谈谈 JVM 垃圾回收机制

前言 垃圾回收需要思考三件事情,哪些内存需要回收?什么时候回收?如何回收? 一、哪些内存需要回收 JVM 的内存区域中,程序计数器、虚拟机栈和本地方法栈的生命周期是随线程而生,随线程而灭的。这几个区域的内存分配和回收都具有确定性,不需要过多考虑回收问题,当方法或线程结束时,内存自然就跟着回收了。 J

谈谈分布式事务原理

分布式系统中,不同服务之间的交互可能会出现各种问题,如网络、异常等,可能会导致服务间的数据产生不一致的情况,如何避免?本文将详细讲述分布式事务的原理和解决方案。

[转帖]谈谈Service与Ingress

https://zhuanlan.zhihu.com/p/596889677 你好,我是张磊。今天我和你分享的主题是:谈谈Service与Ingress。 在上一篇文章中,我为你详细讲解了将Service暴露给外界的三种方法。其中有一个叫作LoadBalancer类型的Service,它会为你在Cl

[转帖]谈谈对K8S CNI、CRI和CSI插件的理解

K8S的设计初衷就是支持可插拔架构,解决PaaS平台不好用、不能用、需要定制化等问题,K8S集成了插件、附加组件、服务和接口来扩展平台的核心功能。附加组件被定义为与环境的其他部分无缝集成的组件,提供类似本机的特性,并扩展集群管理员可用的组件,扩展还可以用于添加自定义软硬件的支持;服务和接口提供了看似

[转帖]谈谈ClickHouse性能情况以及相关优化

https://zhuanlan.zhihu.com/p/349105024 ClickHouse性能情况 主要分为4个方面 1、单个查询吞吐量 场景一: 如果数据被放置在page cache中,则一个不太复杂的查询在单个服务器上大约能够以2-10GB/s(未压缩)的速度进行处理(对于简单的查询,速

谈谈Selenium中的三种切换之alert

谈谈Selenium中的三种切换之alert 一、如何识别 识别方法:alert中的确定、取消、输入框无法用inspector定位到,当然还有一些特例。 alert分为三种 alert confirm prompt 分别对应3个js的命令(可在chrome的console面板中调试) alert('

谈谈Selenium中浏览器驱动的日志

谈谈Selenium中浏览器驱动的日志 来源于一位同学,“老师为啥firefox执行后会有日志文件,chrome没有呢?” 比对 你打开chrome浏览器 from selenium import webdriver driver = webdriver.Chrome() 这样是没有日志的 同样的代