过滤器和拦截器的辨析
介绍
过滤器和拦截器都是为了在请求到达目标处理器(Servlet或Controller)之前或者之后插入自定义的处理逻辑
- 过滤器:
遵循AOP(面向切面编程)思想实现,基于Servlet规范提供的Filter接口,它是位于客户端请求与服务器响应之间的一个组件,依赖于Servlet容器。当请求到达服务器时,过滤器会在请求进入实际目标资源(如Servlet、JSP页面)之前或之后执行特定的操作,原理是基于函数回调
- 拦截器
遵循AOP(面向切面编程)思想实现,如Spring MVC中的HandlerInterceptor
接口,它不依赖于Servlet容器的具体实现,而是由应用框架管理。拦截器是在请求进入到控制器层(Controller)方法前后执行自定义逻辑
原理解析
过滤器
为什么说过滤器基于函数回调?
过滤器基于函数回调,所谓函数回调/回调函数指的是:一个函数(称为回调函数)作为参数传递给另一个函数(称为调用函数),当满足一定条件或者在某个特定时刻,调用函数会调用传递过来的回调函数
由于Java中不直接支持函数指针,所以常常通过接口来实现回调机制
FilterChain
就是一个接口
public interface FilterChain {
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException;
}
Filter
的实现类中doFilter()
方法中FilterChain
作为参数被传进来,并且在合适的时机被回调了其doFilter
方法
public class MyFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
Filter.super.init(filterConfig);
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
log.info("hello");
chain.doFilter(request,response);
}
@Override
public void destroy() {
Filter.super.destroy();
}
}
拦截器
拦截器基于AOP(面向切面编程)思想实现,但是并不一定用到动态代理或者切面,切点之类的技术,以如Spring MVC中的HandlerInterceptor
接口为例,从源码看更像是直接将拦截器注入,形成了一个拦截器链,在controller
层面上进行代码织入
DispatcherServlet
作为SpringMVC框架的核心类,http请求的核心执行方法为doService()
,再进入doDispatch()
方法
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
/**
* 1.
* HandlerExecutionChain 是一个对象
* 包含了以下重要的属性
* private final Object handler; //处理器(controller和其最后的方法)
* List<HandlerInterceptor> interceptorList = new ArrayList<>();//拦截器列表,用来存储匹配处理器的拦截器
*/
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
/**
* 2.
* 下面这行代码 大概做了以下事情
* 1.通过request和url 匹配了对应的controller以及调用的方法 填充了HandlerExecutionChain.handler
* 2.通过匹配request和HandlerInterceptor的注册信息(拦截哪些,放行哪些),往HandlerExecutionChain.interceptorList中添加对应的拦截器
*/
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = HttpMethod.GET.matches(method);
if (isGet || HttpMethod.HEAD.matches(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
/**
* 3.执行 拦截器链条中的所有前置方法
*/
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
/**
* 4.交由处理器(controller对应的方法)去处理方法中的业务逻辑
*/
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
/**
* 5.倒序执行 拦截器链条中的所有后置方法
*/
mappedHandler.applyPostHandle(processedRequest, response, mv);
}}
//...
流程解析
由于Filter依赖于Servlet容器所以不同的容器Filter,FilterChain的实现类存在差异,这里以Tomcat为例分析
1.向后台发起一次请求
2.接待线程接收到,将任务转交给工作线程
3.判断协议,封装必要对象
4.将request,response一路转交至StandardWrapperValve.invoke(Request request, Response response)
5.创建过滤链ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
5.1从上下文中获取注册好的过滤器
5.2遍历过滤器,匹配URL,Servlet等,将匹配好的过滤器加入到过滤器链
6.依次回调FilterChain的doFilter方法filterChain.doFilter(request.getRequest(),response.getResponse());
7.将所有过滤器的前置代码执行完毕,进入servlet,servlet.service(request, response);
8.进入DispatcherServlet
统一调度
9.调用拦截器前置方法
10.进入controller中的对应方法,执行具体的业务逻辑
11.调用拦截器后置(数组倒序执行)
12.将所有过滤器的后置代码执行完毕(方法栈,先进后出)
13.将结果返回给请求者
注意,过滤器和拦截器实现先进后出的实现方式是不同的,过滤器基于函数回调,方法栈结构天生支持先进后出;拦截器则是直接使用循环倒序遍历
总结
同:
- 过滤器和拦截器都遵循面向切面编程的思想(AOP),实现了在请求到达目标处理器(Servlet/Controller)之前或者之后插入自定义的处理逻辑
异:
-
使用范围不同
过滤器实现的是
javax.servlet.Filter
该接口在Servlet
规范中定义,依赖WEB容器拦截器是一个Spring组件,由Spring管理,并不依赖Tomcat容器,可以单独使用(Application,Swing)
-
使用的场景不同
拦截器更加接近业务系统,所以拦截器更适用于处理统一的业务逻辑,比如权限判断等
过滤器通常用来实现通用功能,比如xss过滤,敏感词,处理跨域等等
-
触发的时机不同
过滤器的触发时机早于拦截器
-
底层实现细节不同
过滤器实现先进后出基于方法栈的数据结构
拦截器实现先进后出基于循环倒序遍历
1.本站内容仅供参考,不作为任何法律依据。用户在使用本站内容时,应自行判断其真实性、准确性和完整性,并承担相应风险。
2.本站部分内容来源于互联网,仅用于交流学习研究知识,若侵犯了您的合法权益,请及时邮件或站内私信与本站联系,我们将尽快予以处理。
3.本文采用知识共享 署名4.0国际许可协议 [BY-NC-SA] 进行授权
4.根据《计算机软件保护条例》第十七条规定“为了学习和研究软件内含的设计思想和原理,通过安装、显示、传输或者存储软件等方式使用软件的,可以不经软件著作权人许可,不向其支付报酬。”您需知晓本站所有内容资源均来源于网络,仅供用户交流学习与研究使用,版权归属原版权方所有,版权争议与本站无关,用户本人下载后不能用作商业或非法用途,需在24个小时之内从您的电脑中彻底删除上述内容,否则后果均由用户承担责任;如果您访问和下载此文件,表示您同意只将此文件用于参考、学习而非其他用途,否则一切后果请您自行承担,如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。
5.本站是非经营性个人站点,所有软件信息均来自网络,所有资源仅供学习参考研究目的,并不贩卖软件,不存在任何商业目的及用途
暂无评论内容