springmvc5.x-mvc-Implementierungsprinzip und Quellcode-Implementierung

Oben: Spring5.x-deklaratives Transaktionsprinzip und Quellcode-Implementierung


Artikelserie:

                    spring5.x-deklaratives Transaktionsprinzip und Quellcode-Implementierung

                    Spring5.x-AOP-Implementierungsprinzip und Quellcode-Analyse

                    Spring5.x-Listener-Prinzip und Quellcode-Implementierung

                    spring5.x-solve zirkuläre Abhängigkeitsanalyse

                    Lernen des Quellcodes des spring5.x-IOC-Moduls

                    Einführung in spring5.x und die dazugehörige Spring-Quellcode-Leseumgebung


Grundwissen

Bitte lesen Sie den Originalartikel: springmvc

Lernen des Quellcodes

cca5a76aada179e072014928521a51f5.png

@RequestMapping("/{id}")
    public String showUserInfo(ModelMap modelMap, @PathVariable("id")Integer id){
        Student student = new Student();
        student.setId(id);
        student.setAge(100);
        student.setName("test");
        modelMap.addAttribute("name", student.getName());
        modelMap.addAttribute("age", student.getAge());
        modelMap.addAttribute("id", student.getId());
        return "result";
    }

Springmvc-Initialisierung

  1. DispatcherServlet-Initialisierung: DispatcherServlet ist der Front-End-Controller von Spring MVC. DispatcherServlet wird initialisiert, wenn der Servlet-Container in web.xml oder WebApplicationInitializer konfiguriert wird. Der Speicherort des Initialisierungsquellcodes von DispatcherServlet ist org.springframework.web.servlet.DispatcherServlet.

<servlet>
    <servlet-name>springDispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:springmvc.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>

Codespeicherort: org.springframework.web.servlet.DispatcherServlet#initStrategies

//用于初始化 Spring MVC 的各个策略组件。
protected void initStrategies(ApplicationContext context) {
      //初始化处理文件上传的解析器,用于解析请求中的 multipart 数据。
        this.initMultipartResolver(context);
      //初始化处理国际化的解析器,用于解析请求中的语言区域信息。
        this.initLocaleResolver(context);
      //初始化处理主题的解析器,用于解析请求中的主题信息。
        this.initThemeResolver(context);
      //初始化处理器映射器,用于将请求映射到对应的处理器(Controller)。
        this.initHandlerMappings(context);
      //初始化处理器适配器,用于调用处理器的方法并从中获取 ModelAndView 对象。
        this.initHandlerAdapters(context);
      //初始化处理器异常解析器,用于处理请求过程中发生的异常。
        this.initHandlerExceptionResolvers(context);
      //初始化请求到视图名称的转换器,用于将处理器返回的逻辑视图名称转换为实际的视图路径。
        this.initRequestToViewNameTranslator(context);
      //初始化视图解析器,用于将视图名称解析为具体的视图类型。
        this.initViewResolvers(context);
      //初始化 FlashMap 管理器,用于处理请求间的数据传递。
        this.initFlashMapManager(context);
    }

dc032971efa1ce353e8c28da77fc3fe8.png

  1. ServletContextListener registrieren: Wenn der Servlet-Container gestartet wird, wird normalerweise ein Listener (ServletContextListener) registriert, um den Spring MVC-Kontext zu initialisieren. Implementieren Sie die Spring MVC-Initialisierung in der contextInitialized-Methode des Listeners, z. B. Laden von Konfigurationsdateien, Erstellen von ApplicationContext usw.

Codespeicherort: org.springframework.context.event.SimpleApplicationEventMulticaster#doInvokeListener

//执行应用程序事件监听器
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
        try {
            //调用监听器的 并传递当前发生的应用程序事件作为参数。
            listener.onApplicationEvent(event);
        } catch (ClassCastException var6) {
            String msg = var6.getMessage();
            if (msg != null && !this.matchesClassCastMessage(msg, event.getClass().getName())) {
                throw var6;
            }

            Log logger = LogFactory.getLog(this.getClass());
            if (logger.isDebugEnabled()) {
                logger.debug("Non-matching event type for listener: " + listener, var6);
            }
        }

    }
  1. WebApplicationContext-Initialisierung: Spring MVC verwendet einen eigenen Container (WebApplicationContext), der erstellt und konfiguriert wird, wenn ServletContextListener initialisiert wird. Informationen zur Initialisierung von WebApplicationContext finden Sie im Quellcode von org.springframework.web.context.support.XmlWebApplicationContext oder org.springframework.web.context.support.AnnotationConfigWebApplicationContext.

da408f84bedb1e065eb92149bcf7dc26.png

  1. HandlerMapping- und HandlerAdapter-Initialisierung: HandlerMapping ist für die Zuordnung von Anforderungen zum entsprechenden Prozessor (Controller) verantwortlich, während HandlerAdapter für den Aufruf der entsprechenden Prozessormethode verantwortlich ist. Die Initialisierung dieser Komponenten umfasst Konfigurationsdateien, das Scannen von Anmerkungen und andere Prozesse. Die relevanten Quellcodespeicherorte sind org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping, org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping, org .springframework.web .servlet.handler.SimpleUrlHandlerMapping sowie org.springframework.web.servlet.handler.BeanNameUrlHandlerAdapter, org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter usw.

90d762452f9c9b0ad1331f787b3c0601.pnga02ec54c3a625cdd83c7e76f1bb511f0.png

Beachten Sie den Listener: org.springframework.context.event.SimpleApplicationEventMulticaster#doInvokeListener

1e32edea4a64e084cb4983a003009cbb.png

  1. Initialisierung des View-Resolvers: Der View-Resolver (ViewResolver) ist dafür verantwortlich, den Rückgabewert der Prozessormethode in eine bestimmte Ansicht zu analysieren. Spring MVC unterstützt mehrere Arten von Ansichtsauflösern, z. B. InternalResourceViewResolver, FreeMarkerViewResolver usw. Während des Initialisierungsprozesses werden die relevanten Eigenschaften und Speicherorte des View Resolvers konfiguriert, wie z. B. View-Präfix, Suffix usw. Der relevante Quellcodespeicherort ist org.springframework.web.servlet.view.InternalResourceViewResolver.

Da die von uns konfigurierte Ansicht lautet:

<!-- 配置视图解析器 -->
  <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/views/"></property>
    <property name="suffix" value=".jsp"></property>
  </bean>

Es analysiert also Folgendes:

Beispiel: org.springframework.beans.factory.BeanFactoryUtils#beansOfTypeIncreasingAncestors(org.springframework.beans.factory.ListableBeanFactory, java.lang.Class<T>, boolean, boolean)

5e453c7e2e35df940479ed79abea1754.png

Schließlich werden wir alle onRefresh() aufrufen, um die Initialisierung abzuschließen. Codespeicherort: org.springframework.web.servlet.FrameworkServlet#initWebApplicationContext

if (!this.refreshEventReceived) {
      // Either the context is not a ConfigurableApplicationContext with refresh
      // support or the context injected at construction time had already been
      // refreshed -> trigger initial onRefresh manually here.
      onRefresh(wac);
    }

Implementierung der SpringMVC-Distribution

Nachdem die Federladung abgeschlossen ist, handelt es sich um eine Frage des Aufrufs. Beachten Sie hier, dass die Verteilung nach verschiedenen Aufrufmethoden erfolgt, z. B. http tcp und andere Verteilungsmethoden sind nicht identisch. Das wird irgendwann auf doDispatch angepasst.

Codespeicherort: org.springframework.web.servlet.DispatcherServlet#doDispatch

//分发方法
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        //初始化请求信息
    HttpServletRequest processedRequest = request;
        //用于存储根据请求对象匹配到的处理器对象
    HandlerExecutionChain mappedHandler = null;
        //标识是否已解析多部分请求
    boolean multipartRequestParsed = false;
      //获取当前WebAsyncManager对象(异步处理)
    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

    try {
            //初始化视图模型
      ModelAndView mv = null;
      Exception dispatchException = null;

      try {
                //检查请求是否为多部分请求(文件或),通过检查请求头中的 "Content-Type" 是否以 "multipart/" 开头来判断。
        processedRequest = checkMultipart(request);
                //判断是否一致,如果是则为true
        multipartRequestParsed = (processedRequest != request);

        // 获取处理对象
        mappedHandler = getHandler(processedRequest);
                //为空就是没找着路劲 返回404
        if (mappedHandler == null) {
          noHandlerFound(processedRequest, response);
          return;
        }

        // 获取处理对
        HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

        // 获取请求方法
        String method = request.getMethod();
                //判断是否为get请求
        boolean isGet = "GET".equals(method);
                //如果是 或头为HEAD
        if (isGet || "HEAD".equals(method)) {
                    //获取最后时间
          long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
          if (logger.isDebugEnabled()) {
            logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
          }
                    //新请一个响应并检查,如果不通过直接中止
          if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
            return;
          }
        }
              //调用处理器的预处理方法 ,如果不通过中止
        if (!mappedHandler.applyPreHandle(processedRequest, response)) {
          return;
        }

        // 调用适配器
        mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
              //检查异步处理是否已经开始
        if (asyncManager.isConcurrentHandlingStarted()) {
          return;
        }
              //设置默认的视图名到ModelAndView中
        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);
      }
            //返回客户端
      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);
        }
      }
    }
  }

15bdc30486a7e6764ddc821df1631bf7.png

Gehen Sie dann ins Detail

verarbeitetRequest = checkMultipart(request);

Codespeicherort: org.springframework.web.servlet.DispatcherServlet#checkMultipart

//将请求转换为分段请求,并使分段解析程序可用。如果未设置多部分解析程序,则只需使用现有请求。
protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {
        //不为空 且 请求是否是multipart类型
    if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {
            //将请求转换为 multipart类型的请求对象,
      if (WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class) != null) {
        logger.debug("Request is already a MultipartHttpServletRequest - if not in a forward, " +
            "this typically results from an additional MultipartFilter in web.xml");
      }
                //检查当前请求是否已经发生过多部分请求解析失败的异常
      else if (hasMultipartException(request) ) {
        logger.debug("Multipart resolution failed for current request before - " +
            "skipping re-resolution for undisturbed error rendering");
      }
      else {
        try {
                    //转换为HttpServletRequest 并返回
          return this.multipartResolver.resolveMultipart(request);
        }
        catch (MultipartException ex) {
          if (request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) != null) {
            logger.debug("Multipart resolution failed for error dispatch", ex);
            // Keep processing error dispatch with regular request handle below
          }
          else {
            throw ex;
          }
        }
      }
    }
    // If not returned before: return original request.
    return request;
  }

Die obige Methode analysiert mehrteilige Typanforderungen.

Als nächstes: mappedHandler = getHandler(processedRequest); Dies wird verwendet, um den Handler der aktuellen Anfrage zu bestimmen.

Nullable
  protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
      //不为空
    if (this.handlerMappings != null) {
            //循环调用每个 HandlerMapping 的 getHandler(request) 方法,传入当前的 HttpServletRequest 对象作为参数,来获取对应的处理器。
      for (HandlerMapping hm : this.handlerMappings) {
        if (logger.isTraceEnabled()) {
          logger.trace(
              "Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
        }
        HandlerExecutionChain handler = hm.getHandler(request);
        if (handler != null) {
          return handler;
        }
      }
    }
    return null;
  }

Dies wird verwendet, um die HandlerExecutionChain abzurufen, die eigentlich die Methode des Anforderungsprozessors (Handler) ist.

HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

Das Abrufen des HandlerAdapters basierend auf dem gerade erhaltenen Prozessor bedeutet tatsächlich, ihn zum Aufrufen von ModelandView oder anderen Ansichten zu verwenden. Es gibt viele Typen, wie zum Beispiel:

  1. RequestMappingHandlerAdapter: Passt Prozessorfunktionen, Controller mit Annotationen und andere Prozessortypen an.

  2. SimpleControllerHandlerAdapter: Passt Controller basierend auf der Implementierung der Controller-Schnittstelle an.

  3. HttpRequestHandlerAdapter: Passt sich an den Prozessor an, der die HttpRequestHandler-Schnittstelle implementiert und zur Verarbeitung der ursprünglichen HttpServletRequest und HttpServletResponse verwendet.

  4. Die Standardimplementierung von HandlerAdapter DefaultHandlerAdapter: Wird zur Verarbeitung von Prozessortypen ohne klaren Adapter verwendet, die Standardimplementierung von ServletInvocableHandlerMethod zum Ausführen der Prozessormethode.

  5. Adapter für asynchrone Anforderungen: z. B. solche, die AsyncHandlerInterceptor und Callable entsprechen.

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
    if (this.handlerAdapters != null) {
            //循环判断是哪种类型匹配,匹配就返回
      for (HandlerAdapter ha : this.handlerAdapters) {
        if (logger.isTraceEnabled()) {
          logger.trace("Testing handler adapter [" + ha + "]");
        }
                //获取最终的HandlerAdapter
        if (ha.supports(handler)) {
          return ha;
        }
      }
    }
    throw new ServletException("No adapter for handler [" + handler +
        "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
  }

mappedHandler.applyPreHandle (processedRequest, Antwort), diese Methode wird verwendet, um den Ausführungsort des Interceptors aufzuzeichnen.

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];
                //调用前置拦截器方法 如果返回值为 false,表示拦截器不允许继续执行后续的处理逻辑
        if (!interceptor.preHandle(request, response, this.handler)) {
                    //方法进行拦截器链的后置处理,并直接返回 false
          triggerAfterCompletion(request, response, null);
          return false;
        }
                //记录位置
        this.interceptorIndex = i;
      }
    }
    return true;
  }
//用于触发拦截器链的后置处理(afterCompletion
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex)
      throws Exception {

    HandlerInterceptor[] interceptors = getInterceptors();
      //不为空
    if (!ObjectUtils.isEmpty(interceptors)) {
            //循环执行拦截器的后置处理逻辑,通常用于资源清理、日志记录等操作。
      for (int i = this.interceptorIndex; i >= 0; i--) {
        HandlerInterceptor interceptor = interceptors[i];
        try {
          interceptor.afterCompletion(request, response, this.handler, ex);
        }
        catch (Throwable ex2) {
          logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
        }
      }
    }
  }

mv = ha.handle(processedRequest, Response, mappedHandler.getHandler());

Akzeptiert drei Parameter: „processedRequest“ ist das von der Vorprozessorkette verarbeitete Anforderungsobjekt, „response“ ist das Antwortobjekt und „mappedHandler.getHandler()“ ist das Anforderungshandlerobjekt, dem es zugeordnet ist.

Wenn die Methode handle() ausgeführt wird, wird die entsprechende Verarbeitungslogik entsprechend der Art des Anforderungsprozessors aufgerufen. Verschiedene Anforderungshandler können unterschiedliche Objekttypen sein, z. B. Controller, HttpRequestHandler oder HttpMessageConverter usw.

Hinweis: Im Allgemeinen können HTTP-Anfragen mehrere Parametertypen enthalten. Zu den gängigen Typen gehören die folgenden:

  1. Abfrageparameter (Abfrageparameter): Schlüssel-Wert-Paare befinden sich in der URL und beginnen mit ?. Sie werden in der Form Schlüssel=Wert ausgedrückt und mehrere Parameter werden durch & getrennt. Beispiel: http://hong.com/api?key1=value1&key2=value2. Abfrageparameter können durch Parsen der URL abgerufen werden.

  2. Pfadparameter: Befinden sich im URL-Pfad und werden zur Darstellung von Bezeichnern oder Attributen bestimmter Ressourcen verwendet. Pfadparameter werden häufig beim RESTful-Routing verwendet, wobei Platzhalter zur Darstellung von Parameterwerten verwendet werden. Beispiel: http://hong.com/api/users/{userId}, wobei {userId} der Pfadparameter ist.

  3. Parameter des Anforderungshauptteils: Befinden sich im Anforderungshauptteil und werden normalerweise mithilfe von Formulardaten oder im JSON-Format übergeben. Der Typ der Parameter kann über das Content-Type-Header-Feld der HTTP-Anfrage bestimmt werden. Gängige Parametertypen sind:

    • Formularparameter: Wird im Formulardatenformat übergeben, d. h. in der Form „Schlüssel=Wert“.

    • JSON-Parameter: Die Daten im Anforderungstext werden im JSON-Format übergeben und sind ein zulässiges JSON-Objekt.

    • Dateiparameter: werden zum Hochladen von Dateien verwendet. Der Anforderungstext enthält die Binärdaten der Datei.

Wie Sie Anforderungsparameter bestimmen, hängt vom serverseitigen Framework oder der Programmiersprache ab, die Sie verwenden. Die meisten Frameworks stellen entsprechende Tools oder Bibliotheken zum Parsen und Abrufen von Anforderungsparametern bereit. Im Allgemeinen können Anforderungsparameter erhalten werden, indem die entsprechenden Parameter vom Anforderungsobjekt abgerufen werden. Beispielsweise können Sie im Spring-Framework von Java die Annotation @RequestParam, das HttpServletRequest-Objekt usw. verwenden, um Anforderungsparameter abzurufen.

Dieser Standort ist sehr komplex. Wenn Sie interessiert sind, können Sie tiefer gehen.

Einige Schüler fragen sich vielleicht, welche Parameter SpringMVC unterstützen kann.

  1. Abfrageparameter: Abfrageparameter als Methodenparameter empfangen. Sie können die Annotation @RequestParam verwenden, um Parameter an Abfrageparameter in der Anforderung zu binden, und Sie können auch Attribute wie Standardwerte angeben und angeben, ob diese erforderlich sind.

  2. Pfadparameter: Empfangen Sie Parameter mithilfe von Platzhaltern im Anforderungspfad. Verwenden Sie die Annotation @PathVariable, um Pfadparameter und Methodenparameter zu binden.

  3. Parameter des Anforderungshauptteils: Wird normalerweise zum Empfangen von Daten in POST- oder PUT-Anforderungen verwendet. Sie können die Annotation @RequestBody verwenden, um Daten im Anforderungstext an Methodenparameter zu binden. Zu den unterstützten Datenformaten gehören JSON, XML usw.

  4. Header-Informationen (Request Header): Sie können die Annotation @RequestHeader verwenden, um bestimmte Request-Header-Informationen an Methodenparameter zu binden.

  5. Cookie-Parameter: Verwenden Sie die Annotation @CookieValue, um bestimmte Cookie-Werte an Methodenparameter zu binden.

  6. Formularparameter: Parameter, die zum Empfangen von Formularübermittlungen geeignet sind. Sie können die Annotation @RequestParam oder die Annotation @ModelAttribute verwenden, um Formularfelder an Methodenparameter zu binden.

  7. Datei-Upload: Wenn Sie eine Datei-Upload-Anfrage erhalten, können Sie den Methodenparameter „MultipartFile“ verwenden, um die hochgeladenen Dateidaten zu empfangen.

Das Obige habe ich natürlich aus dem Quellcode gelernt. Gibt es noch etwas, das ich noch nicht gesehen habe? CanHandlerMethodArgumentResolver

6cf5361b22eadd6ab088a4d983c577a0.png

mappedHandler.applyPostHandle(processedRequest, Antwort, mv);

Der obige Absatz stellt eine Implementierung des Interceptors dar. Einige unserer Anfragen oder ungeöffneten Schnittstellenberechtigungen können in Kombination damit abgefangen werden.

Codespeicherort: org.springframework.web.servlet.HandlerExecutionChain#applyPostHandle

void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
      throws Exception {

    HandlerInterceptor[] interceptors = getInterceptors();
    if (!ObjectUtils.isEmpty(interceptors)) {
      for (int i = interceptors.length - 1; i >= 0; i--) {
        HandlerInterceptor interceptor = interceptors[i];
                //调用拦截器
        interceptor.postHandle(request, response, this.handler, mv);
      }
    }
  }

ProcessDispatchResult(processedRequest, Response, MappedHandler, MV, DispatchException);

Dies ist die endgültige Implementierung der Ansicht, es gibt jedoch viele Arten von Ansichten wie folgt:

  1. JSP-Ansicht (InternalResourceView): Verwenden Sie JSP (JavaServer Pages) als Ansichtstechnologie. Über den View Resolver von InternalResourceViewResolver wird der Name der logischen Ansicht der JSP-Datei zugeordnet und die Modelldaten werden zum Rendern an die JSP übergeben.

  2. Thymeleaf-Ansicht (ThymeleafView): Verwendet die Thymeleaf-Vorlagen-Engine für das Rendern von Ansichten. Thymeleaf ist eine moderne Java-Template-Engine, die in HTML, XML, JavaScript und andere Dateien integriert werden kann.

  3. Freemarker-Ansicht (FreeMarkerView): Verwenden Sie die FreeMarker-Vorlagen-Engine für das Rendern von Ansichten. FreeMarker ist eine Vorlagen-Engine, die die endgültige Ausgabe anhand von Vorlagendateien und Datenmodellen generiert.

  4. Velocity View (VelocityView): Verwendet die Apache Velocity-Vorlagen-Engine für das Rendern von Ansichten. Velocity ist eine Java-basierte Template-Engine, mit der Ausgaben in Text, HTML, XML und anderen Formaten generiert werden können.

  5. JSON-Ansicht (MappingJackson2JsonView): Gibt Modelldaten im JSON-Format an den Client zurück. Die Modelldaten werden über die Jackson-Bibliothek in einen JSON-String serialisiert und über HttpServletResponse an den Client zurückgegeben.

  6. XML-Ansicht (MarshallingView): Gibt Modelldaten im XML-Format an den Client zurück. Konvertieren Sie Modelldaten über JAXB (Java Architecture for XML Binding) in XML und geben Sie sie über HttpServletResponse an den Client zurück.

  7. Usw.: Zusätzlich zu den oben genannten Ansichten unterstützt Spring auch benutzerdefinierte Ansichtsauflöser und benutzerdefinierte Ansichtstypen, und andere Ansichtstechnologien können zum Rendern von Ansichten entsprechend den Geschäftsanforderungen verwendet werden.

Codespeicherort: org.springframework.web.servlet.DispatcherServlet#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) {
            //匹配类型为ModelAndViewDefiningException
      if (exception instanceof ModelAndViewDefiningException) {
        logger.debug("ModelAndViewDefiningException encountered", exception);
                //转换成ModelAndViewDefiningException
        mv = ((ModelAndViewDefiningException) exception).getModelAndView();
      }
      else {
                //获取自定义异常
        Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
                //转换
        mv = processHandlerException(request, response, handler, exception);
        errorView = (mv != null);
      }
    }

    // 不为空且 非被清除
    if (mv != null && !mv.wasCleared()) {
            //进行视图创建
      render(mv, request, response);
      if (errorView) {
        WebUtils.clearErrorRequestAttributes(request);
      }
    }
    else { //证明被解析过了
            //打印日志
      if (logger.isDebugEnabled()) {
        logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
            "': assuming HandlerAdapter completed request handling");
      }
    }
      //若请求是异步处理的(Concurrent handling started during a forward),则直接返回,不做后续处理。
    if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
      // Concurrent handling started during a forward
      return;
    }
      //不为空打印日志
    if (mappedHandler != null) {
      mappedHandler.triggerAfterCompletion(request, response, null);
    }
  }

Was folgt, sind einige Ausnahmen und schließlich die Verarbeitung, bei denen es sich um Prozesse zum Löschen des Caches handelt. Schauen Sie nicht genauer hin, aber Sie können es bei Interesse herausfinden.

zu guter Letzt

springmvc ist sehr wichtig, insbesondere der Quellcode, der Logik wie Ansichtsanalyse und Abfangen umfasst. Diese Kerne sind insbesondere für Studenten gedacht, die Spring verbessern möchten. Wenn Sie tiefer gehen oder Architektur betreiben und einige Frameworks integrieren möchten, sind Sie hier genau richtig Sie müssen die Abfolge dieser Prozesse verstehen, sonst erleiden Sie leicht einen großen Verlust. Natürlich ist das oben Gesagte nur meine Meinung.

Referenzartikel:

https://www.yii666.com/blog/452442.html

https://blog.csdn.net/weixin_56644618/article/details/127594065

Supongo que te gusta

Origin blog.csdn.net/qq_16498553/article/details/132659194
Recomendado
Clasificación