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 测试结果
附:
附一篇之前写的博客,分析过滤器和拦截器的