所有分类
  • 所有分类
  • 未分类

SpringBoot-过滤器/拦截器/AOP-用法

简介

说明

本文介绍SpringMVC(SpringBoot)中的过滤器、拦截器、AOP的区别及其用法。

过滤器/拦截器/AOP

概述

简介

  • 过滤器:Filter;拦截器:Interceptor  。Spring中,建议优先用拦截器(Filter 能做的,interceptor 几乎都能更简单)。
  • AOP:可以自定义切入的点,有方法的参数,但拿不到http请求,可以通过RequestContextHolder等方式获得。
    AOP用法见:Spring之AOP系列–使用/实例 – 自学精灵

执行顺序概述

过滤前=> 拦截前=> ControllerAdvice=> AOP=> Controller=> AOP=> ControllerAdvice => 拦截后=> 过滤后

执行顺序详解

  • 不抛异常时
    • 监听器 => 过滤器=> 拦截器(前处理)=> AOP(前处理)=> @ControllerAdvice的@ModelAttribute=> 自己的controller=> AOP(后处理)=> @ControllerAdvice实现beforeBodyWrite接口=> 拦截器(后处理)
  • 抛异常时:
    • 监听器 => 过滤器=> 拦截器(前处理)=> AOP(前处理)=> @ControllerAdvice的@ModelAttribute=> 自己的controller=> AOP(后处理)=>  @ControllerAdvice的@ExceptionHandler=> @ControllerAdvice实现beforeBodyWrite接口=> 拦截器(后处理)。

过滤器与拦截器的区别

过滤器拦截器
使用场景对请求/响应进行修改、判断等。一般用于过滤参数、登录权限验证、资源访问权限控制、敏感词汇过滤、字符编码转换。在service或者一个方法前/后调用一个方法,或者在方法后调用一个方法。
执行时机请求时:进入容器后,servlet之前进行处理。
响应时:在servlet之后处理。
请求时:进入servlet后,进入Controller前进行处理。
响应时:在Controller后,servlet前进行处理。
能力可以拿到原始的http请求与响应,拿不到请求的控制器和请求控制器中的方法的信息。
可以修改请求、响应、参数:比如:修改字符编码、删除低俗文字、删除危险字符、修改参数
可以拿到你请求的控制器和方法,却拿不到请求与响应。
顺序可指定顺序。可指定顺序。
实现方式回调函数(同步调用)AOP
使用范围只能用于Web。
(过滤器实现 javax.servlet.Filter 接口(属于Servlet),所以过滤器要依赖于Tomcat等容器,只能在web程序中使用。)
可用于Web、Application、Swing。
(由Spring提供,不依赖Tomcat等容器,可单独使用。)
作用范围所有请求。只能处理controller请求。
无法控制静态资源。
灵活性/粒度灵活性差(粒度大)。
不能使用 Spring 容器资源。
灵活性好(粒度小)。
能使用Spring里的任何资源、对象,例如 Service对象、数据源、事务管理等,通过IoC注入到拦截器即可。
打断链路打断方法:请求和响应引发中断,需要额外动作,比如重定向到错误页面。打断方法:preHandle方法内返回 false
执行次数一个controller周期只调用一次:一个过滤器实例只能在容器初始化时调用一次。一个controller周期可调用多次

过滤器

见:SpringBoot–过滤器(Filter)–使用/教程/实例 – 自学精灵

拦截器

见:SpringBoot–拦截器(Interceptor)–使用/教程/实例 – 自学精灵

联合实例:过滤/拦截/@ControllerAdvice/AOP

代码

实体类

package com.example.entity;

import lombok.Data;

@Data
public class User {
    private Integer id;
    private String name;
    private Integer age;
}

控制器

package com.example.controller;

import com.example.entity.User;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/hello")
public class HelloController {

    @RequestMapping("/test1")
    public User test1(User user) {
        System.out.println("HelloController.test1");
        return user;
    }
}

过滤器

package com.example.filter;

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

public class MyFilter implements Filter {
    @Override
    public void init(javax.servlet.FilterConfig filterConfig) throws ServletException {
        System.out.println("MyFilter.init");
        System.out.println("  过滤器名:" + filterConfig.getFilterName());
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
                         FilterChain filterChain) throws IOException, ServletException {
        // do something 处理request 或response
        System.out.println("MyFilter.doFilter");

        if (servletRequest instanceof HttpServletRequest) {
            System.out.println("  URL:" + ((HttpServletRequest)servletRequest).getRequestURL());
        }

        // 调用filter链中的下一个filter
        filterChain.doFilter(servletRequest,servletResponse);
    }

    @Override
    public void destroy() {
        System.out.println("MyFilter.destroy");
    }
}
package com.example.filter;

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FilterConfig {

    @Bean
    public FilterRegistrationBean registrationBean1() {
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new MyFilter());
        filterRegistrationBean.addUrlPatterns("/*");
        // filterRegistrationBean.setName("xxx");  //可设置过滤器名字

        return filterRegistrationBean;
    }
}

 拦截器

package com.example.interceptor;

import org.springframework.lang.Nullable;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

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

public class MyInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
                             Object handler) throws Exception {
        System.out.println("MyInterceptor.preHandle");
        return true;
    }

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

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
                                Object handler, @Nullable Exception ex) throws Exception {
        System.out.println("MyInterceptor.afterCompletion");
    }
}
package com.example.interceptor;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {

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

@ControllerAdvice

package com.example.advice;

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ModelAttribute;

@ControllerAdvice
public class GlobalControllerAdvice {
    @ModelAttribute
    public void authenticationUser() {
        System.out.println("GlobalControllerAdvice.authenticationUser");
    }
}

对@RequestMapping的AOP

package com.example.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class RequestMappingAspect {
    @Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)")
    public void pointcut() {

    }

    @Around("pointcut()")
    public Object around1(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("[RequestMappingAspect.around1]: around before");
        Object object = joinPoint.proceed();
        System.out.println("[RequestMappingAspect.around1]: around after");

        return object;
    }
}

测试

启动SpringBoot

MyFilter.init
  过滤器名:myFilter

postman访问:http://localhost:8080/hello/test1?name=Tony

后端结果

MyFilter.doFilter
  URL:http://localhost:8080/hello/test1
MyInterceptor.preHandle
GlobalControllerAdvice.authenticationUser
[RequestMappingAspect.around1]: around before
HelloController.test1
[RequestMappingAspect.around1]: around after
MyInterceptor.postHandle
MyInterceptor.afterCompletion

postman结果

{
    "id": null,
    "name": "Tony",
    "age": null
}

关闭SpringBoot

Disconnected from the target VM, address: '127.0.0.1:61631', transport: 'socket'
MyFilter.destroy
2020-09-05 17:49:01.499  INFO 17144 --- [extShutdownHook] o.s.s.concurrent.ThreadPoolTaskExecutor  : Shutting down ExecutorService 'applicationTaskExecutor'
2

评论4

请先

  1. 过滤器的实现方式用回调函数描述不准确吧,过滤器是同步、阻塞的。doFilter()方法调用类似于回调函数的使用方式,但本质上是同步调用的方式,用回调总感觉有点误解
    嘟胤null 2024-08-15 0
    • 好的,我括号里加了个具体描述。
      自学精灵 2024-08-15 0
  2. 执行数据的图好像画错了。写着AOP=> @ControllerAdvice=> 自己的Controller。颜色图画反了
    150003 2024-05-16 0
    • 确实画错了,已修复。
      自学精灵 2024-05-17 0
显示验证码
没有账号?注册  忘记密码?

社交账号快速登录