欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

這一次搞懂SpringMVC原理說明

 更新時間:2020年08月27日 11:10:38   作者:夜勿語  
這篇文章主要介紹了這一次搞懂SpringMVC原理說明,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧

前言

前面幾篇文章,學習了Spring IOC、Bean實例化過程、AOP、事務的源碼和設計思想,了解了Spring的整體運行流程,但如果是web開發(fā),那么必不可少的還有Spring MVC,本篇主要分析在請求調(diào)用過程中SpringMVC的實現(xiàn)原理,通過本篇要搞懂它是怎么解決請求、參數(shù)、返回值映射等問題的。

正文

請求入口

我們都知道前端調(diào)用后端接口時,都會通過Servlet進行轉(zhuǎn)發(fā),而Servlet的聲明周期包含下面四個階段:

實例化(new)

初始化(init)

執(zhí)行(service調(diào)用doGet/doPost)

銷毀(destroy)

前兩個階段在Spring啟動階段就做好了(init根據(jù)配置可能是第一次請求時才會調(diào)用),銷毀是服務關閉的時候進行,本文主要分析的就是請求執(zhí)行階段。我們知道SpringMVC的核心就是DispatcherServlet,該類是對Servlet的擴展,所以直接從該類的service方法開始,但在此類中沒有service方法,那肯定是在其父類中,我們先來看看其繼承體系:

逐個往上找,在FrameworkServlet方法中就有一個service方法:

 protected void service(HttpServletRequest request, HttpServletResponse response)
 throws ServletException, IOException {

 HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
 if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
 processRequest(request, response);
 }
 else {
 super.service(request, response);
 }
 }

 protected void service(HttpServletRequest req, HttpServletResponse resp)
  throws ServletException, IOException
 {
  String method = req.getMethod();

  if (method.equals(METHOD_GET)) {
   long lastModified = getLastModified(req);
   if (lastModified == -1) {
    doGet(req, resp);
   } else {
    long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
    if (ifModifiedSince < lastModified) {
     maybeSetLastModified(resp, lastModified);
     doGet(req, resp);
    } else {
     resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
    }
   }

  } else if (method.equals(METHOD_HEAD)) {
   long lastModified = getLastModified(req);
   maybeSetLastModified(resp, lastModified);
   doHead(req, resp);
  } else if (method.equals(METHOD_POST)) {
   doPost(req, resp);
  } else if (method.equals(METHOD_PUT)) {
   doPut(req, resp);
  } else if (method.equals(METHOD_DELETE)) {
   doDelete(req, resp);
  } else if (method.equals(METHOD_OPTIONS)) {
   doOptions(req,resp);
  } else if (method.equals(METHOD_TRACE)) {
   doTrace(req,resp);
  } else {
   String errMsg = lStrings.getString("http.method_not_implemented");
   Object[] errArgs = new Object[1];
   errArgs[0] = method;
   errMsg = MessageFormat.format(errMsg, errArgs);
   
   resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
  }
 }

但其主要還是調(diào)用父類HttpServlet中的方法,而該類又會根據(jù)不同的請求方式會調(diào)到子類中,最后的核心方法就是DispatcherServlet中的doDispatch方法:

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
 HttpServletRequest processedRequest = request;
 HandlerExecutionChain mappedHandler = null;
 boolean multipartRequestParsed = false;

 //異步管理
 WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

 try {
 ModelAndView mv = null;
 Exception dispatchException = null;

 try {
 //文件上傳
 processedRequest = checkMultipart(request);
 multipartRequestParsed = (processedRequest != request);

 //這個方法很重要,重點看
 // Determine handler for the current request.
 mappedHandler = getHandler(processedRequest);
 if (mappedHandler == null) {
  noHandlerFound(processedRequest, response);
  return;
 }

 //獲取跟HandlerMethod匹配的HandlerAdapter對象
 // Determine handler adapter for the current request.
 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

 // Process last-modified header, if supported by the handler.
 String method = request.getMethod();
 boolean isGet = "GET".equals(method);
 if (isGet || "HEAD".equals(method)) {
  long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
  if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
  return;
  }
 }

 //前置過濾器,如果為false則直接返回
 if (!mappedHandler.applyPreHandle(processedRequest, response)) {
  return;
 }

 //調(diào)用到Controller具體方法,核心方法調(diào)用,重點看看
 // Actually invoke the handler.
 mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

 if (asyncManager.isConcurrentHandlingStarted()) {
  return;
 }

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

 //視圖渲染及后置過濾器執(zhí)行
 processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
 }
 catch (Exception ex) {
 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);
 }
 }
 }
 }

MVC的所有處理邏輯都在這個方法中,先總結(jié)一下這個方法的實現(xiàn)邏輯,首先根據(jù)請求的url拿到緩存中的HandlerMethod對象和執(zhí)行鏈對象,HandlerMethod中封裝了controller對象、方法對象和方法參數(shù)等信息,執(zhí)行鏈則是包含了一個個HandlerInterceptor攔截器;然后再通過HandlerMethod拿到對應的HandlerAdapter,這個對象的作用就是去適配我們的controller;準備工作做完后,首先會執(zhí)行前置過濾,如果被攔截則直接返回,否則就去調(diào)用controller中的方法執(zhí)行我們的業(yè)務邏輯并返回一個ModelView對象;接著執(zhí)行中置過濾器,以及處理全局異常捕獲器捕獲到異常;最后進行視圖渲染返回并執(zhí)行后置過濾器進行資源釋放等工作。

以上就是MVC的整體執(zhí)行流程,下面就逐個來分析,首先進入getHandler方法:

 protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
 //handlerMappering實例
 if (this.handlerMappings != null) {
 for (HandlerMapping mapping : this.handlerMappings) {
 //獲取HandlerMethod和過濾器鏈的包裝類
 HandlerExecutionChain handler = mapping.getHandler(request);
 if (handler != null) {
  return handler;
 }
 }
 }
 return null;
 }

是委托給HandlerMapping對象的,這是一個接口,主要的實現(xiàn)類是RequestMappingHandlerMapping,同樣先來看看其繼承體系:

這個類是管理請求和處理類之間的映射關系的,你是否疑惑它是在哪里實例化的呢?下面先來看看MVC組件的初始化。

組件初始化

這里我以自動化配置的注解方式說明,Spring提供了一個@EnableWebMvc,通過前面的學習我們知道在這個注解中必定導入了一個配置類,點進去可以看到是DelegatingWebMvcConfiguration,這個類就是負責MVC的組件和擴展實現(xiàn)的初始化,其本身我們先不看,先看其父類WebMvcConfigurationSupport,這個類我們應該不陌生,要做一些自定義擴展時就需要繼承該類(如攔截器Interceptor),同樣作用的類還有WebMvcConfigurerAdapter,這個類是對前者相對安全的擴展,為什么是相對安全呢?因為繼承前者會導致自動配置失效,而使用后者則不必擔心此問題,只需要在類上加上@EnableWebMvc注解。

在WebMvcConfigurationSupport中我們可以看到很多@Bean標注的方法,也就是mvc組件的實例化,這里主要看看requestMappingHandlerMapping,其余的可自行閱讀理解,也就是一些Bean的注冊:

 public RequestMappingHandlerMapping requestMappingHandlerMapping() {
 RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
 mapping.setOrder(0);
 mapping.setInterceptors(getInterceptors());
 mapping.setContentNegotiationManager(mvcContentNegotiationManager());
 mapping.setCorsConfigurations(getCorsConfigurations());

 ......省略

 return mapping;
 }

這里主要看getInterceptors方法如何獲取攔截器的:

 protected final Object[] getInterceptors() {
 if (this.interceptors == null) {
 InterceptorRegistry registry = new InterceptorRegistry();
 //鉤子方法,需要自己定義
 addInterceptors(registry);
 registry.addInterceptor(new ConversionServiceExposingInterceptor(mvcConversionService()));
 registry.addInterceptor(new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider()));
 this.interceptors = registry.getInterceptors();
 }
 return this.interceptors.toArray();
 }

第一次進來會調(diào)用addInterceptors添加攔截器,這是一個模板方法,在子類DelegatingWebMvcConfiguration中實現(xiàn):

 private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
 
 protected void addInterceptors(InterceptorRegistry registry) {
 this.configurers.addInterceptors(registry);
 }

 public void addInterceptors(InterceptorRegistry registry) {
 for (WebMvcConfigurer delegate : this.delegates) {
 delegate.addInterceptors(registry);
 }
 }

可以看到最終是調(diào)用WebMvcConfigurer的addInterceptors方法,也就是我們對WebMvcConfigurerAdapter的自定義擴展??吹竭@里我們應該明白了MVC的組件是如何添加到IOC容器中的,但是DispatcherServlet又是怎么獲取到它們的呢?回到之前的代碼中,在DispatcherServlet這個類中有一個onRefresh方法,這個方法又調(diào)用了initStrategies方法完成了MVC九大組件的注冊:

 protected void onRefresh(ApplicationContext context) {
 initStrategies(context);
 }

 protected void initStrategies(ApplicationContext context) {
 initMultipartResolver(context);
 initLocaleResolver(context);
 initThemeResolver(context);
 initHandlerMappings(context);
 initHandlerAdapters(context);
 initHandlerExceptionResolvers(context);
 initRequestToViewNameTranslator(context);
 initViewResolvers(context);
 initFlashMapManager(context);
 }

 private void initHandlerMappings(ApplicationContext context) {
 this.handlerMappings = null;

 if (this.detectAllHandlerMappings) {
 // Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
 Map<String, HandlerMapping> matchingBeans =
  BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
 if (!matchingBeans.isEmpty()) {
 this.handlerMappings = new ArrayList<>(matchingBeans.values());
 // We keep HandlerMappings in sorted order.
 AnnotationAwareOrderComparator.sort(this.handlerMappings);
 }
 }
 else {
 try {
 HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
 this.handlerMappings = Collections.singletonList(hm);
 }
 catch (NoSuchBeanDefinitionException ex) {
 // Ignore, we'll add a default HandlerMapping later.
 }
 }
 
 if (this.handlerMappings == null) {
 this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
 }
 }

以initHandlerMappings為例,其它組件實現(xiàn)邏輯基本一樣。首先從IOC容器中拿到handlerMappings的所有實現(xiàn)類(WebMvcConfigurationSupport中注入的對象就在這里被獲取到),若沒有,則從DispatcherServlet.properties配置文件中(這個配置在spring-webmvc工程下org/springframework/web/servlet/DispatcherServlet.properties)獲取默認的配置:

org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver

org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver

org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
 org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping

org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
 org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
 org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter

org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
 org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
 org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver

org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator

org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver

org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager

但是onRefresh又是在什么時候調(diào)用的呢?有兩個地方,一個是Servlet初始化時會調(diào)用到initWebApplicationContext進行容器的初始化,這個方法中就會觸發(fā)onRefresh;另外還有一個,在FrameworkServlet中有一個onApplicationEvent方法,而這個方法又會被內(nèi)部類ContextRefreshListener調(diào)用,這個類實現(xiàn)了ApplicationListener接口,表示會接收容器刷新事件。

以上就就是MVC HandlerMapping組件的初始化邏輯,其它組件實現(xiàn)邏輯相同,下面不再分析。

調(diào)用Controller

回到getHandler方法,其調(diào)用的是AbstractHandlerMapping類的方法:

 public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
 //根據(jù)請求的uri拿到對應的HandlerMethod對象
 Object handler = getHandlerInternal(request);
 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);
 }

 //獲取HandlerMethod和過濾器鏈的包裝類
 HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);

 if (logger.isTraceEnabled()) {
 logger.trace("Mapped to " + handler);
 }
 else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
 logger.debug("Mapped to " + executionChain.getHandler());
 }

 //是否是跨域請求,就是查看request請求頭中是否有Origin屬性
 if (CorsUtils.isCorsRequest(request)) {
 //自定義的鉤子方法獲取跨域配置
 CorsConfiguration globalConfig = this.corsConfigurationSource.getCorsConfiguration(request);
 //注解獲取跨域配置
 CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
 CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
 //這里設置了跨域的過濾器CorsInterceptor
 executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
 }

 return executionChain;
 }

先看AbstractHandlerMethodMapping.getHandlerInternal:

 protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
 //從request對象中獲取uri,/common/query2
 String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
 this.mappingRegistry.acquireReadLock();
 try {
 //根據(jù)uri從映射關系中找到對應的HandlerMethod對象
 HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
 //把Controller類實例化
 return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
 }
 finally {
 this.mappingRegistry.releaseReadLock();
 }
 }

 protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
 List<Match> matches = new ArrayList<>();
 // 根據(jù)url拿到對應的RequestMappingInfo
 List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
 if (directPathMatches != null) {
 addMatchingMappings(directPathMatches, matches, request);
 }
 if (matches.isEmpty()) {
 // No choice but to go through all mappings...
 addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
 }

 if (!matches.isEmpty()) {
 Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
 matches.sort(comparator);
 Match bestMatch = matches.get(0);
 if (matches.size() > 1) {
 if (logger.isTraceEnabled()) {
  logger.trace(matches.size() + " matching mappings: " + matches);
 }
 if (CorsUtils.isPreFlightRequest(request)) {
  return PREFLIGHT_AMBIGUOUS_MATCH;
 }
 Match secondBestMatch = matches.get(1);
 //如果兩個RequestMappinginfo什么都相同,報錯
 if (comparator.compare(bestMatch, secondBestMatch) == 0) {
  Method m1 = bestMatch.handlerMethod.getMethod();
  Method m2 = secondBestMatch.handlerMethod.getMethod();
  String uri = request.getRequestURI();
  throw new IllegalStateException(
  "Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
 }
 }
 request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
 handleMatch(bestMatch.mapping, lookupPath, request);
 return bestMatch.handlerMethod;
 }
 else {
 return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
 }
 }

 private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {
 for (T mapping : mappings) {
 // 拿到匹配的RequestMappingInfo對象,有可能url相同,@RequestMapping的屬性(請求方式、參數(shù)等)匹配不上
 T match = getMatchingMapping(mapping, request);
 if (match != null) {
 //RequestMappingInfo對象和HandlerMethod對象封裝到Match對象中,其實就是注解屬性和Method對象的映射
 matches.add(new Match(match, this.mappingRegistry.getMappings().get(mapping)));
 }
 }
 }

這里邏輯很簡單,就是通過請求url從urlLookup中拿到對應的RequestMappingInfo(每一個 @RequestMapping對應一個RequestMappingInfo對象)對象,再根據(jù)RequestMappingInfo對象從mappingLookup拿到對應的HandlerMethod并返回。

但這里你可能會比較好奇urlLookup和mappingLookup從哪里來的,仔細觀察你會發(fā)現(xiàn)當前這個類實現(xiàn)了一個接口InitializingBean,實現(xiàn)了這個接口的類會在該類的Bean實例化完成后調(diào)用afterPropertiesSet方法,上面的映射關系就是在這個方法中做的。實際上這個方法不止完成了上面兩個映射關系,還有下面兩個:

corsLookup:handlerMethod -> corsConfig

registry:RequestMappingInfo -> MappingRegistration(包含url、handlerMethod、RequestMappingInfo、name等信息)

這里就不展開分析了,奉上一張時序圖,讀者可根據(jù)下面的時序圖自行分析:

拿到HandlerMethod對象后,又會通過getHandlerExecutionChain方法去獲取到所有的HandlerInterceptor攔截器對象,并連同HandlerMethod對象一起封裝為HandlerExecutionChain。之后是獲取跨域配置,這里不詳細分析。

拿到HandlerExecutionChain對象后返回到doDispatch方法,又調(diào)用了getHandlerAdapter

方法拿到HandlerAdapter:

 protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
 //根據(jù)handlerMethod對象,找到合適的HandlerAdapter對象,這里用到了策略模式
 if (this.handlerAdapters != null) {
 for (HandlerAdapter adapter : this.handlerAdapters) {
 if (adapter.supports(handler)) {
  return adapter;
 }
 }
 }
 }

這里的handlerAdapters變量值從哪里來?相信不用我再分析,主要看這里的設計思想,典型的策略模式。

之后調(diào)用完前置過濾器后,才是真正調(diào)用我們controller方法的邏輯,通過HandlerAdapter.handle去調(diào)用,最終會調(diào)用到ServletInvocableHandlerMethod.invokeAndHandle:

 public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
 Object... providedArgs) throws Exception {

 //具體調(diào)用邏輯,重點看
 Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
 setResponseStatus(webRequest);

 if (returnValue == null) {
 if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
 mavContainer.setRequestHandled(true);
 return;
 }
 }
 else if (StringUtils.hasText(getResponseStatusReason())) {
 mavContainer.setRequestHandled(true);
 return;
 }

 mavContainer.setRequestHandled(false);
 Assert.state(this.returnValueHandlers != null, "No return value handlers");
 try {
 //返回值處理
 this.returnValueHandlers.handleReturnValue(
  returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
 }
 catch (Exception ex) {
 if (logger.isTraceEnabled()) {
 logger.trace(formatErrorForReturnValue(returnValue), ex);
 }
 throw ex;
 }
 }

這個方法里面主要看invokeForRequest和handleReturnValue的調(diào)用,前者是完成參數(shù)綁定并調(diào)用controller,后者則是對返回值進行處理并封裝到ModelAndViewContainer中。先來看invokeForRequest:

 public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
 Object... providedArgs) throws Exception {

 //獲取參數(shù)數(shù)組
 Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
 if (logger.isTraceEnabled()) {
 logger.trace("Arguments: " + Arrays.toString(args));
 }
 return doInvoke(args);
 }

doInvoke就是完成反射調(diào)用,主要還是看參數(shù)綁定的實現(xiàn)邏輯,在getMethodArgumentValues方法中:

 protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
 Object... providedArgs) throws Exception {

 if (ObjectUtils.isEmpty(getMethodParameters())) {
 return EMPTY_ARGS;
 }
 //入?yún)⒌陌b類,里面包裝了參數(shù)類型,參數(shù)名稱,參數(shù)注解等等信息
 MethodParameter[] parameters = getMethodParameters();
 Object[] args = new Object[parameters.length];
 for (int i = 0; i < parameters.length; i++) {
 MethodParameter parameter = parameters[i];
 //設置參數(shù)名稱解析器
 parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
 args[i] = findProvidedArgument(parameter, providedArgs);
 if (args[i] != null) {
 continue;
 }
 //典型的策略模式,根據(jù)parameter能否找到對應參數(shù)的處理類,能找到就返回true
 if (!this.resolvers.supportsParameter(parameter)) {
 throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
 }
 try {
 //具體參數(shù)值解析過程,重點看看
 args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
 }
 catch (Exception ex) {
 // Leave stack trace for later, exception may actually be resolved and handled..
 if (logger.isDebugEnabled()) {
  String error = ex.getMessage();
  if (error != null && !error.contains(parameter.getExecutable().toGenericString())) {
  logger.debug(formatArgumentError(parameter, error));
  }
 }
 throw ex;
 }
 }
 return args;
 }

參數(shù)、返回值解析

因為參數(shù)類型非常多,同時還會伴隨各種注解,如:@RequestBody、@RequestParam、@PathVariable等,所以參數(shù)解析的工作是非常繁雜的,同時還要考慮到擴展性,所以SpringMVC依然采用了策略模式來完成對各種參數(shù)類型的解析綁定,其頂層接口就是HandlerMethodArgumentResolver,而默認SpringMVC提供的解析方式就高達20多種:

上面是類圖,讀者可根據(jù)自己熟悉的參數(shù)類型找到對應的類進行分析,最核心的還是要掌握這里的設計思想。

接著方法調(diào)用完成后就是對返回值的處理,同樣的,返回值類型也是非常多,也可以使用各種注解標注,所以也是使用策略模式實現(xiàn),其頂層接口是HandlerMethodReturnValueHandler,實現(xiàn)類如下:

調(diào)用完成之后就是執(zhí)行后續(xù)操作了:執(zhí)行中置過濾器、處理全局異常、視圖渲染以及執(zhí)行后置過濾器,這些與主流程沒有太大關系,本篇不展開分析了,最后是MVC的執(zhí)行時序圖:

總結(jié)

本篇是Spring核心原理系列的最后一篇,前前后后花了一個月時間,終于從宏觀上大致上理解了Spring的實現(xiàn)原理和運行機制,明白了之前項目中一些坑是如何產(chǎn)生的,最主要的是學到設計模式的運用以及如何利用Spring的一些常用的擴展點進行自定義擴展。但對于Spring這個龐大的體系來說,還有很多是要去理解學習的,尤其是設計思想,只有長期琢磨才能深刻的理解掌握。在我之前的文章中包括本篇還有很多沒分析到的細節(jié),在后面我會不定期分享出來。希望能給大家一個參考,也希望大家多多支持腳本之家。

相關文章

  • Java實現(xiàn)調(diào)用接口API并返回數(shù)據(jù)

    Java實現(xiàn)調(diào)用接口API并返回數(shù)據(jù)

    這篇文章主要介紹了Java實現(xiàn)調(diào)用接口API并返回數(shù)據(jù)方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-05-05
  • 詳解Java向服務端發(fā)送文件的方法

    詳解Java向服務端發(fā)送文件的方法

    這篇文章主要為大家詳細介紹了Java向服務端發(fā)送文件的方法,主要是IO流,感興趣的小伙伴們可以參考一下
    2016-05-05
  • SpringBoot JWT接口驗證實現(xiàn)流程詳細介紹

    SpringBoot JWT接口驗證實現(xiàn)流程詳細介紹

    這篇文章主要介紹了SpringBoot+JWT實現(xiàn)接口驗證,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習吧
    2022-09-09
  • Java?easyexcel使用教程之導出篇

    Java?easyexcel使用教程之導出篇

    EasyExcel是阿里巴巴開源的一個excel處理框架,以使用簡單,節(jié)省內(nèi)存著稱,下面這篇文章主要給大家介紹了關于Java?easyexcel使用教程之導出篇的相關資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2022-06-06
  • Java中Integer方法實例詳解

    Java中Integer方法實例詳解

    這篇文章主要給大家介紹了關于Java中Integer方法的相關資料,Java中的Integer是int的包裝類型,文中通過代碼實例介紹的非常詳細,需要的朋友可以參考下
    2023-08-08
  • SpringBoot+Spring Security無法實現(xiàn)跨域的解決方案

    SpringBoot+Spring Security無法實現(xiàn)跨域的解決方案

    這篇文章主要介紹了SpringBoot+Spring Security無法實現(xiàn)跨域的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • Java實現(xiàn)排球比賽計分系統(tǒng)

    Java實現(xiàn)排球比賽計分系統(tǒng)

    這篇文章主要為大家詳細介紹了Java實現(xiàn)排球比賽計分系統(tǒng),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-06-06
  • Java開發(fā)崗位面試被問到嵌套類怎么辦

    Java開發(fā)崗位面試被問到嵌套類怎么辦

    本篇文章主要介紹了深入理解Java嵌套類和內(nèi)部類,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2021-07-07
  • 解決@JsonIgnore的使用以及自己踩坑

    解決@JsonIgnore的使用以及自己踩坑

    這篇文章主要介紹了解決@JsonIgnore的使用以及自己踩坑,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-07-07
  • Java 重命名 Excel 工作表并設置工作表標簽顏色的示例代碼

    Java 重命名 Excel 工作表并設置工作表標簽顏色的示例代碼

    這篇文章主要介紹了Java 重命名 Excel 工作表并設置工作表標簽顏色的示例代碼,代碼簡單易懂,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-10-10

最新評論