Spring的过滤器、拦截器、切面 三者的区别,以及对应案例分析

一、三者的实现方式
1.1 过滤器 xxxFilter
过滤器的配置比较简单,直接实现Filter接口即可,也可以通过@WebFilter注解实现对特定URL的拦截,Filter接口中定义了三个方法:
- init(): 该方法在容器启动初始化过滤器时被调用,它在过滤器的整个生命周期只会被调用一次
- doFilter(): 容器中的每一次请求都会调用该方法,其中的FilterChain用来调用下一个过滤器
- destory(): 当容器销毁过滤器实例时调用该方法,在过滤器的整个生命周期也只会被调用一次
1.2 拦截器 xxxInterceptor
拦截器它是链式调用,一个应用中可以同时存在多个拦截器,一个请求也可以触发多个拦截器,而每个拦截器的调用会依据它的声明顺序依次执行。实现HandlerInterceptor接口,该接口定义了三个方法:
- preHandle(): 这个方法将在请求处理之前调用,如果该方法返回false,将视为当前请求结束;
- postHandle(): 在Controller中的方法调用之后,DispatcherServlet返回渲染视图之前被调用;
- afterComplete(): 在整个请求结束之后,DispatcherServlet渲染了对应视图之后执行。
1.3 切面 xxxAspect
切面注解能够在任何不想改原来代码的地方添加处理逻辑,可以根据自定义注解定义切面。
- @Aspect:定义在切面实现类上的注解
- @Pointcut:定义切入点
- @Before:切面方法执行之前的增强
- @After:切面方法执行之后的增强,不管抛异常还是正常退出都执行的增强
- @AfterReturning:切面方法执行之后的增强,方法正常退出时执行
- @AfterThrowing:切面方法执行之后的增强,抛出异常后执行
- @Around:环绕增强,包围一个连接点的增强,可获取执行前信息,可修改执行后数据
二、三者的区别
过滤器:
 无法获取请求要访问的类、方法以及参数;
 可以获取原始的Http请求与响应。
拦截器:
 可以获取请求访问的类、方法;
 但是无法获取请求参数的值。
切面:
 可以获取访问的类、方法以及参数值;
 无法获取Http原始的请求与响应的对象。
=================== 请求处理顺序 ===================
 过滤器 -> 拦截器 -> 切面
 =================== 报错处理顺序 ===================
 切面 -> @ControllerAdvice -> 拦截器 -> 过滤器
三、三者的实现案例
3.1 过滤器:MyFilter
package com.wyw.learn.namebindingvsaop;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import java.io.IOException;
/**
 * @author name:    silk
 * @version 1.0
 * @description: TODO
 * @date 2024/4/22 20:18
 */
@Component
public class MyFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        Filter.super.init(filterConfig);
    }
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        String filterVal = "这是我设置的过滤器参数值";
        servletRequest.setAttribute("myFilterParam", filterVal);
        filterChain.doFilter(servletRequest, servletResponse);
    }
    @Override
    public void destroy() {
        Filter.super.destroy();
    }
}
3.2 拦截器:MyInterceptor
package com.wyw.learn.namebindingvsaop;
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 name:    silk
 * @version 1.0
 * @description: TODO
 * @date 2024/4/22 20:22
 */
@Component
public class MyInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String interceptorVal = "这是我设置的拦截器参数值";
        request.setAttribute("myInterceptorParam", interceptorVal);
        return HandlerInterceptor.super.preHandle(request, response, handler);
    }
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
    }
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }
}
另外,拦截器需要在配置类中规定拦截哪些路径
package com.wyw.learn.namebindingvsaop;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
 * @author name:    silk
 * @version 1.0
 * @description: TODO
 * @date 2024/4/22 21:49
 */
@Configuration   // 配置项
public class MyConfig implements WebMvcConfigurer {
    @Autowired
    private MyInterceptor myInterceptor;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(myInterceptor)
                .addPathPatterns("/**");   // 拦截所有URL;
    }
}
3.3 切面:MyAspect
切面注解:
package com.wyw.learn.namebindingvsaop;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AopAnno {
}
切面逻辑:
package com.wyw.learn.namebindingvsaop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
/**
 * @author name:    silk
 * @version 1.0
 * @description: TODO
 * @date 2024/4/22 15:39
 */
@Component
@Aspect
public class MyAspect {
    @Pointcut(value = "@annotation(com.wyw.learn.namebindingvsaop.AopAnno)")
    public void pointCut() {
    }
    @Before("pointCut()")
    public void doProcess(JoinPoint joinPoint) {
        Object obj = joinPoint.getArgs().length == 0 ? null : joinPoint.getArgs()[0];
        if (obj == null) {
            return;
        }
        // 获取request对象,注意这已不是原始的请求
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        String aspectVal = "这是我设置的切面参数值";
        request.setAttribute("myAspectParam", aspectVal);
        // 通过切面给参数属性赋值
        Arrays.stream(obj.getClass().getDeclaredFields()).forEach(field -> {
            field.setAccessible(true);
            if ("description".equals(field.getName())) {
                try {
                    field.set(obj, "这是我给请求参数属性设置的切面参数值");
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        });
    }
}
3.4 测试类:MyTestController
package com.wyw.learn.namebindingvsaop;
import com.itheima.reggie.entity.Dish;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
/**
 * @author name:    silk
 * @version 1.0
 * @description: TODO
 * @date 2024/4/22 20:35
 */
@RestController
@RequestMapping("/myTest")
public class MyTestController {
    @Autowired
    private HttpServletRequest request;
    @PostMapping("/test")
    @AopAnno
    public void testDemo(@RequestBody Dish dish) {
        Object myFilter = request.getAttribute("myFilterParam");
        Object myInterceptor = request.getAttribute("myInterceptorParam");
        Object myAspect = request.getAttribute("myAspectParam");
        String description = dish.getDescription();
        System.out.println("请求中过滤器参数值是:" + myFilter);
        System.out.println("请求中拦截器参数值是:" + myInterceptor);
        System.out.println("请求中切面参数值是:" + myAspect);
        System.out.println("请求参数的Description属性的参数值是:" + description);
    }
}
3.5 测试结果

附:
附一篇之前写的博客,分析过滤器和拦截器的



















