过滤器、拦截器、切面之间的比较
一、过滤器(Filter)
- 过滤器是基于
函数回调
来实现
- 过滤url级别权限、过滤敏感词、设置字符编码等
- 实现javax.servlet.Filter接口,重写init()、doFilter()、destroy()方法
- 具体解释在代码注释中
/**
* @author lqh
* @date 2020/12/3
*/
@Component
@WebFilter(urlPatterns = "/**" , filterName = "MyFilter" )//可通过xml配置或@WebFilter实现对特定URL拦截
public class MyFilter implements Filter{
/**
* init():此方法在容器初始化时被调用,在Filter生命周期中只会被调用一次。注意:此方法必须执行成功,否则过滤器不起作用
* @param filterConfig
* @throws ServletException
*/
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("Filter-init...");
}
/**
* 每次请求都会调用该方法,FilterChain用来调用下一个过滤器
* @param servletRequest
* @param servletResponse
* @param filterChain
* @throws IOException
* @throws ServletException
*/
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("Filter-doFilter...");
/* System.out.println(((HttpServletRequest) servletRequest).getRequestURI());
System.out.println(((HttpServletRequest) servletRequest).getRequestURL());*/
filterChain.doFilter(servletRequest,servletResponse);
System.out.println("Filter-doFilter后...");
}
/**
* 容器销毁过滤器调用该方法,整个生命周期只会被调用一次
*/
@Override
public void destroy() {
System.out.println("Filter-destroy...");
}
}
二、拦截器(Interceptor)
- 拦截器是基于
反射(动态代理)
来实现
- 权限认证(比如拦截未登录的用户)、审计日志等
- 实现HandlerInterceptor接口,重写preHandle、postHandle、afterCompletion方法
/**
* 拦截器:是链式调用,一个应用中可以同时存在多个拦截器,一个请求可以触发多个拦截器,拦截器的执行顺序和它的声明顺序有关
* 实现HandlerInterceptor接口,请求的拦截通过此接口实现
* 需要注意的是,拦截器是基于Spring的,且只有通过DispatcherServlet的请求才能被拦截
*/
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("Interceptor-preHandle...");
return true;
//return false;如果返回false,将视为请求结束,不仅自身拦截器失效(postHandle\afterCompletion都不会执行),还会导致其他拦截器失效
}
/**
* Controller方法调用之后,DispatcherServlet返回视图之前被调用
* @param request
* @param response
* @param handler
* @param modelAndView
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("Interceptor-postHandle...");
}
/**
* DispatcherServlet图渲染结束之后被调用
* @param request
* @param response
* @param handler
* @param ex
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("Interceptor-afterCompletion...");
}
}
- 注册拦截器
/**
* @author lqh
* @date 2020/12/3
* 注册拦截器
*/
@Configuration
public class MyInterceptorConfig implements WebMvcConfigurer {
/**
* addPathPatterns需要拦截的请求;excludePathPatterns需要排除的请求
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/post","/favicon.ico");
}
}
三、切面(@Aspect、@ControllerAdvice)
- 切面基于
动态代理
实现
- 日志记录,性能统计,安全控制,事务处理,异常处理
- @Aspect 配置切入点、前置通知、后置通知
@Aspect
@Component
public class MyAspect {
@Pointcut("execution(* cn.openwit.springbootnotebook.controller.*Controller.*(..))")
public void declareJointPointExpression() {
}
@Before("declareJointPointExpression()")
public void beforeMethod(){
System.out.println("Aspect-Before....");
}
@After("declareJointPointExpression()")
public void afterMethod(JoinPoint joinPoint) {
System.out.println("Aspect-After....");
}
}
- @ControllerAdvice全局异常处理
@ControllerAdvice
public class GlobalExceptionHandler {
/**
* 全局处理异常
* @param e
*/
@ExceptionHandler(value = Exception.class)
public void serviceExceptionHandle(Exception e){
System.out.println("ControllerAdvice...");
}
}
四、执行结果验证
- http请求接口
@Controller
public class IndexController {
@GetMapping("/index")
public String index(){
System.out.println("Controller中index-return前...");
if(true){
//throw new RuntimeException();
return "index";
}
System.out.println("Controller中index-return后...");
return "index";
}
}
2. #### 无异常结果
由以上结果我们可以看出
- 过滤器:可以拿到原始http请求,却拿不到请求的控制器和请求控制器中的方法的信息
- 拦截器:可以拿到请求的控制器和方法,却拿不到请求方法的参数
- 切片:可以拿到方法的参数,却拿不到http请求和响应的对象
四、执行结果验证
借鉴两张图片进行说明
- 过滤器
-
使用范围
Filter接口时javax.servlet包中的,servlet规范中的,依赖与Tomcat等容器使用,只能在web应用中使用 -
具体实现原理
自定义中doFilter方法
FilterChain接口源码
FilterChain接口的具体实现类
此处filter.doFilter()实现回调
-
- 过滤器
- 使用范围
拦截器说Spring的组件,由Spring容器进行管理,不依赖于tomcat容器,可以单独使用。
- 使用范围
五、过滤器(Filter)与拦截器(Interceptor)区别:
1、过滤器是基于函数回调,而拦截器是基于java的反射机制;
2、过滤器是servlet规范规定的,只能用于web程序中,而拦截器是在spring容器中,它不依赖servlet容器
3、过滤器可以拦截几乎所有的请求(包含对静态资源的请求),而拦截器只拦截action请求(不拦截静态资源请求)
4、过滤器不能访问action上下文及值栈里的对象,而拦截器都是可以的。
5、拦截器可以获取spring容器里的对象,而过滤器是不行的
6、拦截器在action的生命周期内是可以多次调用,而过滤器只在容器初始化时被调用一次。
7、拦截器是被包裹在过滤器之中。