从源码角度分析SpringMVC执行流程

ops/2025/1/15 15:13:35/
webkit-tap-highlight-color: rgba(0, 0, 0, 0);">

文章目录

  • 一、SpringMVC基本概述
  • 二、SpringMVC的执行流程
  • 三、SpringMVC源码的执行流程
  • 四、前端控制器根据请求获取处理器原理
  • 五、如何根据处理器获取处理器适配器
  • 六、SpringMVC拦截器执行源码解读
  • 七、处理器适配器执行方法原理

一、SpringMVC基本概述

SpringMVC是基于Servlet进行的封装框架,是一个Spring框架的一个模块。它以SpringIOC容器为基础,并利用容器的特性来简化它的配置,所以 SpringMVC Spring 可直接整合使用。
SpringMVC也是一个容器,使用IoC核心技术,管理界面层中的控制器对象。SpringMVC的底层就是servlet,以servlet为核心,接收请求、处理请求,显示处理结果给用户。在此之前这个功能是由Servlet来实现的,现在使用SpringMVC来代替Servlet行驶控制器的角色和功能。其核心Servlet是:DispatcherServlet

SpringMVC中的核心组件:

  1. 前端控制器:DispatcherServlet,本质是一个Servlet,底层实质是继承了HttpServlet,用于接受请求,响应结果,相当于是转发器,是SpringMVC框架中最核心的组件,有了它就能减少其他组件之间的耦合度
  2. 处理器映射器HandlerMapping,专门负责映射的,根据请求路径去映射处理器方法。
  3. 处理器适配器HandlerAdapter,适配调用具体的处理器,解析请求中的参数,并且执行处理器中的方法,执行完成之后返回一个ModelAndView对象。
  4. 处理器Handler,就是我们写的方法。
  5. 视图解析器ViewResolver,会根据传递过来的ModelAndView对象进行视图解析,根据视图逻辑名称装换成为视图的物理名称View
  6. 视图View是一个接口,它的实现类支持不同类型的视图。比如:JSP、freemarker、Thymeleaf等等。

二、SpringMVC的执行流程

在这里插入图片描述

  1. 用户发送请求,根据请求会分发到DispatcherServlet中。
  2. DispatcherServlet会根据请求Request的请求路径以及请求方式找到处理器Handler并且返回。底层实质返回的是一个HandlerExecutionChain处理器执行链对象,里面封装了本次请求要执行的处理器方法以及所有的拦截器。
  3. DispatcherServlet根据Handler匹配到对应的处理器适配器HandlerAdapter,实现方式是循环遍历DispatcherServlet中的所有处理器适配器,根据Handler的类型进行适配。
  4. 执行本次请求所有拦截器的preHandle方法。
  5. 调用处理器适配器的handle方法,处理器适配器会从请求中获取参数,执行处理器方法,并且最终返回一个ModelAndView对象。
  6. 执行本次请求所有拦截器的postHandle方法。
  7. 通过视图解析器对ModelAndView进行解析,将响应视图的逻辑位置解析成真正的物理位置,并且返回View对象。
  8. 视图进行渲染,并且执行本次请求的所有拦截器的afterCompletion方法。
  9. 响应给前端。

三、SpringMVC源码的执行流程

DispatcherServlet前端控制器

java">public class DispatcherServlet extends FrameworkServlet {//初始化的时候会将所有HandlerMapping都封装在这个List里面private List<HandlerMapping> handlerMappings;// 前端控制器最核心的方法,这个方法是负责处理请求的,每次发送请求时,都会调用这个方法,这个方法实质是FrameworkServlet继承了HttpServlet,重写其service(HttpServletRequest req, HttpServletResponse resp)方法protected void doDispatch(HttpServletRequest processedRequest, HttpServletResponse response) throws Exception {//根据请求路径,来获取对应的要执行的处理器,其本质是通过请求的uri和请求方式进行匹配到对应的处理器//返回的是一个处理器执行链对象,这个对象就是本次请求所要执行的所有拦截器和处理器//本质是通过调用HandlerMapping的handler方法//HandlerExecutionChain是一次请求一个对象HandlerExecutionChain mappedHandler mappedHandler = getHandler(processedRequest);//根据处理器获取处理器适配器//Handler实质就是我们写的方法HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());//执行该请求对应的所有拦截器的preHandler方法if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}//调用处理器方法,返回ModelAndView//在执行处理器方法之前,需要给处理器方法传参数,这个方法会对处理器进行数据绑定,即将请求参数封装到处理器方法中mv = ha.handle(processedRequest, response, mappedHandler.getHandler());//执行该请求所有拦截器中的postHandle方法mappedHandler.applyPostHandle(processedRequest, response, mv);//处理分发结果,本质就是响应结果到浏览器processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);}//处理分发结果private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {//渲染render(mv, request, response);	//执行该请求所有拦截器中的afterCompletion方法mappedHandler.triggerAfterCompletion(request, response, null);}protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {//通过视图解析器进行解析,返回视图View对象View view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);//调用视图对象的渲染方法,完成响应view.render(mv.getModelInternal(), request, response);}//解析视图名称protected View resolveViewName(String viewName, Map<String, Object> model, Locale locale,HttpServletRequest request) throws Exception {//可以配置多个视图解析器,例如ThymeleafViewResolver,InternalResourceViewResolver......for (ViewResolver viewResolver : this.viewResolvers) {//通过视图解析器进行解析,返回视图对象ViewView view = viewResolver.resolveViewName(viewName, locale);if (view != null) {return view;}}return null;}
}

视图解析器接口

java">// 视图解析器有很多实现类,例如ThymeleafViewResolver,InternalResourceViewResolver......
public interface ViewResolver {//根据视图逻辑名称获取视图物理名称,返回视图对象View resolveViewName(String viewName, Locale locale) throws Exception;
}

视图接口

java">//视图接口,例如实现类ThymeleafView,InternalResourceView......
public interface View {//这里是真正的渲染void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception;
}

DispatcherServlet执行原理

在这里插入图片描述

总结:

  1. 浏览器发送请求,根据配置的web.xml文件找到DispatcherServlet
  2. DispatcherServlet实质是继承了HttpServlet,重写其service方法,service方法然后调用DispatcherServlet最核心的doDispatch方法
  3. doDispatch方法首先会根据请求Request信息,根据请求路径,来获取对应的要执行的处理器,实质返回的是一个处理器执行链
    • 什么是处理器映射器(HandlerMapping)?
      专门负责映射的,根据请求路径去映射处理器方法。
      下面有很多实现类,例如专门处理@RequestMapping注解的RequestMappingHandlerMapping
      SpringMVC初始化处理器映射器会解析所有带有@Controller的类,将所有带有@RequestMapping和其延申注解(@GetMapping@PostMapping)的方法封装成HandlerMethod对象并且以Map形式封装在处理器映射器中。同时所有的拦截器也会封装在拦截器处理器中。

    • 什么是执行链?
      HandlerExecutionChain:里面封装了这次请求对应的处理器方法,以及这次请求的所有要执行的拦截器。

    • 什么是处理器方法?
      HandlerMethod:里面封装了我们Controller的字节码、Controller对应方法的Method对象以及需要执行方法所需的参数。

    • 如何获取执行链的:
      因为HandlerMapping在SpringMVC启动的时候就已经初始化好了,HandlerMapping内部其实是根据请求的uri与所有的HandlerMethod进行匹配,同理拦截器也是如此。具体实现方式可以参考本文AbstractHandlerMapping部分源码。

四、前端控制器根据请求获取处理器原理

获取处理器代码 HandlerExecutionChain mappedHandler mappedHandler = getHandler(processedRequest);

前端控制器 DispatcherServlet

java">//前端控制器,SpringMVC最核心的类
public class DispatcherServlet extends FrameworkServlet {//在我们SpringMVC有多种Handler实现方式,有通过@RequestMapping注解,也有通过继承HttpRequestHandler来实现的,//针对不同的Handler会有不同的HandlerMapping来进行处理。使用注解对应的处理器映射器是RequestMappingHandlerMapping,//使用HttpRequestHandler继承对应的处理器映射器是BeanNameUrlHandlerMapping。//SpringMVC初始化的时候会将所有HandlerMapping都封装在这个List里面private List<HandlerMapping> handlerMappings;//根据请求获取处理器,实质返回的是一个处理器执行链protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {//遍历所有的处理器映射器for (HandlerMapping mapping : this.handlerMappings) {//调用HandlerMapping的getHandler方法匹配对应的处理器和拦截器HandlerExecutionChain handler = mapping.getHandler(request);if (handler != null) {return handler;}}return null;}
}

处理器映射 HandlerMapping

java">/*** 处理器映射器,专门负责映射的,根据请求路径去映射处理器方法* 这个接口下面有很多实现类,使用注解对应的处理器映射器是RequestMappingHandlerMapping。* 使用HttpRequestHandler继承对应的处理器映射器是BeanNameUrlHandlerMapping。*/
public interface HandlerMapping {//根据请求信息,获取处理器执行链HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}

处理器映射器抽象类 AbstractHandlerMapping

java">/*** 处理器映射抽象类* 本文仅对RequestMappingHandlerMapping处理器映射器进行源码解读*/
public abstract class AbstractHandlerMapping implements HandlerMapping{//所有拦截器,SpringMVC初始化的时候会把所有的拦截器都放在这个List里面private final List<HandlerInterceptor> adaptedInterceptors = new ArrayList<HandlerInterceptor>();//根据请求查询处理器执行链public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {//实质是调用AbstractHandlerMethodMapping的getHandlerInternal方法//实质获取的是处理器方法RequestMethod对象Object handler = getHandlerInternal(request);//这个方法会去匹配拦截器,并且将一次请求所有要执行的拦截器和处理器方法封装起来返回一个处理器执行链HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);return executionChain;}//匹配拦截器protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {//创建一个处理器执行链HandlerExecutionChain chain = new HandlerExecutionChain(handler);//获取请求uriString lookupPath = this.urlPathHelper.getLookupPathForRequest(request);//遍历循环所有拦截器for (HandlerInterceptor interceptor : this.adaptedInterceptors) {MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;//拦截器根据当前请求uri进行和拦截器配置的拦截路径匹配if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {//将拦截器加入到处理器执行链中chain.addInterceptor(mappedInterceptor.getInterceptor());}}}
}

处理器方法映射抽象类 AbstractHandlerMethodMapping

java">public abstract class AbstractHandlerMethodMapping<T>  extends AbstractHandlerMapping{	//SpringMVC初始化的时候会对这里进行处理,将所有处理器加载进来private final MappingRegistry mappingRegistry = new MappingRegistry();//内部类class MappingRegistry {private final Map<T, MappingRegistration<T>> registry = new HashMap<T, MappingRegistration<T>>();//SpringMVC初始化的时候做了处理,将所有的处理器都进行封装成了uri->List<RequestMappingInfo>格式的Map//RequestMappingInfo是请求映射详情,里面封装了每个处理器的详细信息,比如请求方式,请求路径,请求所需要的参数等等//RequestMappingInfo提供了一个getMatchingCondition(HttpServletRequest request)方法,根据请求判断是否与这个映射匹配,其本质就是根据Request中的请求信息与ReqeustMappingInfo的信息进行逐一比较。private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<String, T>();//SpringMVC初始化的时候做了处理,将所有的处理器封装成了RequestMappingInfo->HandlerMethod格式的Map,方便查找。private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<T, HandlerMethod>();//根据请求的uri,获取对应的请求映射详情RequestMappingInfopublic List<T> getMappingsByUrl(String urlPath) {return this.urlLookup.get(urlPath);}//根据请求映射详情ReqeustMappingInfo获取处理器方法public Map<T, HandlerMethod> getMappings() {return this.mappingLookup;}}//映射注册,处理器的各种信息封装在里面private static class MappingRegistration<T> {//在使用@RequestMapping注解方式情况下,这里实质是一个ReqeustMappingInfoprivate final T mapping;//处理器方法private final HandlerMethod handlerMethod;//映射路径private final List<String> directUrls;private final String mappingName;}//根据请求获取对应的处理器protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {//根据请求获取请求uriString lookupPath = getUrlPathHelper().getLookupPathForRequest(request);//根据请求的uri匹配处理器方法HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);}//根据请求路径匹配处理器器方法protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {List<Match> matches = new ArrayList<Match>();//根据请求uri,获取匹配的RequestMappingInfo,由于同一个uri存在相同的处理器,所以这里返回的是ListList<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);//再根据当前请求进行匹配(其实就是根据请求参数,请求方式等进行过滤RequestMappingInfo,然后根据RequestMappingInfo找到映射方法HandlerMethod),并且封装到matches集合里面addMatchingMappings(directPathMatches, matches, request);//其实这里省略了bestMatch 的长度判断,如果大于1会报错。Match bestMatch = matches.get(0);//返回HandlerMethod对象return bestMatch.handlerMethod;}//根据请求信息获取匹配的处理器方法,并且封装成Match对象加入matches集合中private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {//针对注解实现的方式,这里的泛型就是ReqeustMappingInfo//遍历传入的请求映射详情for (T mapping : mappings) {//根据RequestMappingInfo和请求信息进行匹对,判断当前映射是否满足请求。T match = getMatchingMapping(mapping, request);if (match != null) {//根据请求映射详情RequestMappingInfo找到处理器方法HandlerMethod,并且创建一个Matche对象封装到集合中matches.add(new Match(match, this.mappingRegistry.getMappings().get(mapping)));}}}//映射工具类private class Match {private final T mapping;private final HandlerMethod handlerMethod;}
}
  • RequestMappingInfoHandlerMapping
java">public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMethodMapping<RequestMappingInfo> {//根据请求信息和RequestMappingInfo判断当前请求映射是否匹配此次请求,如果符合返回该请求映射//注意,由于请求映射在项目初始化的时候就已经创建了,但是返回的时候是采用创建一个新的RequestMappingInfo对象返回的protected RequestMappingInfo getMatchingMapping(RequestMappingInfo info, HttpServletRequest request) {return info.getMatchingCondition(request);}
}
  • RequestMappingHandlerMapping
java">//请求映射处理器,针对使用了@RequestMapping注解的请求方式
public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping {}
  • 请求映射详情 RequestMappingInfo
java">//这个类是请求映射详情,里面封装了需要执行处理器所需要的信息,比如请求方式,请求参数
//比如我们定义了  @RequestMapping(value = "/user/findUserById",method = RequestMethod.GET)
//那么methodsCondition里面就是GET,这个类就是将我们写的Controller所需要的请求方式,请求rui,请求参数进行封装
public final class RequestMappingInfo{//下面这些变量都是这个请求映射执行所需要的参数信息封装在这里private final PatternsRequestCondition patternsCondition;private final RequestMethodsRequestCondition methodsCondition;private final ParamsRequestCondition paramsCondition;private final HeadersRequestCondition headersCondition;private final ConsumesRequestCondition consumesCondition;private final ProducesRequestCondition producesCondition;private final RequestConditionHolder customConditionHolder;//根据请求信息,判断当前的RequestMappingInfo是否满足请求,如果请求就返回一个RequestMappingInfo,如果不满足返回nullpublic RequestMappingInfo getMatchingCondition(HttpServletRequest request) {//根据请求判断当前的请求是否和requestMappingInfo对象的请求一致,底层逻辑就是request.getMethod方法获取当前请求方式RequestMethodsRequestCondition methods = this.methodsCondition.getMatchingCondition(request);ParamsRequestCondition params = this.paramsCondition.getMatchingCondition(request);HeadersRequestCondition headers = this.headersCondition.getMatchingCondition(request);ConsumesRequestCondition consumes = this.consumesCondition.getMatchingCondition(request);ProducesRequestCondition produces = this.producesCondition.getMatchingCondition(request);if (methods == null || params == null || headers == null || consumes == null || produces == null) {return null;}PatternsRequestCondition patterns = this.patternsCondition.getMatchingCondition(request);if (patterns == null) {return null;}RequestConditionHolder custom = this.customConditionHolder.getMatchingCondition(request);if (custom == null) {return null;}//如果一致,这里创建一个对象返回return new RequestMappingInfo(this.name, patterns,methods, params, headers, consumes, produces, custom.getCondition());}
}

HandlerExecutionChain 处理器执行链

java">//处理器链执行链
public class HandlerExecutionChain {//处理器,就是我们写的方法//这里使用Object的原因是因为处理器有很多种,//如果使用的是@RequestMapping注解的方式,底层实质是一个处理器方法对象HandlerMethod,这个处理器方法对象封装了名称以及对应的处理器方法//HandlerMethod:处理器方法是在web服务器启动时初始化就创建好了的,这个类当中比较重要的属性包括:beanName和Method方法//如果我们是采用继承HttpRequestHandler方式,这个handler就是我们写的Controller,实质还是一个HttpRequestHandlerprivate final Object handler;//该请求对应的所有拦截器按照顺序放到了List集合中private List<HandlerInterceptor> interceptorList;//拦截器下标private int interceptorIndex = -1;
}//处理器方法,这个是专门针对@RequestMapping注解的方式,里面封装了我们写的方法信息
public class HandlerMethod {//执行方法所有的类字节码private final Class<?> beanType;//实际要执行的方法private final Method method;//处理器方法所有请求参数信息private final MethodParameter[] parameters;
}

SpringMVC初始化处理器的结构

在这里插入图片描述

总结:

  1. SpringMVC实现处理器的方式有多种,针对不同的方式会有多种不同的处理器映射器。比如使用注解@RequstMapping实现处理器的处理器映射器就是RequestMappingHandlerMapping
  2. SpingMVC初始化的时候会将所有的处理器映射器都创建出来,并且放到DispacherServlethandlerMappings变量里。
  3. 针对RequestMappingHandlerMapping处理器初始化时,会将所有的处理器基本信息如请求地址,请求方式,请求参数等封装成RequestMappingInfo对象。将所有处理器的类字节码Class<?> beanType,请求方法Method,请求参数MethodParameter[] parameters封装成为HandlerMethod对象。然后将RequestMappingInfo作为key,HandlerMethod作为Value封装为Map放在处理器映射器的变量里。
  4. 创建RequestMappingHandlerMapping的同时,会将所有的拦截器放在处理器映射器变量中。
  5. 前端控制器循环遍历所有处理器映射器HandlerMapping,并且调用其中getHandler方法匹配对应的处理器。
  6. getHandler方法会根据请求uri,获取对应的RequestMappingInfo,然后再根据RequestMappingInfo作为key获取到处理器方法HandlerMethod`并且返回。
  7. 循环遍历所有的拦截器,并且根据当前请求路径与拦截器配置的路径相匹配,拿到本次请求要执行的所有拦截器集合。
  8. 将处理器方法和本次请求所有要执行的拦截器封装成拦截器执行链HandlerExecutionChain

五、如何根据处理器获取处理器适配器

获取处理器适配器HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

前端控制器 DispatcherServlet

java">//前端控制器,SpringMVC最核心的类
public class DispatcherServlet extends FrameworkServlet {private List<HandlerMapping> handlerMappings;//SpringMVC初始化的时候会将所有的处理器适配器创建出来,并且封装到handlerAdapters中private List<HandlerAdapter> handlerAdapters;//根据处理器获取对应的处理器适配器protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {//遍历适配器,不同处理器的适配器不一样for (HandlerAdapter ha : this.handlerAdapters) {	//适配器调用supports方法,判断当前处理器是否和适配器匹配if (ha.supports(handler)) {return ha;}}}
}

HandlerAdapter 处理器适配器

java">/*** 映射器适配器,我们常用到的实现类有RequestMappingHandlerAdapter......* 因SpringMVC中的Handler可以有多种实现方式,但是Servlet需要的处理方法的结构确实固定的,* 都是以为Request和Response作为入参,那么如何让固定参数中的Servlet处理方法调用灵活的Handler来处理呢,这就是需要Handler来作适配。*/ 
public interface HandlerAdapter {//传入处理器,判断当前处理器适配器是否匹配//初始化DispatcherServlet的时候会创建所有的处理器适配器,会有多个处理器适配器。//会挨个遍历调用supports查询处理器方法和哪个处理器适配器适配。//他是怎么查询的呢,前面我们提到过不同的处理器映射器所对应的适配器类型不同//RequestMappingHanlderMapping处理器类是HandlerMethod,//BeanNameUrlHandlerMapping处理器实现类是HttpRequestHandler,底层是根据这个类型进行判断的。boolean supports(Object handler);
}

AbstractHandlerMethodAdapter

java">/*** 针对@ReqeustMapping注解的处理器适配器*/
public abstract class AbstractHandlerMethodAdapter{//根据传入的处理器,判断当前适配器是否适配该处理器,HandlerMethodAdapterpublic final boolean supports(Object handler) {return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));}
}

RequestMappingHandlerAdapter

java">public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter{protected boolean supportsInternal(HandlerMethod handlerMethod) {return true;}
}

控制器处理器初始化

在这里插入图片描述
总结:

  1. 底层使用了适配器模式。
  2. 每个处理器都有自己适合的处理器适配器。
  3. 在SpringMVC当中处理器适配器也有很多种,其中一个比较有名的处理器适配器是:RequestMappingHandlerAdapter
    这个处理器是适配器是专门处理处理器方法上有 @RequestMapping 注解的。
  4. HandlerAdapter也是一个接口,其中一个比较常用的实现类:RequestHandlerAdapter。
  5. 在服务器启动阶段,所有的HandlerAdapter接口实现类都会创建出来,放在DispatcherServlet 类的List<HandlerAdapter> handlerAdapters 中。
  6. HandlerAdapter接口非常重要,通过他来调用最终的HandlerMethod
  7. HandlerAdapter是适配器,是对HandlerMethod进行的适配。

六、SpringMVC拦截器执行源码解读

DispatcherServlet

java">//前端控制器,SpringMVC最核心的类部分代码
public class DispatcherServlet extends FrameworkServlet {protected void doDispatch(HttpServletRequest processedRequest, HttpServletResponse response){//执行该请求对应的所有拦截器的preHandler方法if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}//执行该请求所有拦截器中的postHandle方法mappedHandler.applyPostHandle(processedRequest, response, mv);//执行该请求所有拦截器中的afterCompletion方法mappedHandler.triggerAfterCompletion(request, response, null);}
}

HandlerExecutionChain

java">/*** 处理器执行链对象*/
public class HandlerExecutionChain {//本次请求所有要执行的拦截器private List<HandlerInterceptor> interceptorList;//本次请求所有要执行的拦截器 数组格式,和interceptorList是一样的private HandlerInterceptor[] interceptors;//最后执行的拦截器下标private int interceptorIndex = -1;//执行处理器执行链中所有拦截器的preHandle方法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];if (!interceptor.preHandle(request, response, this.handler)) {//如果拦截器拦截了,直接执行afterCompletion方法triggerAfterCompletion(request, response, null);return false;}this.interceptorIndex = i;}}return true;}//执行处理器执行链中所有拦截器的postHandle方法void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception {HandlerInterceptor[] interceptors = getInterceptors();if (!ObjectUtils.isEmpty(interceptors)) {//postHandle是根据拦截器配置的反序执行for (int i = interceptors.length - 1; i >= 0; i--) {HandlerInterceptor interceptor = interceptors[i];interceptor.postHandle(request, response, this.handler, mv);}}}//执行处理器执行链中所有拦截器的afterCompletion方法void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex)throws Exception {HandlerInterceptor[] interceptors = getInterceptors();if (!ObjectUtils.isEmpty(interceptors)) {//interceptorIndex此时值是最大下标,所以afterCompletion也是反序执行的for (int i = this.interceptorIndex; i >= 0; i--) {HandlerInterceptor interceptor = interceptors[i];interceptor.afterCompletion(request, response, this.handler, ex);				}}}//获取处理器执行链中的所有拦截器,返回的是一个数组public HandlerInterceptor[] getInterceptors() {if (this.interceptors == null && this.interceptorList != null) {this.interceptors = this.interceptorList.toArray(new HandlerInterceptor[this.interceptorList.size()]);}return this.interceptors;}}

总结:

  1. DispatcherServlet根据请求获取处理器执行链,里面封装了本次请求所要执行的所有拦截器。
  2. 拦截器的preHandle方法是按照拦截器的顺序依次执行的,当有一个拦截器的返回值是false时,会根据interceptorIndex倒序执行所有拦截器的afterCompletion方法,执行完成后,程序结束运行。
  3. 当拦截器所有的preHandle方法都是返回true时,才会执行postHandle方法,拦截器的postHandle方法执行顺序是倒序的。
  4. 拦截器的afterCompletion方法是在将视图响应给前端前执行,或者在preHandle方法返回false的时候执行的。会根据执行拦截器最大索引interceptorIndex倒序执行

七、处理器适配器执行方法原理

适配器执行方法代码ModelAndView mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

HandlerAdapter 处理器适配器

java">public interface HandlerAdapter {//处理器是配置执行方法,返回一个ModelAndView对象//前面我们说到,springMVC有多种处理器适配器,每个处理器适配器都会实现这个方法,//作为SpringMVC角度来看,SpringMVC不关心怎么实现的,只要保证根据请求Request和Response作为入参,返回执行结果并且封装成ModelAndView	ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
}

ModelAndView

java">public class ModelAndView {//视图对象,里面包含了要跳转的视图地址信息private Object view;//实质是一个Map,用于存取数据,private ModelMap model;
}

AbstractHandlerMethodAdapter 处理器适配器抽象类

java">//这个处理器适配器匹配的是@RequestMapping注解写的方法
public abstract class AbstractHandlerMethodAdapter implements HandlerAdapter{//执行处理器方法,@Overridepublic final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler){return handleInternal(request, response, (HandlerMethod) handler);}//抽象方法,子类RequestMappingHandlerAdapter重写了该方法protected abstract ModelAndView handleInternal(HttpServletRequest request,HttpServletResponse response, HandlerMethod handlerMethod);}
}

RequestMappingHandlerAdapter 针对注解方式实现处理器的适配器

java">//AbstractHandlerMethodAdapter的子类,这个处理器是专门处理器注解方式实现的处理器
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter{@Overrideprotected ModelAndView handleInternal(HttpServletRequest request,HttpServletResponse response, HandlerMethod handlerMethod) {ModelAndView mav;//执行处理器方法return mav = invokeHandlerMethod(request, response, handlerMethod);}protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {//ServletInvocableHandlerMethod底层继承了HandlerMethodServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);//执行方法invocableMethod.invokeAndHandle(webRequest, mavContainer);//封装成ModelAndView对象return getModelAndView(mavContainer, modelFactory, webRequest);}
}

ServletInvocableHandlerMethod 底层继承了HandlerMethod

java">//底层是继承了HandlerMethod
public class ServletInvocableHandlerMethod extends InvocableHandlerMethod{public void invokeAndHandle(ServletWebRequest webRequest,ModelAndViewContainer mavContainer, Object... providedArgs) {invokeForRequest(webRequest, mavContainer, providedArgs);}//执行方法protected Object doInvoke(Object... args) throws Exception {return getBridgedMethod().invoke(getBean(), args);}
}

InvocableHandlerMethod

java">public class InvocableHandlerMethod extends HandlerMethod {//参数解析器对象,实质是一个List<HandlerMethodArgumentResolver>集合private HandlerMethodArgumentResolverComposite argumentResolvers = new HandlerMethodArgumentResolverComposite();public Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer,Object... providedArgs) {//获取执行方法的请求参数Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);//执行方法Object returnValue = doInvoke(args);//返回执行结果return returnValue;}//获取执行方法的参数private Object[] getMethodArgumentValues(NativeWebRequest request, ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {//获取该方法的所有形参MethodParameter[] parameters = getMethodParameters();Object[] args = new Object[parameters.length];//遍历所有的形参,并且去根据形参名请求request里取值for (int i = 0; i < parameters.length; i++) {MethodParameter parameter = parameters[i];//使用参数解析器解析参数值args[i] = this.argumentResolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);}return args;}}

HandlerMethod处理器方法对象

java">public class HandlerMethod {//执行方法所有的类字节码private final Class<?> beanType;//实际要执行的方法private final Method bridgedMethod;//处理器方法所有请求参数信息,SpringMVC初始化的时候这里会将这个处理器的参数封装到这里private final MethodParameter[] parameters;public MethodParameter[] getMethodParameters() {return this.parameters;}protected Method getBridgedMethod() {return this.bridgedMethod;}
}

HandlerMethodArgumentResolverComposite 处理器参数解析器

java">//这个对象是解析器增强对象,里面有真正的解析器集合
public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver {//真正的参数解析器,根据处理器的实现的方式不同,会有多个参数解析器private final List<HandlerMethodArgumentResolver> argumentResolvers = new LinkedList<HandlerMethodArgumentResolver>();//解析参数@Overridepublic Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {//根据请求匹配对应的参数解析器HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);//参数解析器解析参数值return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);}//根据请求匹配对应的参数解析器private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {HandlerMethodArgumentResolver result;if (result == null) {for (HandlerMethodArgumentResolver methodArgumentResolver : this.argumentResolvers) {//这里只针对RequestParamMethodArgumentResolver解析器做源码跟踪if (methodArgumentResolver.supportsParameter(parameter)) {result = methodArgumentResolver;break;}}}return result;}
}

HandlerMethodArgumentResolver 方法参数解析器接口

java">/*** 处理器方法参数解析器 AbstractNamedValueMethodArgumentResolver是针对注解实现处理器的参数解析器*/
public interface HandlerMethodArgumentResolver {//根据传入参数对象,判断当前解析器是否能够进行解析boolean supportsParameter(MethodParameter parameter);//解析参数Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception;
}

AbstractNamedValueMethodArgumentResolver 处理@ReqeustMapping注解方式的参数解析器

java">//针对@RequestMapping注解实现方式的参数解析器
public abstract class AbstractNamedValueMethodArgumentResolver implements HandlerMethodArgumentResolver {//解析参数public final Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {//获取形参名称NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);MethodParameter nestedParameter = parameter.nestedIfOptional();//根据形参名,去请求中获取参数Object arg = resolveName(namedValueInfo.name, nestedParameter, webRequest);}//获取形参名,并且封装成为NamedValueInfo格式//NamedValueInfo:里面封装了参数名称和值的信息private NamedValueInfo getNamedValueInfo(MethodParameter parameter) {//创建一个形参和值的映射对象,//这里会判断处理器上面是否有@RequestParam注解,如果有就从注解上获取value参数名NamedValueInfo namedValueInfo = createNamedValueInfo(parameter);//判断是否从注解中获取到了请求参数名,如果没有获取,就要通过asm去获取形参名namedValueInfo = updateNamedValueInfo(parameter, namedValueInfo);return namedValueInfo;}private NamedValueInfo updateNamedValueInfo(MethodParameter parameter, NamedValueInfo info) {String name = info.name;if (info.name.length() == 0) {//通过asm技术去获取形参名name = parameter.getParameterName();}String defaultValue = (ValueConstants.DEFAULT_NONE.equals(info.defaultValue) ? null : info.defaultValue);return new NamedValueInfo(name, info.required, defaultValue);}//参数名称和值的封装对象protected static class NamedValueInfo {//参数名称private final String name;//参数是否必须private final boolean required;//默认值private final String defaultValue;public NamedValueInfo(String name, boolean required, String defaultValue) {this.name = name;this.required = required;this.defaultValue = defaultValue;}}}

RequestParamMethodArgumentResolver 请求参数解析器

java">/*** 这个参数解析器主要处理带有@RequestParam注解的以及一些基本数据类型*/
public class RequestParamMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver{@Overrideprotected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {RequestParam ann = parameter.getParameterAnnotation(RequestParam.class);return (ann != null ? new RequestParamNamedValueInfo(ann) : new RequestParamNamedValueInfo());}//从请求中获取参数@Overrideprotected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {Object arg = null;if (arg == null) {String[] paramValues = request.getParameterValues(name);if (paramValues != null) {arg = (paramValues.length == 1 ? paramValues[0] : paramValues);}}return arg;}//根据传入方法参数对象,判断当前解析器是否能够进行解析@Overridepublic boolean supportsParameter(MethodParameter parameter) {//判断方法请求参数对象是否有@RequestParam注解,如果有说明匹配上了if (parameter.hasParameterAnnotation(RequestParam.class)) {return true;} else {parameter = parameter.nestedIfOptional();//springMVC初始化的时候会初始化两个RequestParamMethodArgumentResolver参数解析器//其中有一个useDefaultResolution是true,这样做的目的是为了给用户自定义的HandlerMethodArgumentResolver让出更多的可能性。//感兴趣的可以去深入研究if (this.useDefaultResolution) {//判断参数是否是简单类型,如果是简单类型就返回匹配上了return BeanUtils.isSimpleProperty(parameter.getNestedParameterType());}else {return false;}}}
}

MethodParameter 处理器方法请求参数对象

java">public class MethodParameter {//参数名称解析器,用来发现方法和构造函数的参数名称的,SpringMVC启动的时候会自动加载进来private volatile ParameterNameDiscoverer parameterNameDiscoverer;public String getParameterName() {ParameterNameDiscoverer discoverer = this.parameterNameDiscoverer;if (discoverer != null) {String[] parameterNames = discoverer.getParameterNames(this.method);if (parameterNames != null) {this.parameterName = parameterNames[this.parameterIndex];}}return this.parameterName;}
}

ParameterNameDiscoverer 参数名称解析器

java">/*** 参数名称解析器*/
public interface ParameterNameDiscoverer {//根据方法,解析所有的参数名称String[] getParameterNames(Method method);
}

PrioritizedParameterNameDiscoverer

java">
public class PrioritizedParameterNameDiscoverer implements ParameterNameDiscoverer {//springMVC启动会注册多个参数解析器 其中就包含LocalVariableTableParameterNameDiscovererprivate final List<ParameterNameDiscoverer> parameterNameDiscoverers = new LinkedList<ParameterNameDiscoverer>();//根据方法获取方法名称,循环遍历参数解析器,并且进行解析,直到解析到了值public String[] getParameterNames(Method method) {for (ParameterNameDiscoverer pnd : this.parameterNameDiscoverers) {String[] result = pnd.getParameterNames(method);if (result != null) {return result;}}return null;}
}

LocalVariableTableParameterNameDiscoverer 参数解析器

java">public class LocalVariableTableParameterNameDiscoverer implements ParameterNameDiscoverer {//根据请求方法获取方法参数名称public String[] getParameterNames(Method method) {Method originalMethod = BridgeMethodResolver.findBridgedMethod(method);Class<?> declaringClass = originalMethod.getDeclaringClass();map = inspectClass(declaringClass);return null;}//检析Class,获取方法形参名称private Map<Member, String[]> inspectClass(Class<?> clazz) {InputStream is = clazz.getResourceAsStream(ClassUtils.getClassFileName(clazz));//这里使用了asm,动态获取方法参数名称ClassReader classReader = new ClassReader(is);Map<Member, String[]> map = new ConcurrentHashMap<Member, String[]>(32);classReader.accept(new ParameterNameDiscoveringVisitor(clazz, map), 0);return map;}
}

总结:

  1. 处理器适配器接口有一个方法handle方法,这个方法传入RequestResponse以及处理器去执行处理器方法,并且将返回值封装成ModelAndView对象。
  2. SpringMVC是如何获取请求参数的,其实最本质的方式还是根据@RequestParam注解的value属性获取请求参数名称,然后再从request中获取。
  3. 不带@RequestParam注解的请求参数又是如何获取的呢,SpringMVC底层用的其实是asm框架的方式来获取参数名称,然后再去request中获取。

http://www.ppmy.cn/ops/150320.html

相关文章

手动实现一个循环顺序队列

#include <iostream>using namespace std;class Queue { private:int data[1024]; // 存储元素的数组int frontIndex; // 头指针int rearIndex; // 尾指针int size; // 当前队列中的元素个数public:// 构造函数Queue():frontInde…

使用 C# 制作图像的特写窗口

许多网站都会显示一个特写窗口&#xff0c;其中显示放大的图像部分&#xff0c;以便您可以看到更多细节。您在主图像上移动鼠标&#xff0c;它会在单独的图片中显示特写。此示例执行的操作类似。&#xff08;示例使用的一些数学运算非常棘手&#xff0c;因此您可能需要仔细查看…

vue运用uniapp框架开发企业微信小程序中常用的一些基础方法

嗨&#xff0c;我是小路。今天主要和大家分享的主题是“vue运用uniapp框架开发企业微信小程序中常用的一些基础方法”。 作为一名程序员&#xff0c;很多代码都是忘了再用&#xff0c;用了再忘。 今天梳理下日常开发中常用到的一些基础的方法&#xff0c;以方便后期开…

LSA更新、撤销

LSA的新旧判断&#xff1a; 1.seq&#xff0c;值越大越优先 2.chksum&#xff0c;值越大越优先 3.age&#xff0c;本地的LSA age和收到的LSA age作比较 如果差值<900s&#xff0c;认为age一致&#xff0c;保留本地的&#xff1a;我本地有一条LSA是100 你给的是400 差值小于…

基础入门-抓包技术HTTPS协议APP小程序PC应用Web证书信任转发联动

知识点&#xff1a; 1、抓包技术-Web应用-http/s-Burp&Yakit 2、抓包技术-APP应用-http/s-Burp&Yakit 3、抓包技术-PC端应用-http/s-Burp&Yakit 4、抓包技术-WX小程序-http/s-Burp&Yakit 5、抓包技术-软件联动-http/s-Proxifier 6、抓包技术-通用方案-http/s-R…

算法-盒子中小球的最大数量

原题目链接&#xff1a;1742. 盒子中小球的最大数量 - 力扣&#xff08;LeetCode&#xff09; 你在一家生产小球的玩具厂工作&#xff0c;有 n 个小球&#xff0c;编号从 lowLimit 开始&#xff0c;到 highLimit 结束&#xff08;包括 lowLimit 和 highLimit &#xff0c;即 n…

如何开放2375和2376端口供Docker daemon监听

Linux (以 Ubuntu 为例) 1. 修改 Docker 配置文件 打开 Docker 的配置文件 /etc/docker/daemon.json。如果该文件不存在&#xff0c;则可以创建一个新的。 bash sudo nano /etc/docker/daemon.json在配置文件中添加以下内容&#xff1a; json {"hosts": ["un…

C#,任意阶幻方(Magic Square)的算法与源代码

1 什么是幻方&#xff1f; 幻方&#xff08;Magic Square&#xff09;是一种将数字安排在正方形格子中&#xff0c;使每行、列和对角线上的数字和都相等的方法。 幻方也是一种中国传统游戏。旧时在官府、学堂多见。它是将从一到若干个数的自然数排成纵横各为若干个数的正方形&…