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

文章目录

  • 一、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前端控制器

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;}
}

视图解析器接口

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

视图接口

//视图接口,例如实现类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

//前端控制器,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

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

处理器映射器抽象类 AbstractHandlerMapping

/*** 处理器映射抽象类* 本文仅对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

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
public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMethodMapping<RequestMappingInfo> {//根据请求信息和RequestMappingInfo判断当前请求映射是否匹配此次请求,如果符合返回该请求映射//注意,由于请求映射在项目初始化的时候就已经创建了,但是返回的时候是采用创建一个新的RequestMappingInfo对象返回的protected RequestMappingInfo getMatchingMapping(RequestMappingInfo info, HttpServletRequest request) {return info.getMatchingCondition(request);}
}
  • RequestMappingHandlerMapping
//请求映射处理器,针对使用了@RequestMapping注解的请求方式
public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping {}
  • 请求映射详情 RequestMappingInfo
//这个类是请求映射详情,里面封装了需要执行处理器所需要的信息,比如请求方式,请求参数
//比如我们定义了  @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 处理器执行链

//处理器链执行链
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

//前端控制器,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 处理器适配器

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

AbstractHandlerMethodAdapter

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

RequestMappingHandlerAdapter

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

//前端控制器,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

/*** 处理器执行链对象*/
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 处理器适配器

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

ModelAndView

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

AbstractHandlerMethodAdapter 处理器适配器抽象类

//这个处理器适配器匹配的是@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 针对注解方式实现处理器的适配器

//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

//底层是继承了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

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处理器方法对象

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 处理器参数解析器

//这个对象是解析器增强对象,里面有真正的解析器集合
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 方法参数解析器接口

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

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

//针对@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 请求参数解析器

/*** 这个参数解析器主要处理带有@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 处理器方法请求参数对象

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 参数名称解析器

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

PrioritizedParameterNameDiscoverer


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 参数解析器

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.rhkb.cn/news/1921.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java中网络编程的学习

目录 网络编程概述 网络模型 网络通信三要素: IP 端口号 通信协议 IP地址&#xff08;Internet Protocol Address&#xff09; 端口号 网络通信协议 TCP 三次握手 四次挥手 UDP TCP编程 客户端Socket的工作过程包含以下四个基本的步骤&#xff1a; 服务器程序…

Winforms开发基础之非主线程操作UI控件的误区

前言 想象一下&#xff0c;你正在开发一个桌面应用程序&#xff0c;用户点击按钮后需要执行一个耗时操作。为了避免界面卡顿&#xff0c;你决定使用后台线程来处理任务。然而&#xff0c;当你在后台线程中尝试更新UI控件时&#xff0c;程序突然崩溃了。这是为什么呢&#xff1…

前端组件开发:组件开发 / 定义配置 / 配置驱动开发 / 爬虫配置 / 组件V2.0 / form表单 / table表单

一、最早的灵感 最早的灵感来自sprider / 网络爬虫 / 爬虫配置&#xff0c;在爬虫爬取网站文章时候&#xff0c;会输入给爬虫一个配置文件&#xff0c;里边的内容是一个json对象。里边包含了所有想要抓取的页面的信息。爬虫通过这个配置就可以抓取目标网站的数据。其实本文要引…

个人主页搭建全流程(Nginx部署+SSL配置+DCDN加速)

前言 最近开始准备秋招&#xff0c;打算做一个个人主页&#xff0c;以便在秋招市场上更有竞争力。 目前&#xff0c;现有的一些搭建主页的博文教程存在以下一些问题&#xff1a; 使用Github Page进行部署&#xff0c;这在国内访问容易受阻使用宝塔面板等框架&#xff0c;功能…

day07_Spark SQL

文章目录 day07_Spark SQL课程笔记一、今日课程内容二、Spark SQL函数定义&#xff08;掌握&#xff09;1、窗口函数2、自定义函数背景2.1 回顾函数分类标准:SQL最开始是_内置函数&自定义函数_两种 2.2 自定义函数背景 3、Spark原生自定义UDF函数3.1 自定义函数流程&#x…

【I/O编程】UNIX文件基础

IO编程的本质是通过 API 操作 文件。 什么是 IO I - Input 输入O - Output 输出 这里的输入和输出都是站在应用&#xff08;运行中的程序&#xff09;的角度。外部特指文件。 这里的文件是泛指&#xff0c;并不是只表示存在存盘中的常规文件。还有设备、套接字、管道、链接…

软件测试 —— Selenium常用函数

软件测试 —— Selenium常用函数 操作测试对象点击/提交对象 click()模拟按键输入 send_keys("")清除文本内容 clear() 模拟用户键盘行为 Keys包示例用法 获取文本信息 textget_attribute("属性名称") 获取当前页面标题 title获取当前页面的 url current_u…

java导出pdf文件

java导出pdf&#xff0c;前端下载 1、制作pdf模板2、获取pdf导出中文需要的文件3、实现4、前端发起请求并生成下载链接 使用注意点 因为原来制作的pdf表单内容过于复杂&#xff0c;下面代码只包含前两行的操作。 本次操作需要前端向后端发起请求&#xff0c;后端返回数据给前端…

Python教程丨Python环境搭建 (含IDE安装)——保姆级教程!

工欲善其事&#xff0c;必先利其器。 学习Python的第一步不要再加收藏夹了&#xff01;提高执行力&#xff0c;先给自己装好Python。 1. Python 下载 1.1. 下载安装包 既然要下载Python&#xff0c;我们直接进入python官网下载即可 Python 官网&#xff1a;Welcome to Pyt…

ip属地是根据手机号还是位置

在数字化时代&#xff0c;IP地址作为网络世界中的“门牌号”&#xff0c;其属地信息往往与用户的地理位置紧密相关。然而&#xff0c;关于IP属地是如何确定的&#xff0c;是否依赖于手机号还是实际位置&#xff0c;这一话题时常引发讨论。本文将深入探讨IP属地的确定方式&#…

Python编程与在线医疗平台数据挖掘与数据应用交互性研究

一、引言 1.1 研究背景与意义 在互联网技术飞速发展的当下,在线医疗平台如雨后春笋般涌现,为人们的就医方式带来了重大变革。这些平台打破了传统医疗服务在时间和空间上的限制,使患者能够更加便捷地获取医疗资源。据相关报告显示,中国基于互联网的医疗保健行业已进入新的…

如何在 Linux、MacOS 以及 Windows 中打开控制面板

控制面板不仅仅是一系列图标和菜单的集合&#xff1b;它是通往优化个人计算体验的大门。通过它&#xff0c;用户可以轻松调整从外观到性能的各种参数&#xff0c;确保他们的电脑能够完美地适应自己的需求。无论是想要提升系统安全性、管理硬件设备&#xff0c;还是简单地改变桌…

【数据结构】基础知识

目录 1.1 什么是数据结构 1.2数据 1.3 逻辑结构 1.4 存储结构 1.4.1 顺序存储 1.4.2 链式存储 1.4.3 索引存储 1.4.4 散列存储 1.5 操作 1.1 什么是数据结构 数据的逻辑结构以及存储操作 数据结构没有那么复杂&#xff0c;它就教会你一件事&#xff1a;如何更有效的…

2025年中科院分区大类划分公布!新增8155本

2025年中科院分区表变更情况 扩大收录范围 2025年的期刊分区表在原有的自然科学&#xff08;SCIE&#xff09;、社会科学&#xff08;SSCI&#xff09;和人文科学&#xff08;AHCI&#xff09;的基础上&#xff0c;增加了ESCI期刊的收录&#xff0c;并根据这些期刊的数据进行…

【Hive】新增字段(column)后,旧分区无法更新数据问题

TOC 【一】问题描述 Hive修改数据表结构的需求&#xff0c;比如&#xff1a;增加一个新字段。 如果使用如下语句新增列&#xff0c;可以成功添加列col1。但如果数据表tb已经有旧的分区&#xff08;例如&#xff1a;dt20190101&#xff09;&#xff0c;则该旧分区中的col1将为…

Xcode 正则表达式实现查找替换

在软件开发过程中&#xff0c;查找和替换文本是一项常见的任务。正则表达式&#xff08;Regular Expressions&#xff09;是一种强大的工具&#xff0c;可以帮助我们在复杂的文本中进行精确的匹配和替换。Xcode 作为一款流行的开发工具&#xff0c;提供了对正则表达式的支持。本…

我国无人机新增实名登记110.3 万架,累计完成飞行2666万小时

据央视新闻从中国民航局了解到&#xff0c;2024 年我国全年新增通航企业 145 家、通用机场 26 个&#xff0c;颁发无人驾驶航空器型号合格证 6 个、新增实名登记无人机 110.3 万架&#xff0c;无人机运营单位总数超过 2 万家&#xff0c;累计完成无人机飞行 2666 万小时&#x…

【论文阅读】SDA-FC: Bridging federated clustering and deep generative model

论文地址&#xff1a;SDA-FC: Bridging federated clustering and deep generative model - ScienceDirect 代码地址&#xff1a;https://github.com/Jarvisyan/SDA-FC 摘要 联邦聚类&#xff08;FC&#xff09;是集中式聚类在联邦环境中的扩展。关键在于如何在不共享私人数据…

查看APK的公钥,MD5信息

查看md5 sha1 sha256的等信息 keytool -list -printcert -jarfile apk的路径地址 查看公钥私钥信息 keytool -list -rfc --keystore keystore文件的路径地址 | openssl x509 -inform pem -pubkey 把里面的keystore文件的路径地址替换成你的本地文件就可以了 如果报以上错误 就…

王炸组合:Dolphinscheudler 3.1.*搭配SeaT unnel2.3.*高效完成异构数据数据集成

概述 本篇主要介绍如何通过Dolphinscheduler海豚调度搭配Seatunnel完成异构数据源之间的数据同步功能&#xff0c;这个在大数据流批一体数仓建设的过程中是一个非常好的解决方案&#xff0c; 稳定高效&#xff0c;只要用上了你肯定爱不释手。 环境准备 dolphinscheduler集群…