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 {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {Filter.super.init(filterConfig);}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {String filterVal = "这是我设置的过滤器参数值";servletRequest.setAttribute("myFilterParam", filterVal);filterChain.doFilter(servletRequest, servletResponse);}@Overridepublic 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 {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String interceptorVal = "这是我设置的拦截器参数值";request.setAttribute("myInterceptorParam", interceptorVal);return HandlerInterceptor.super.preHandle(request, response, handler);}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);}@Overridepublic 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 {@Autowiredprivate MyInterceptor myInterceptor;@Overridepublic 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 {@Autowiredprivate HttpServletRequest request;@PostMapping("/test")@AopAnnopublic 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 测试结果
附:
附一篇之前写的博客,分析过滤器和拦截器的