博主最近看了一下公司刚刚开发的微服务,准备入手从基本的过滤器以及拦截器开始剖析,以及在帮同学们分析一下上次的jetty过滤器源码与本次Springboot中tomcat中过滤器的区别。正题开始,拦截器顾名思义是进行拦截请求的一系列操作。先给大家示例一下使用操作

 @Configuration
public class WebConfiguration implements WebMvcConfigurer { @Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new TstCfg());
}
}

配置示例

 /**
* @title: TstCfg
* @Author junyu
* 旧巷里有一个穿着白衬衫笑起来如太阳般温暖我的少年。
* 记忆里有一个穿着连衣裙哭起来如孩子般讨人喜的女孩。
* 他说,哪年树弯了腰,人见了老,桃花落了白发梢,他讲的笑话她还会笑,那便是好。
* 她说,哪年国改了号,坟长了草,地府过了奈何桥,她回头看时他还在瞧,就不算糟。
* @Date: 2020/7/29 11:53
* @Version 1.0
*/
public class TstCfg extends HandlerInterceptorAdapter { @Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("前");
return super.preHandle(request, response, handler);
} @Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("后");
} @Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("一直会出现");
System.out.println(1/0);
}
}

拦截器示例

  首先我们可能会想到,我们的拦截器是何时装配到拦截器数组中

  其实就是在springboot启动时执行doCreateBean时,进行调用创建的org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$EnableWebMvcConfiguration会在这里放入进去所有实现了WebMvcConfigurer接口的类,一共有7个,其中就有我们自己实现了WebMvcConfigurer接口的WebConfiguration类,

  我们的写的配置类WebConfiguration,继承了WebMvcConfigurer并重写了addInterceptors方法,所以我们的拦截器就在这时候装配进去了。这次知道为什么我们写的配置拦截器的配置示例需要继承------WebMvcConfigurer,我们当然也可以去继承已经实现了这个类的其他类,因为都可以去添加拦截器,博主亲试过,所以就不贴图了!

 

   好了,拦截器已经添加完了,那什么时候调用我们拦截器呢?一步一步脚印来,当浏览器请求我们地址的 时候,分一下几步:

 第一步:tomcat容器首先会接受到请求,这里将会走DispatcherServlet,看到这个大家都熟悉了。

第二步:当然不会先走我们的拦截器了,我们的拦截器是在Springboot框架进行管理的,现在还在servlet,所以会先走到filter过滤器这一步,来贴图:官方代码太长,一屏截不下,前面有一个创建过滤器链的过程:等下次在给大家讲一下jetty的过滤器链与tomcat的过滤器链的区别

ApplicationFilterChain filterChain = ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);

第三步:所以一旦连过滤器都没通过的话,会直接return回去,不会再进行拦截器的调用。来贴代码,过滤器通过后如何调用我们拦截器的

 private void internalDoFilter(ServletRequest request,
ServletResponse response)
throws IOException, ServletException {
//这里将会调用所有过滤器链的过滤器,不做重点讲解了,看看下面拦截器的调用
// Call the next filter if there is one
if (pos < n) {
ApplicationFilterConfig filterConfig = filters[pos++];
try {
Filter filter = filterConfig.getFilter(); if (request.isAsyncSupported() && "false".equalsIgnoreCase(
filterConfig.getFilterDef().getAsyncSupported())) {
request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, Boolean.FALSE);
}
if( Globals.IS_SECURITY_ENABLED ) {
final ServletRequest req = request;
final ServletResponse res = response;
Principal principal =
((HttpServletRequest) req).getUserPrincipal(); Object[] args = new Object[]{req, res, this};
SecurityUtil.doAsPrivilege ("doFilter", filter, classType, args, principal);
} else {
filter.doFilter(request, response, this);
}
} catch (IOException | ServletException | RuntimeException e) {
throw e;
} catch (Throwable e) {
e = ExceptionUtils.unwrapInvocationTargetException(e);
ExceptionUtils.handleThrowable(e);
throw new ServletException(sm.getString("filterChain.filter"), e);
}
return;
} // We fell off the end of the chain -- call the servlet instance
try {
if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
lastServicedRequest.set(request);
lastServicedResponse.set(response);
} if (request.isAsyncSupported() && !servletSupportsAsync) {
request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR,
Boolean.FALSE);
}
// Use potentially wrapped request from this point
if ((request instanceof HttpServletRequest) &&
(response instanceof HttpServletResponse) &&
Globals.IS_SECURITY_ENABLED ) {
final ServletRequest req = request;
final ServletResponse res = response;
Principal principal =
((HttpServletRequest) req).getUserPrincipal();
Object[] args = new Object[]{req, res};
SecurityUtil.doAsPrivilege("service",
servlet,
classTypeUsedInService,
args,
principal);
} else {
//过滤器终于完事了,现在终于开始正式调用我们的方法了,我们看看service方法做了什么吧!
servlet.service(request, response);
}
} catch (IOException | ServletException | RuntimeException e) {
throw e;
} catch (Throwable e) {
e = ExceptionUtils.unwrapInvocationTargetException(e);
ExceptionUtils.handleThrowable(e);
throw new ServletException(sm.getString("filterChain.servlet"), e);
} finally {
if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
lastServicedRequest.set(null);
lastServicedResponse.set(null);
}
}
}

调用servlet的service方法

其实最终它会调用到DispatcherServlet的doDispatch方法

 protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
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); // Determine handler for the current request.
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 = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
//所有拦截器开始在调用方法前拦截,如果你拦截器中返回false,则直接return不会再调用该方法!下面有源代码
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
} // Actually invoke the handler.
//底层进行invoke反射,调用当前请求的方法,不用再往里面看了
mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) {
return;
} applyDefaultViewName(processedRequest, mv);
//调用拦截器的postHandle,下面有源代码
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
//该方法中多做了一些逻辑,其实最后也调用了triggerAfterCompletion方法,最终调用拦截器方法的afterCompletion方法
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
//所以不管是否出现异常,拦截器方法的afterCompletion方法是一定会调用的!
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
//所以不管是否出现异常,拦截器方法的afterCompletion方法是一定会调用的!
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}

doDispatch方法,拦截器重点

现在终于开始了我们拦截器的方法了,一个一个来:

 boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = 0; i < interceptors.length; i++) {
HandlerInterceptor interceptor = interceptors[i];
//调用所有拦截器的preHandle方法
if (!interceptor.preHandle(request, response, this.handler)) {
//就算preHandle方法没有通过,仍然会调用这个triggerAfterCompletion方法。
triggerAfterCompletion(request, response, null);
return false;
}
this.interceptorIndex = i;
}
}
return true;
}

applyPreHandle

 void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
throws Exception { HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = interceptors.length - 1; i >= 0; i--) {
HandlerInterceptor interceptor = interceptors[i];
//调用拦截器的postHandle方法,
interceptor.postHandle(request, response, this.handler, mv);
}
}
}

applyPostHandle

 void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex)
throws Exception { HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = this.interceptorIndex; i >= 0; i--) {
HandlerInterceptor interceptor = interceptors[i];
try {
//调用拦截器的afterCompletion方法,不管是否异常都会进行调用,但是如果该方法报异常,会被抓住。
//不会影响程序正常运行,只会打印出来
interceptor.afterCompletion(request, response, this.handler, ex);
}
catch (Throwable ex2) {
logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
}
}
}
}

triggerAfterCompletion

下面这个就是打印了一下,但是不会影响我们的请求响应回去:

还是会正常响应回客户端:

好了,到此拦截器的实现以及源码分析流程到此结束,本来想给大家从Springboot的reflash方法开始解析拦截器,但是内容太多了,不仅跑题而且博主也一时半会给大家无法讲解明白。


Springboot拦截器使用及其底层源码剖析的更多相关文章

  1. 详解Mybatis拦截器(从使用到源码)

    详解Mybatis拦截器(从使用到源码) MyBatis提供了一种插件(plugin)的功能,虽然叫做插件,但其实这是拦截器功能. 本文从配置到源码进行分析. 一.拦截器介绍 MyBatis 允许你在 ...

  2. 2018.11.20 Struts2中对结果处理方式分析&struts2内置的方式底层源码剖析

    介绍一下struts2内置帮我们封装好的处理结果方式也就是底层源码分析 这是我们的jar包里面找的位置目录 打开往下拉看到result-type节点 name那一列就是我们的type类型取值 上一篇博 ...

  3. python部分重点底层源码剖析

    Python源码剖析—Set容器(hashtable实现) python源码剖析(内存管理和垃圾回收)

  4. SpringMVC拦截器详解[附带源码分析]

    目录 前言 重要接口及类介绍 源码分析 拦截器的配置 编写自定义的拦截器 总结 总结 前言 SpringMVC是目前主流的Web MVC框架之一. 如果有同学对它不熟悉,那么请参考它的入门blog:h ...

  5. [转]SpringMVC拦截器详解[附带源码分析]

      目录 前言 重要接口及类介绍 源码分析 拦截器的配置 编写自定义的拦截器 总结 前言 SpringMVC是目前主流的Web MVC框架之一. 如果有同学对它不熟悉,那么请参考它的入门blog:ht ...

  6. SpringMVC拦截器详解[附带源码分析](转)

    本文转自http://www.cnblogs.com/fangjian0423/p/springMVC-interceptor.html 感谢作者 目录 前言 重要接口及类介绍 源码分析 拦截器的配置 ...

  7. struts2 paramsPrepareParamsStack拦截器简化代码(源码分析)

    目录 一.在讲 paramsPrepareParamsStack 之前,先看一个增删改查的例子. 1. Dao.java准备数据和提供增删改查 2. Employee.java 为model 3. E ...

  8. struts2内置拦截器和自定义拦截器详解(附源码)

    一.Struts2内置拦截器 Struts2中内置类许多的拦截器,它们提供了许多Struts2的核心功能和可选的高级特 性.这些内置的拦截器在struts-default.xml中配置.只有配置了拦截 ...

  9. HashMap底层源码剖析

    HashMap底层源码剖析 一.HashMap底层用到的数据结构 数组+单向链表+红黑树 数组:数组每一项都是一个链表,其实就是数组和链表的结合体 单向链表:当法神hash碰撞时,首先会找到数组对应位 ...

随机推荐

  1. POJ1017贪心

    题意:小P开了一家淘宝店铺,店铺里所有的商品高度都为h,但长和宽分别为1*1,2*2,3*3,4*4,5*5,6*6六种规格.这一天来了一个大客户,他订购了很多物品.所以小P需要将东西都邮寄给他,但是 ...

  2. 深入浅出了解CDN

    背景: 本文公众号来源:漫话编程 虎牙直播2000W+人在线观看,直播间还不卡,据了解,2018年1月,阿里云为虎牙提供了边缘节点服务(ENS).基于阿里云ENS,可以轻松地将业务模块放到边缘运行,在 ...

  3. WPF 精准大小的消息气泡生成

    在其他网站发布过,都是这里写的. 全部为Wpf方式,生成的气泡宽度高度较为精确. 适用任意字体以及字号,可设置图像字体.字号.行间距.字间距.阴影.气泡宽度等,在RichtextboxHelper.c ...

  4. Aspose下载图片

    /// <summary> /// 把DataTable数据按照Excel模板导出到Excel /// </summary> /// <param name=" ...

  5. day42 io模型

    目录 一.io模型简介 二.阻塞io阻塞IO模型图.png 三.非阻塞io 四.io多路复用 五.异步io 一.io模型简介 Stevens在文章中一共比较了五种IO Model: blocking ...

  6. flask 源码专题(三):请求上下文和应用上下文入栈与出栈

    1.请求上下文和应用上下文入栈 # 将ctx入栈,但是内部也将应用上下文入栈 ctx.push() def push(self): # 获取到的 top == ctx top = _request_c ...

  7. java 面向对象(十五):面向对象的特征三:多态性

    1.多态性的理解:可以理解为一个事物的多种形态.2.何为多态性:对象的多态性:父类的引用指向子类的对象(或子类的对象赋给父类的引用)举例:Person p = new Man();Object obj ...

  8. scrapy 源码解析 (五):启动流程源码分析(五) Scraper刮取器

    Scraper刮取器 对ExecutionEngine执行引擎篇出现的Scraper进行展开.Scraper的主要作用是对spider中间件进行管理,通过中间件完成请求.响应.数据分析等工作. Scr ...

  9. 前端01 /HTML简单简绍

    前端01 /HTML简单简绍 目录 前端01 /HTML简单简绍 1.web服务本质 2.浏览器的工作流程 3.HTML是什么 4.web服务本质 5.HTML文档结构 6.HTML注释 6.标签语法 ...

  10. vue 写h5页面-摇一摇

    依赖的第三方的插件 shake.js github地址: https://github.com/alexgibson/shake.js 提供一个摇一摇音效下载地址:http://aspx.sc.chi ...