位置: IT常识 - 正文

SpringMVC详解(springmvc简介)

编辑:rootadmin
SpringMVC的介绍 【1】Spring Web MVC是基于Servlet API构建的原始Web框架,从一开始就已包含在Spring框架中。正式名称“ Spring Web MVC”来自其源模块的名称(spring-webmvc),但它通常被称为“ Spring MVC”。 SpringMV ... SpringMVC的介绍

推荐整理分享SpringMVC详解(springmvc简介),希望有所帮助,仅作参考,欢迎阅读内容。

文章相关热门搜索词:springmvc怎么理解,springmvc简介,springmvc入门实例,springmvc model详解,springmvc怎么理解,springmvc简单介绍,spring mvc详解,讲一下springmvc,内容如对您有帮助,希望把文章链接给更多的朋友!

【1】Spring Web MVC是基于Servlet API构建的原始Web框架,从一开始就已包含在Spring框架中。正式名称“ Spring Web MVC”来自其源模块的名称(spring-webmvc),但它通常被称为“ Spring MVC”。

SpringMVC的具体执行流程:

【1】说明:

  1)Spring MVC 是围绕前端控制器模式设计的,其中:中央 Servlet DispatcherServlet 为请求处理流程提供统一调度,实际工作则交给可配置组件执行。这个模型是灵活的且开放的,我们可以通过自己去定制这些组件从而进行定制自己的工作流。

  2)说白了就是用一个DispatcherServlet 封装了一个Servlet的调度中心, 由调度中心帮我们调用我们的处理方法:在这个过程中调度中心委托给各个组件执行具体工作 ,比如帮我们映射方法请求、帮我解析参数、调用处理方法、响应数据和页面 等

【2】图示:

【3】组件说明:

DispatcherServlet: 前端调度器 , 负责将请求拦截下来分发到各控制器方法中HandlerMapping: 负责根据请求的URL和配置@RequestMapping映射去匹配, 匹配到会返回Handler(具体控制器的方法)HandlerAdaper: 负责调用Handler-具体的方法- 返回视图的名字 Handler将它封装到ModelAndView(封装视图名,request域的数据)ViewReslover: 根据ModelAndView里面的视图名地址去找到具体的jsp封装在View对象中View:进行视图渲染(将jsp转换成html内容 --这是Servlet容器的事情了) 最终response到的客户端

【4】流程说明:

1)用户发送请求至前端控制器DispatcherServlet2)DispatcherServlet收到请求调用处理器映射器HandlerMapping。 2.1)处理器映射器根据请求url找到具体的处理器,生成处理器执行链HandlerExecutionChain(包括处理器对象和处理器拦截器)一并返回给DispatcherServlet。3)DispatcherServlet根据处理器Handler获取处理器适配器HandlerAdapter,执行HandlerAdapter处理一系列的操作,如:参数封装,数据格式转换,数据验证等操作4)执行处理器Handler(Controller,也叫页面控制器)。 4.1)Handler执行完成返回ModelAndView 4.2)HandlerAdapter将Handler执行结果ModelAndView返回到DispatcherServlet5)DispatcherServlet将ModelAndView传给ViewReslover视图解析器 5.1)ViewReslover解析后返回具体View6)DispatcherServlet对View进行渲染视图(即将模型数据model填充至视图中)。7)DispatcherServlet响应用户。SpringMVC请求流程图解:

  

执行流程源码解析(我是直接开启SpringBoot里面分析的)

【1】分析主线流程,DispatcherServlet类#doDispatch方法

//DispatcherServlet类#doDispatch方法//主流程1,执行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 { //检查请求是否是multipart(即文件上传),若是进行相关处理 processedRequest = checkMultipart(request); multipartRequestParsed = (processedRequest != request); //通过handermapping映射获取HandlerExecutionChain(处理链中包括了interceptor的前置和后置方法) //主流程2,获取HandlerExecutionChain mappedHandler = getHandler(processedRequest); if (mappedHandler == null) { noHandlerFound(processedRequest, response); return; } // 根据处理器(handler及HandlerExecutionChain)获取处理器适配器(处理器适配器是为了提供统一接口进行后续处理,从而支持多种类型的处理器)       // 主流程3的具体地方 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; } } //执行chain中拦截器附加的预处理方法,即preHandle方法  if (!mappedHandler.applyPreHandle(processedRequest, response)) { // 返回false就不进行后续处理了 return; } // 执行HandlerAdapter处理一系列的操作,如:参数封装,数据格式转换,数据验证等操作 ,主流程4的具体地方 // 执行处理器Handler(Controller,也叫页面控制器)。 // Handler执行完成返回ModelAndView // HandlerAdapter将Handler执行结果ModelAndView返回到DispatcherServlet mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) { return; } // 如果没有视图,给你设置默认视图 json忽略 applyDefaultViewName(processedRequest, mv); // 后置拦截器 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); } // DispatcherServlet将ModelAndView传给ViewReslover视图解析器 // ViewReslover解析后返回具体View // DispatcherServlet对View进行渲染视图(即将模型数据model填充至视图中)。 // DispatcherServlet响应用户。 // 如果有异常,还会处理异常 ,主流程5,6的具体地方 processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); } catch (Exception ex) { //拦截器afterCompletion处理器 triggerAfterCompletion(processedRequest, response, mappedHandler, ex); } catch (Throwable err) { 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); } } }}

【2】分析getHandler方法如何返回 处理器执行链HandlerExecutionChain:

//分析getHandler//this.handlerMappings的内容//0.RequestMappingHandlerMapping//1.BeanNameUrlHandlerMapping//2.RouterFunctionMapping//3.SimpleUrlHandlerMapping//4.WelcomePageHandlerMapping@Nullableprotected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { if (this.handlerMappings != null) { for (HandlerMapping mapping : this.handlerMappings) { HandlerExecutionChain handler = mapping.getHandler(request); if (handler != null) { return handler; } } } return null;}//调用到了AbstractHandlerMapping类#getHandler方法@Override@Nullablepublic final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { //getHandlerInternal(request)方法为抽象方法,供子类实现 //获取到的handler对象一般为bean/HandlerMethod Object handler = getHandlerInternal(request); //上述找不到则使用默认的处理类,没有设定则返回null,则会返回前台404错误 if (handler == null) { handler = getDefaultHandler(); } if (handler == null) { return null; } // Bean name or resolved handler? if (handler instanceof String) { String handlerName = (String) handler; handler = obtainApplicationContext().getBean(handlerName); } // Ensure presence of cached lookupPath for interceptors and others if (!ServletRequestPathUtils.hasCachedPath(request)) { initLookupPath(request); } //创建处理链对象 HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request); //针对cros跨域请求的处理 if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) { CorsConfiguration config = getCorsConfiguration(handler, request); if (getCorsConfigurationSource() != null) { CorsConfiguration globalConfig = getCorsConfigurationSource().getCorsConfiguration(request); config = (globalConfig != null ? globalConfig.combine(config) : config); } if (config != null) { config.validateAllowCredentials(); } executionChain = getCorsHandlerExecutionChain(request, executionChain, config); } return executionChain;}

【2.1】分析AbstractHandlerMapping类#getHandler方法

【2.1.1】如何获取handler对象【一般为bean/HandlerMethod】

//RequestMappingInfoHandlerMapping类#getHandlerInternal方法//因为它是RequestMappingHandlerMapping的父类@Override@Nullableprotected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception { request.removeAttribute(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE); try { //调用父类AbstractHandlerMethodMapping类 return super.getHandlerInternal(request); } finally { ProducesRequestCondition.clearMediaTypesAttribute(request); }}//调用到AbstractHandlerMethodMapping类getHandlerInternal方法//针对HandlerMethod的获取protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception { //获取访问的路径,一般类似于request.getServletPath()返回不含contextPath的访问路径 String lookupPath = initLookupPath(request); //获取读锁 this.mappingRegistry.acquireReadLock(); try { //获取HandlerMethod作为handler对象,这里涉及到路径匹配的优先级 //优先级:精确匹配>最长路径匹配>扩展名匹配 HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request); //HandlerMethod内部含有bean对象,其实指的是对应的Controller return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null); } finally { //释放读锁 this.mappingRegistry.releaseReadLock(); }}

【2.1.2】如何创建处理链

//创建处理器链的方法protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) { /创建HandlerExecutionChain HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ? (HandlerExecutionChain) handler : new HandlerExecutionChain(handler)); //与请求url进行匹配,满足的才加入 for (HandlerInterceptor interceptor : this.adaptedInterceptors) { if (interceptor instanceof MappedInterceptor) { MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor; if (mappedInterceptor.matches(request)) { chain.addInterceptor(mappedInterceptor.getInterceptor()); } } else { chain.addInterceptor(interceptor); } } return chain;}

【3】分析getHandlerAdapter寻找的处理器适配器

//分析getHandlerAdapter//this.handlerAdapters的内容//0.RequestMappingHandlerAdapter//1.HandlerFunctionAdapter//2.HttpRequestHandlerAdapter//3.SimpleControllerHandlerAdapterprotected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException { if (this.handlerAdapters != null) { for (HandlerAdapter adapter : this.handlerAdapters) { if (adapter.supports(handler)) { //所以一般返回的也就是RequestMappingHandlerAdapter return adapter; } } } throw new ServletException(...);}//因为RequestMappingHandlerAdapter没有重写故调用父类的//调用AbstractHandlerMethodAdapter类#supports方法@Overridepublic final boolean supports(Object handler) { //如果是类的方法的话默认是true return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));}

【4】分析mappedHandler.applyPreHandle前置处理器与mappedHandler.applyPostHandle后置处理器

//分析mappedHandler.applyPreHandle前置处理器与mappedHandler.applyPostHandle后置处理器//前置处理器是从0到sizeboolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception { for (int i = 0; i < this.interceptorList.size(); i++) { HandlerInterceptor interceptor = this.interceptorList.get(i); if (!interceptor.preHandle(request, response, this.handler)) { triggerAfterCompletion(request, response, null); return false; } this.interceptorIndex = i; } return true;}//前置处理器是从size到0void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception { for (int i = this.interceptorList.size() - 1; i >= 0; i--) { HandlerInterceptor interceptor = this.interceptorList.get(i); interceptor.postHandle(request, response, this.handler, mv); }}SpringMVC详解(springmvc简介)

【5】分析ha.handle(processedRequest, response, mappedHandler.getHandler());,如何进行调用控制器里面的方法

//AbstractHandlerMethodAdapter类#handle方法//因为适配器RequestMappingHandlerAdapter类没有所以定位到了父类public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return handleInternal(request, response, (HandlerMethod) handler);}//RequestMappingHandlerAdapter类#handleInternal方法@Overrideprotected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { ModelAndView mav; // 检查当前请求的method是否为支持的method(默认Null,可通过继承AbstractController设置supportedMethods) // 检查当前请求是否必须session (默认false,可通过继承AbstractController设置requireSession) checkRequest(request); /** * 判断当前是否需要支持在同一个session中只能线性地处理请求 * 因为锁是通过 synchronized 是 JVM 进程级,所以在分布式环境下, * 无法达到同步相同 Session 的功能。默认情况下,synchronizeOnSession 为 false */ if (this.synchronizeOnSession) { // 获取当前请求的session对象 HttpSession session = request.getSession(false); if (session != null) { // 为当前session生成一个唯一的可以用于锁定的key Object mutex = WebUtils.getSessionMutex(session); synchronized (mutex) { // 对HandlerMethod进行参数等的适配处理,并调用目标handler mav = invokeHandlerMethod(request, response, handlerMethod); } } else { // 如果当前不存在session,则直接对HandlerMethod进行适配 mav = invokeHandlerMethod(request, response, handlerMethod); } } else { // 这般都会走这里,重点反射调用 // 如果当前不需要对session进行同步处理,则直接对HandlerMethod进行适配 mav = invokeHandlerMethod(request, response, handlerMethod); } //判断当前请求头中是否包含Cache-Control请求头,如果不包含,则对当前response进行处理 if (!response.containsHeader(HEADER_CACHE_CONTROL)) { // 如果当前SessionAttribute中存在配置的attributes,则为其设置过期时间。 // 这里SessionAttribute主要是通过@SessionAttribute注解生成的 if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) { applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers); } else { // 如果当前不存在SessionAttributes,则判断当前是否存在Cache-Control设置, // 如果存在,则按照该设置进行response处理,如果不存在,则设置response中的 // Cache的过期时间为-1,即立即失效 prepareResponse(response); } } return mav;}

【5.1】分析invokeHandlerMethod方法怎么做【先是参数处理器,再是生成容器,然后去反射调用,最后将结果放入容器】

@Nullableprotected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { // 把我们的请求req resp包装成 ServletWebRequest ServletWebRequest webRequest = new ServletWebRequest(request, response); try { // 获取容器中全局配置的InitBinder和当前HandlerMethod所对应的Controller中 // 配置的InitBinder,用于进行参数的绑定 WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod); // 获取容器中全局配置的ModelAttribute和当前HandlerMethod所对应的Controller 中配置的ModelAttribute, // 这些配置的方法将会在目标方法调用之前进行调用 ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory); // 封装handlerMethod,会在调用前解析参数、调用后对返回值进行处理 ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod); if (this.argumentResolvers != null) { // 让invocableMethod拥有参数解析能力 invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers); } if (this.returnValueHandlers != null) { // 让invocableMethod拥有返回值处理能力 invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers); } // 让invocableMethod拥有InitBinder解析能力 invocableMethod.setDataBinderFactory(binderFactory); // 设置ParameterNameDiscoverer,该对象将按照一定的规则获取当前参数的名称 invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer); // 创建ModelAndView处理容器 ModelAndViewContainer mavContainer = new ModelAndViewContainer(); // 将request的Attribute复制一份到ModelMap mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request)); // 调用我们标注了@ModelAttribute的方法,主要是为我们的目标方法预加载 modelFactory.initModel(webRequest, mavContainer, invocableMethod); // 重定向的时候,忽略model中的数据 默认false mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect); // 获取当前的AsyncWebRequest,这里AsyncWebRequest的主要作用是用于判断目标 // handler的返回值是否为WebAsyncTask或DeferredResult,如果是这两种中的一种, // 则说明当前请求的处理应该是异步的。所谓的异步,指的是当前请求会将Controller中 // 封装的业务逻辑放到一个线程池中进行调用,待该调用有返回结果之后再返回到response中。 // 这种处理的优点在于用于请求分发的线程能够解放出来,从而处理更多的请求,提高吞吐。 // 只有待目标任务完成之后才会回来将该异步任务的结果返回。 AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response); asyncWebRequest.setTimeout(this.asyncRequestTimeout); // 封装异步任务的线程池、request、interceptors到WebAsyncManager中 WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); asyncManager.setTaskExecutor(this.taskExecutor); asyncManager.setAsyncWebRequest(asyncWebRequest); asyncManager.registerCallableInterceptors(this.callableInterceptors); asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors); // 这里就是用于判断当前请求是否有异步任务结果的,如果存在,则对异步任务结果进行封装 if (asyncManager.hasConcurrentResult()) { Object result = asyncManager.getConcurrentResult(); mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0]; asyncManager.clearConcurrentResult(); LogFormatUtils.traceDebug(logger, traceOn -> { String formatted = LogFormatUtils.formatValue(result, !traceOn); return "Resume with async result [" + formatted + "]"; }); invocableMethod = invocableMethod.wrapConcurrentResult(result); } // 对请求参数进行处理,调用目标HandlerMethod,并且将返回值封装为一个ModelAndView对象 invocableMethod.invokeAndHandle(webRequest, mavContainer); if (asyncManager.isConcurrentHandlingStarted()) { return null; } // 对封装的ModelAndView进行处理,主要是判断当前请求是否进行了重定向,如果进行了重定向,还会判断是否需要将FlashAttributes封装到新的请求中 return getModelAndView(mavContainer, modelFactory, webRequest); } finally { webRequest.requestCompleted(); }}

【5.1.1】分析invocableMethod.invokeAndHandle怎么反射调用

//ServletInvocableHandlerMethod类#invokeAndHandle方法public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { // 真正的调用我们的目标对象 Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs); // 设置相关的返回状态 setResponseStatus(webRequest); // 如果请求处理完成,则设置requestHandled属性 if (returnValue == null) { if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) { disableContentCachingIfNecessary(webRequest); mavContainer.setRequestHandled(true); return; } } // 如果请求失败,但是有错误原因,那么也会设置requestHandled属性 else if (StringUtils.hasText(getResponseStatusReason())) { mavContainer.setRequestHandled(true); return; } mavContainer.setRequestHandled(false); Assert.state(this.returnValueHandlers != null, "No return value handlers"); try { // 遍历当前容器中所有ReturnValueHandler,判断哪种handler支持当前返回值的处理, // 如果支持,则使用该handler处理该返回值 this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest); } catch (Exception ex) { throw ex; }}//InvocableHandlerMethod类#invokeForRequest方法public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { // 获取我们目标方法入参的值 Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs); // 反射调用 ,里面主要就是 method.invoke(getBean(), args) 来进行反射调用 return doInvoke(args);}

【5.1.2】分析 对封装的ModelAndView进行处理

//RequestMappingHandlerAdapter类#getModelAndView方法private ModelAndView getModelAndView(ModelAndViewContainer mavContainer, ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception { modelFactory.updateModel(webRequest, mavContainer); if (mavContainer.isRequestHandled()) { return null; } ModelMap model = mavContainer.getModel(); ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus()); if (!mavContainer.isViewReference()) { mav.setView((View) mavContainer.getView()); } if (model instanceof RedirectAttributes) { Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes(); HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class); if (request != null) { RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes); } } return mav;}

【6】分析processDispatchResult方法对返回结果的处理

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, @Nullable Exception exception) throws Exception { boolean errorView = false; // 异常处理 if (exception != null) { if (exception instanceof ModelAndViewDefiningException) { logger.debug("ModelAndViewDefiningException encountered", exception); mv = ((ModelAndViewDefiningException) exception).getModelAndView(); } else { Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null); mv = processHandlerException(request, response, handler, exception); errorView = (mv != null); } } // Did the handler return a view to render? if (mv != null && !mv.wasCleared()) { // 解析、渲染视图 render(mv, request, response); if (errorView) { WebUtils.clearErrorRequestAttributes(request); } } else {...} if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) { // Concurrent handling started during a forward return; } if (mappedHandler != null) { // Exception (if any) is already handled..     // 拦截器的后置处理 mappedHandler.triggerAfterCompletion(request, response, null); }}

【6.1】分析render方法视图渲染

protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception { // Determine locale for request and apply it to the response. Locale locale = (this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale()); response.setLocale(locale); View view; String viewName = mv.getViewName(); if (viewName != null) { // 解析视图名 view = resolveViewName(viewName, mv.getModelInternal(), locale, request); if (view == null) { throw new ServletException(...); } } else { // No need to lookup: the ModelAndView object contains the actual View object. view = mv.getView(); if (view == null) { throw new ServletException(...); } } // Delegate to the View object for rendering. try { if (mv.getStatus() != null) { request.setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, mv.getStatus()); response.setStatus(mv.getStatus().value()); } // 进行视图渲染 ,调用的是 AbstractView类的方法 view.render(mv.getModelInternal(), request, response); } catch (Exception ex) { throw ex; }}源码解析注解@RequestMapping

【1】@RequestMapping解析

【1.1】说明:

  1)明确一点:@RequestMapping是通过RequestMappingHandlerMapping负责解析。

  2)HandlerMapping便是负责根据请求URI 映射 到对应的handler方法。而RequestMappingHandlerMapping是HandlerMapping的其中一个实现类, 负责根据@RequestMapping注解进行映射。

  3)所以HandlerMapping有很多其他实现类,RequestMappingHandlerMapping是最常用的。HandlerMapping可分为2个过程:1解析、2映射

【1.2】分析RequestMappingHandlerMapping类

  1)基于继承关系可以发现它实现了InitializingBean接口

  2)分析afterPropertiesSet方法做了什么

@Override@SuppressWarnings("deprecation")public void afterPropertiesSet() { //这里都是一些设置配置 this.config = new RequestMappingInfo.BuilderConfiguration(); this.config.setTrailingSlashMatch(useTrailingSlashMatch()); this.config.setContentNegotiationManager(getContentNegotiationManager()); if (getPatternParser() != null) { this.config.setPatternParser(getPatternParser()); } else { this.config.setSuffixPatternMatch(useSuffixPatternMatch()); this.config.setRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch()); this.config.setPathMatcher(getPathMatcher()); } //调用父类AbstractHandlerMethodMapping类的afterPropertiesSet方法 super.afterPropertiesSet();}//父类AbstractHandlerMethodMapping类的afterPropertiesSet方法public void afterPropertiesSet() { initHandlerMethods();}//AbstractHandlerMethodMapping类#initHandlerMethods方法protected void initHandlerMethods() { // 获得所有候选beanName—— 当前容器所有的beanName for (String beanName : getCandidateBeanNames()) { if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) { // 处理候选bean——即解析@RequestMapping和映射路径 processCandidateBean(beanName); } } // 解析完所有@RequestMapping的时候调用,输出日志 handlerMethodsInitialized(getHandlerMethods());}

【1.2.1】分析processCandidateBean方法怎么处理的

protected void processCandidateBean(String beanName) { Class<?> beanType = null; try { beanType = obtainApplicationContext().getType(beanName); } catch (Throwable ex) {..省略日志..} // 这一步判断是关键 是否有Controller 或 RequestMapping注解 if (beanType != null && isHandler(beanType)) { // 解析HandlerMethods detectHandlerMethods(beanName); }}protected void detectHandlerMethods(Object handler) { Class<?> handlerType = (handler instanceof String ? obtainApplicationContext().getType((String) handler) : handler.getClass()); if (handlerType != null) { Class<?> userType = ClassUtils.getUserClass(handlerType); // 循环所有方法 Map<Method, T> methods = MethodIntrospector.selectMethods(userType, (MethodIntrospector.MetadataLookup<T>) method -> { try { return getMappingForMethod(method, userType); } catch (Throwable ex) {..省略异常..} }); ..省略日志.. //循环注册 methods.forEach((method, mapping) -> { Method invocableMethod = AopUtils.selectInvocableMethod(method, userType); registerHandlerMethod(handler, invocableMethod, mapping); }); }}

【1.2.1.1】解析流程

//调用回子类RequestMappingHandlerMapping类#getMappingForMethod方法protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) { // 如果方法上面有@RequestMapping:解析出RequestMappingInfo // RequestMappingInfo 是用来在请求的时候做匹对的 RequestMappingInfo info = createRequestMappingInfo(method); if (info != null) { // 如果方法上面有@RequestMapping,看看类上面是不是有@RequestMapping RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType); // 类上面也有@RequestMapping 那就合并 // 比如 类:/user 方法:/info 合并为 /user/info if (typeInfo != null) { info = typeInfo.combine(info); } // 合并前缀 5.1新增 默认null // 可通过 WebMvcConfigurer#configurePathMatch 进行定制 String prefix = getPathPrefix(handlerType); if (prefix != null) { info = RequestMappingInfo.paths(prefix).options(this.config).build().combine(info); } } return info;}

【1.2.1.2】注册流程

//调用回子类RequestMappingHandlerMapping类#registerHandlerMethod方法protected void registerHandlerMethod(Object handler, Method method, RequestMappingInfo mapping) { super.registerHandlerMethod(handler, method, mapping); updateConsumesCondition(mapping, method);}//父类AbstractHandlerMethodMapping类#registerHandlerMethod方法//其中private final MappingRegistry mappingRegistry = new MappingRegistry();protected void registerHandlerMethod(Object handler, Method method, T mapping) { this.mappingRegistry.register(mapping, handler, method);}//两大存储容器//private final Map<T, MappingRegistration<T>> registry = new HashMap<>();//private final MultiValueMap<String, T> pathLookup = new LinkedMultiValueMap<>();//pathLookup存储【path, mapping】//registry存储【mapping,与之对应的【mapping, handlerMethod, directPaths, name, corsConfig】】public void register(T mapping, Object handler, Method method) { this.readWriteLock.writeLock().lock(); try { HandlerMethod handlerMethod = createHandlerMethod(handler, method); validateMethodMapping(handlerMethod, mapping); Set<String> directPaths = AbstractHandlerMethodMapping.this.getDirectPaths(mapping); for (String path : directPaths) { this.pathLookup.add(path, mapping); } String name = null; if (getNamingStrategy() != null) { name = getNamingStrategy().getName(handlerMethod, mapping); addMappingName(name, handlerMethod); } CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping); if (corsConfig != null) { corsConfig.validateAllowCredentials(); this.corsLookup.put(handlerMethod, corsConfig); } this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directPaths, name, corsConfig != null)); } finally { this.readWriteLock.writeLock().unlock(); }}

【2】@RequestMapping请求映射

【2.1】回看执行流程的【2.1.1】中的getHandlerInternal方法

protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception { // 通过UrlPathHelper对象,用于来解析从们的request中解析出请求映射路径 String lookupPath = initLookupPath(request); //获取读锁 this.mappingRegistry.acquireReadLock(); try { // 通过lookupPath解析最终的handler——HandlerMethod对象 HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request); return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null); } finally { //释放读锁 this.mappingRegistry.releaseReadLock(); }}//分析查找流程//先从pathLookup里面拿//拿不到再去进行通配符匹配,排序获取第一个最优匹配的protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception { List<Match> matches = new ArrayList<>(); // 根据uri从mappingRegistry.pathLookup获取 RequestMappingInfo // pathLookup<path,RequestMappingInfo>会在初始化阶段解析好 List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath); if (directPathMatches != null) { // 如果根据path能直接匹配的RequestMappingInfo 则用该mapping进行匹配其他条件(method、header等) addMatchingMappings(directPathMatches, matches, request); } if (matches.isEmpty()) { // 如果无path匹配,用所有的RequestMappingInfo 通过AntPathMatcher匹配 addMatchingMappings(this.mappingRegistry.getRegistrations().keySet(), matches, request); } if (!matches.isEmpty()) { // 选择第一个为最匹配的 Match bestMatch = matches.get(0); /** * 如果匹配到多个 @RequestMapping(value="/mappin?") @RequestMapping(value="/mappin*") @RequestMapping(value="/{xxxx}") @RequestMapping(value="/**") */ if (matches.size() > 1) { //创建MatchComparator的匹配器对象 Comparator<Match> comparator = new MatchComparator(getMappingComparator(request)); /** 根据精准度排序 大概是这样的: ? > * > {} >** 具体可以去看: * @see org.springframework.util.AntPathMatcher.AntPatternComparator#compare(java.lang.String, java.lang.String)*/ matches.sort(comparator); // 排完序后拿到优先级最高的 bestMatch = matches.get(0); // 是否配置CORS并且匹配 if (CorsUtils.isPreFlightRequest(request)) { for (Match match : matches) { if (match.hasCorsConfig()) { return PREFLIGHT_AMBIGUOUS_MATCH; } } } else { //获取第二最匹配的 Match secondBestMatch = matches.get(1); //若第一个和第二个是一样的 抛出异常 if (comparator.compare(bestMatch, secondBestMatch) == 0) { Method m1 = bestMatch.getHandlerMethod().getMethod(); Method m2 = secondBestMatch.getHandlerMethod().getMethod(); String uri = request.getRequestURI(); throw new IllegalStateException(...); } } } //把最匹配的设置到request中 request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.getHandlerMethod()); handleMatch(bestMatch.mapping, lookupPath, request); //返回最匹配的 return bestMatch.getHandlerMethod(); } else { // return null return handleNoMatch(this.mappingRegistry.getRegistrations().keySet(), lookupPath, request); }}

【2.1】期待后面继续补充吧

本文链接地址:https://www.jiuchutong.com/zhishi/304538.html 转载请保留说明!

上一篇:phpcms怎么调用内容(php调用网址)

下一篇:CSS进阶内容—浮动和定位详解(css浮动怎么弄)

  • 网络推广有哪些方法,推广工具有哪些(网络推广有哪些工作岗位)

    网络推广有哪些方法,推广工具有哪些(网络推广有哪些工作岗位)

  • 微信手机号添加怎么设置(微信手机号添加好友显示账号异常)

    微信手机号添加怎么设置(微信手机号添加好友显示账号异常)

  • 抖音视频拍摄方法是怎样的(抖音视频拍摄方法有哪些)

    抖音视频拍摄方法是怎样的(抖音视频拍摄方法有哪些)

  • 抖音蓝v怎么开通(抖音蓝V怎么开通子账号)

    抖音蓝v怎么开通(抖音蓝V怎么开通子账号)

  • 拼多多直通车夜里可以关掉嘛(拼多多直通车晚上可以调低一点吗?)

    拼多多直通车夜里可以关掉嘛(拼多多直通车晚上可以调低一点吗?)

  • ipad长按不能关机(ipad长按关机键关不了机)

    ipad长按不能关机(ipad长按关机键关不了机)

  • word换行无法与上一行对齐(word无法切换行/列)

    word换行无法与上一行对齐(word无法切换行/列)

  • 华为手机很久不用无法启动(华为手机很久不用忘记密码怎么办)

    华为手机很久不用无法启动(华为手机很久不用忘记密码怎么办)

  • kindle出现感叹号电池(kindle出现感叹号怎么开机)

    kindle出现感叹号电池(kindle出现感叹号怎么开机)

  • 微信发原图会被定位吗(微信发原图会被降低清晰度)

    微信发原图会被定位吗(微信发原图会被降低清晰度)

  • 什么东西可以代替触屏笔(什么东西可以代替糖)

    什么东西可以代替触屏笔(什么东西可以代替糖)

  • 微信视频无法获取摄像头数据是怎么回事(微信视频无法获取)

    微信视频无法获取摄像头数据是怎么回事(微信视频无法获取)

  • 苹果5s电信版能用移动卡吗(苹果5s电信版能改4g网络吗)

    苹果5s电信版能用移动卡吗(苹果5s电信版能改4g网络吗)

  • ipad可以画画吗(redmipad可以画画吗)

    ipad可以画画吗(redmipad可以画画吗)

  • 华为p30pro是不是5g(华为p30pro是不是曲屏)

    华为p30pro是不是5g(华为p30pro是不是曲屏)

  • 苹果xr如何关机重启(苹果xr如何关机重启手机)

    苹果xr如何关机重启(苹果xr如何关机重启手机)

  • 去哪儿旅行优惠券怎么取消(去哪儿旅行优惠券领取)

    去哪儿旅行优惠券怎么取消(去哪儿旅行优惠券领取)

  • 抖音怎么看我艾特别人的视频(抖音怎么看我艾特别人的作品)

    抖音怎么看我艾特别人的视频(抖音怎么看我艾特别人的作品)

  • 手机录音删除了怎么办(手机录音删除了没有备份还可以恢复吗)

    手机录音删除了怎么办(手机录音删除了没有备份还可以恢复吗)

  • 手机加密dns有什么作用(手机加密dns是什么)

    手机加密dns有什么作用(手机加密dns是什么)

  • 京东闪付添加小米钱包(京东闪付怎样添加到小米钱包)

    京东闪付添加小米钱包(京东闪付怎样添加到小米钱包)

  • 淘新闻绑定微信安全吗(淘新闻为什么显示网络错误)

    淘新闻绑定微信安全吗(淘新闻为什么显示网络错误)

  • iphonex有没有红外线(iphone x有红外线吗?)

    iphonex有没有红外线(iphone x有红外线吗?)

  • vivo手机相机时间水印设置在哪里(vivo手机相机时间地点设置在哪里)

    vivo手机相机时间水印设置在哪里(vivo手机相机时间地点设置在哪里)

  • 苹果怎么开volte(苹果手机怎么打开volte)

    苹果怎么开volte(苹果手机怎么打开volte)

  • c0000218蓝屏解救方法(蓝屏c0000218代码是什么情况)

    c0000218蓝屏解救方法(蓝屏c0000218代码是什么情况)

  • Mac系统怎么查询是否感染iWorm病毒?(mac 系统查看)

    Mac系统怎么查询是否感染iWorm病毒?(mac 系统查看)

  • 如何解决U盘无法传输4G以上超大文件(u盘无内容)

    如何解决U盘无法传输4G以上超大文件(u盘无内容)

  • 「文心一言」内测详细使用体验(文心为何意)

    「文心一言」内测详细使用体验(文心为何意)

  • 超过小规模纳税人
  • 缴纳房产税怎么记账
  • 一般纳税人开具专票和普票的区别
  • 公司会计日常工作内容
  • 出资人和法人承担责任
  • 增值税普通发票几个点
  • 农产品的收购价格
  • 销售不同税率的货物会计处理
  • 退货应入会计什么科目
  • 发票勾选没有确认怎么办
  • 所得税网上申报表
  • 房地产教育培训
  • 航天服务费手写发票可以抵减吗?
  • 股份转让的溢价是什么意思
  • 什么科目呀
  • 企业所得税年报过了申报期还能修改吗
  • 企业报税表
  • 没有金税盘怎么查询已开发票
  • 生产企业出口退税申报系统详细操作流程
  • 远期汇票的付款期限可以表示为
  • 怎么计提担保赔偿准备金?
  • 汇兑损益怎么记账
  • 工程咨询费用取费标准
  • 收到以前年度退回的企业所得税
  • 临时设施是租入还是租入
  • 怎么安装win7系统u盘
  • kb4586863更新
  • PHP:Memcached::setMulti()的用法_Memcached类
  • 设备进项抵扣新政策
  • 记帐凭证由谁填制
  • 我告诉你windows7旗舰版
  • php fgets
  • 多角度分析数据
  • 房地产开发间接费
  • 投资者减除费用60000
  • 税票报税怎么弄
  • 酒店客房成本构成
  • 出口退还增值税消费税政策
  • 资金平衡表的编制方法
  • vue基础知识
  • 为什么说网络安全靠人民
  • matching path
  • wordpress mobile themes
  • 专项应付款二级明细科目有哪些
  • 经营性应收项目和经营性应付项目
  • 织梦安装详细教程
  • 小规模纳税人的个人所得税怎么算
  • 个税免税部分
  • 资金结存属于资产科目吗
  • 员工工资可以当进项税吗
  • 成品油企业开具电票前还需要先进行库存下载吗
  • 先确认收入后开票怎么做分录
  • 进项税额可以抵扣会计分录
  • 资金占用费如何计算企业所得税
  • 高价购买股权
  • 销售废旧物资如何入账
  • 个体工商户的公章丢了怎么办
  • 工业企业无形资产有哪些
  • 用sql语句添加删除字段
  • centos6.9开启ssh服务
  • win7怎么设置禁止安装软件
  • 电脑图片密码是什么意思
  • win8 怎么样
  • win7安装软件后所有打开变成记事本
  • win7开机后一段时间卡死
  • win8怎么打开蓝牙设置
  • win7系统中如何查看隐藏文件
  • win7开机错误代码
  • html中渐变
  • perl \w
  • cocos2d-x教程
  • Python 数据清洗
  • js遍历foreach
  • javascript面向对象编程指南
  • 河南办税服务厅
  • 怎么查询个人所得税申报成功
  • 四川税收总额
  • 深圳市国家税务局电话
  • 北京市东城分局刑侦大队电话
  • 车险包括车船费吗
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

    网站地图: 企业信息 工商信息 财税知识 网络常识 编程技术

    友情链接: 武汉网站建设