Explicación detallada de Spring Web MVC DispatcherServlet: original oficial

I. Resumen

 Spring Web MVC es el marco web original construido sobre la API Servlet, incluido en el marco Spring desde el principio. El nombre oficial "SpringWebMVC" proviene del nombre de su módulo fuente (spring-webmvc), pero su nombre más común es "SpringMVC".

Paralelamente a Spring Web MVC, Spring Framework 5.0 presenta un marco web de pila reactiva cuyo nombre "Spring WebFlux" también se basa en su módulo fuente (Spring-WebFlux).

Dos, DispatcherServlet

Spring MVC, como muchos otros marcos web, está diseñado en torno al patrón Front Controller, donde uno central  ServletDispatcherServlet , proporciona un algoritmo compartido para el procesamiento de solicitudes, mientras que el trabajo real lo realizan componentes delegados configurables. Este modo es flexible y admite una variedad de flujos de trabajo.
DispatcherServlet, como cualquier Servlet, debe declararse y asignarse de acuerdo con la especificación de Servlet utilizando la configuración de Java o web.xml. A su vez, DispatcherServlet usa la configuración de Spring para descubrir los componentes de delegado necesarios para el mapeo de solicitudes, la resolución de vistas, el manejo de excepciones, etc.
El siguiente ejemplo de configuración de Java registra e inicializa el DispatcherServlet, que es detectado automáticamente por el contenedor de Servlet

public class MyWebApplicationInitializer implements WebApplicationInitializer {

    @Override
    public void onStartup(ServletContext servletContext) {

        // Load Spring web application configuration
        AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
        context.register(AppConfig.class);

        // Create and register the DispatcherServlet
        DispatcherServlet servlet = new DispatcherServlet(context);
        ServletRegistration.Dynamic registration = servletContext.addServlet("app", servlet);
        registration.setLoadOnStartup(1);
        registration.addMapping("/app/*");
    }
}

1. Jerarquía de contexto

DispatcherServlet Se espera que tenga una  WebApplicationContextApplicationContext extensión simple) para su propia configuración. WebApplicationContext Hay un enlace  ServletContext ay relacionado con él  Servlet . También está vinculado para  ServletContextque las aplicaciones puedan usar  RequestContextUtils los métodos estáticos para consultar  WebApplicationContextsi necesitan acceder a él.

Para muchas aplicaciones, tener uno solo  WebApplicationContext es simple y suficiente. También es posible tener una jerarquía de contexto en la que  varias (u otras  ) instancias WebApplicationContext comparten  una sola raíz , cada una con su propia   subconfiguración.DispatcherServletServletWebApplicationContext

La raíz  WebApplicationContext generalmente contiene beans de infraestructura, como  Servlet repositorios de datos y servicios comerciales que deben compartirse entre varias instancias. Estos beans se heredan de manera efectiva y   se pueden anular (es decir, volver a declarar) en  Servlet un hijo específico  , que  generalmente contiene un   bean local determinado. La siguiente imagen muestra esta relación:WebApplicationContextWebApplicationContextServlet

 El siguiente ejemplo configura una  WebApplicationContext jerarquía:

public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class<?>[] { RootConfig.class };
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class<?>[] { App1Config.class };
    }

    @Override
    protected String[] getServletMappings() {
        return new String[] { "/app1/*" };
    }
}

El siguiente ejemplo muestra el equivalente web.xml:

<web-app>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/root-context.xml</param-value>
    </context-param>

    <servlet>
        <servlet-name>app1</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/app1-context.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>app1</servlet-name>
        <url-pattern>/app1/*</url-pattern>
    </servlet-mapping>

</web-app>

2. Tipos especiales de frijoles

DispatcherServlet Delega a un bean especial para manejar la solicitud y dar la respuesta adecuada. Por "frijoles especiales" nos referimos  Object a instancias administradas por Spring que implementan contratos marco. Estas instancias suelen tener convenciones integradas, pero puede personalizar sus propiedades y ampliarlas o reemplazarlas.

La siguiente tabla enumera  DispatcherServlet las clases especiales de beans detectadas por:

tipo de frijol ilustrar

HandlerMapping

Asigna una solicitud a un controlador junto con una lista de interceptores para el procesamiento previo y posterior. La asignación se basa en algún estándar, cuyos detalles  HandlerMapping varían según la implementación.

Las dos implementaciones principales  HandlerMappin son  RequestMappingHandlerMapping@RequestMapping métodos que admiten anotaciones) y  SimpleUrlHandlerMapping(mantener el registro explícito de patrones de ruta de URI para los controladores).

HandlerAdapter

Ayuda  DispatcherServlet a invocar controladores asignados a solicitudes, independientemente de cómo se invoque realmente el controlador. Por ejemplo, llamar a un controlador anotado requiere analizar la anotación. HandlerAdapter El propósito principal de DispatcherServlet es enmascarar estos detalles.

VerResolver

Una estrategia para resolver excepciones, posiblemente asignándolas a controladores, vistas de errores HTML u otros objetivos.

VerResolver

Resuelve el nombre de la vista lógica en función de lo devuelto por el controlador  String a la  View(vista) real y lo representa en la respuesta.

LocaleResolver, LocaleContextResolver

Analice la  Localezona horaria del cliente, y posiblemente su zona horaria, para poder proporcionar vistas internacionalizadas

Resolución de temas

Resuelve temas que su aplicación web puede usar, por ejemplo, para proporcionar un diseño personalizado.

MultipartResolver

Una abstracción para analizar una solicitud de varias partes (por ejemplo, la carga de un archivo de formulario del navegador) con la ayuda de alguna biblioteca de análisis de varias partes.

 FlashMapManager

Almacene y recupere "entrada" y "salida"  FlashMap, que se pueden usar para pasar atributos de una solicitud a otra, a menudo a través de redireccionamientos.

3. Configuración MVC de la página web

Las aplicaciones pueden declarar los beans de infraestructura necesarios para procesar las solicitudes enumeradas en los tipos de beans especiales. DispatcherServlet comprueba cada bean especial en WebApplicationContext. Si no hay ningún tipo de bean coincidente, se recurrirá al tipo predeterminado que se muestra en DispatcherServlet.properties. En la mayoría de los casos, la configuración de MVC es el mejor lugar para comenzar. Declara el Bean requerido en Java o XML, y proporciona una API de devolución de llamada (devolución de llamada) de configuración de nivel superior para personalizarlo. 

4. Configuración de servlets

En un entorno de Servlet, puede optar por configurar el contenedor de Servlet mediante programación, como una opción o junto  web.xml con la documentación. El siguiente ejemplo registra uno  DispatcherServlet:

import org.springframework.web.WebApplicationInitializer;

public class MyWebApplicationInitializer implements WebApplicationInitializer {

    @Override
    public void onStartup(ServletContext container) {
        XmlWebApplicationContext appContext = new XmlWebApplicationContext();
        appContext.setConfigLocation("/WEB-INF/spring/dispatcher-config.xml");

        ServletRegistration.Dynamic registration = container.addServlet("dispatcher", new DispatcherServlet(appContext));
        registration.setLoadOnStartup(1);
        registration.addMapping("/");
    }
}

WebApplicationInitializer es una interfaz proporcionada por Spring MVC que asegura que su implementación sea detectada y utilizada automáticamente para inicializar cualquier contenedor Servlet 3. WebApplicationInitializer Una implementación de clase base abstracta llamada  , que  facilita el registro AbstractDispatcherServletInitializeral anular métodos para especificar  Servlet asignaciones y  DispatcherServlet ubicaciones de configuración  .DispatcherServlet

Esto se recomienda para aplicaciones que utilizan una configuración de Spring basada en Java, como se muestra en el siguiente ejemplo:

public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return null;
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class<?>[] { MyWebConfig.class };
    }

    @Override
    protected String[] getServletMappings() {
        return new String[] { "/" };
    }
}

Si usa una configuración de Spring basada en XML, debe comenzar directamente desde  AbstractDispatcherServletInitializer la extensión, como se muestra en el siguiente ejemplo:

public class MyWebAppInitializer extends AbstractDispatcherServletInitializer {

    @Override
    protected WebApplicationContext createRootApplicationContext() {
        return null;
    }

    @Override
    protected WebApplicationContext createServletApplicationContext() {
        XmlWebApplicationContext cxt = new XmlWebApplicationContext();
        cxt.setConfigLocation("/WEB-INF/spring/dispatcher-config.xml");
        return cxt;
    }

    @Override
    protected String[] getServletMappings() {
        return new String[] { "/" };
    }
}

AbstractDispatcherServletInitializer También proporciona un método conveniente para agregar  Filter instancias y hacer que se asignen automáticamente a  DispatcherServlet, como muestra el siguiente ejemplo:

public class MyWebAppInitializer extends AbstractDispatcherServletInitializer {

    // ...

    @Override
    protected Filter[] getServletFilters() {
        return new Filter[] {
            new HiddenHttpMethodFilter(), new CharacterEncodingFilter() };
    }
}

Cada filtro se agrega con un nombre predeterminado (nombre) basado en su tipo concreto y se asigna automáticamente a  DispatcherServlet.

AbstractDispatcherServletInitializer El  isAsyncSupported método protegido proporciona un lugar único para habilitar  DispatcherServlet la compatibilidad asincrónica y todos los filtros asignados a él. De forma predeterminada, esta bandera se establece en  true.

DispatcherServlet Finalmente, puede anular  createDispatcherServlet los métodos si necesita personalizarse aún más  .

5. Proceso

DispatcherServlet La solicitud se maneja de la siguiente manera:

WebApplicationContext Los beans declarados en  se  HandlerExceptionResolver utilizan para resolver las excepciones lanzadas durante el procesamiento de solicitudes. Estos solucionadores de excepciones permiten una lógica de manejo de excepciones personalizada

WebRequest Para la compatibilidad con el almacenamiento en caché de HTTP, los métodos están disponibles  para los controladores  checkNotModified . Puede  personalizar  instancias  individuales  web.xml agregando parámetros de inicialización de Servlet (elementos) a la declaración de Servlet en el archivo . La siguiente tabla enumera los parámetros admitidos:init-paramDispatcherServlet

  • WebApplicationContext Se busca y vincula como un atributo en la solicitud, disponible para los controladores y otros elementos en el proceso. Está enlazado bajo  DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE clave por defecto.

  • Un solucionador de configuración regional está vinculado a la solicitud para que los elementos del proceso resuelvan la configuración regional que se usará al procesar la solicitud (presentación de vistas, preparación de datos, etc.). Si no necesita un análisis de configuración regional, no necesita una resolución de configuración regional.

  • Un solucionador de temas está vinculado a la solicitud para permitir que elementos como las vistas decidan qué tema usar. Si no usa un tema, puede ignorarlo.

  • Si especifica un analizador de archivos de varias partes, la solicitud se comprobará como un archivo de varias partes. Si se encuentra una multiparte, la solicitud se envolverá en una  MultipartHttpServletRequest para que otros elementos del proceso la procesen más.

  • Se busca un controlador adecuado. Si se encuentra un controlador, la cadena de ejecución (preprocesador, posprocesador y controlador) asociada con ese controlador se ejecuta para preparar el modelo para la representación. Además, para los controladores anotados, la respuesta se puede representar (dentro  HandlerAdapter ) en lugar de devolver una vista.

  • Si se devuelve un modelo, se renderizará la vista. Si no se devuelve ningún modelo (quizás debido a que un preprocesador o posprocesador interceptó la solicitud, quizás por razones de seguridad), la vista no se representará porque es posible que la solicitud ya se haya cumplido.

Tabla 1. Parámetros de inicialización de DispatcherServlet
parámetro ilustrar

contextClass

Implementar  ConfigurableWebApplicationContext clases que serán instanciadas y configuradas localmente por este Servlet. Por defecto, usa  XmlWebApplicationContext.

contextConfigLocation

Una cadena pasada a la instancia de contexto (  contextClass especificada por) que indica dónde se puede encontrar el contexto. Esta cadena puede constar de varias cadenas (usando comas como separadores) para admitir varios contextos. Si un bean en varias ubicaciones de contexto se define dos veces, la ubicación más nueva tiene prioridad.

namespace

WebApplicationContext espacio de nombres El valor predeterminado es  [servlet-name]-servlet.

throwExceptionIfNoHandlerFound

Si lanzar cuando no se encuentra un controlador para una solicitud  NoHandlerFoundException. Luego, la excepción se puede capturar  HandlerExceptionResolver(por ejemplo, mediante el uso  @ExceptionHandler de un método de controlador) y manejarse como cualquier otro.

De forma predeterminada, se establece en  false; en este caso, DispatcherServlet establezca el estado de respuesta en 404 ( NOT_FOUND) sin generar una excepción.

Tenga en cuenta que si también se configura el manejo de servlet predeterminado, las solicitudes no resueltas siempre se reenvían al servlet predeterminado y nunca se producirán errores 404.

 6. Coincidencia de ruta

La API de servlet expone la ruta de solicitud completa como  requestURI , y la divide en  contextPath, servletPath y  pathInfo, cuyos valores varían según cómo se mapee el servlet. A partir de estas entradas, Spring MVC necesita determinar la ruta de búsqueda que se usará para el controlador asignado, excluyendo  contextPath los  servletMapping prefijos, si corresponde.

servletPath y  pathInfo están decodificados, lo que hace imposible compararlos  requestURI directamente  con los completos lookupPath, lo que hace necesario  requestURI decodificar los . Sin embargo, esto presenta sus propios problemas, ya que la ruta puede contener caracteres reservados codificados como  "/" o  ";", que a su vez cambian la estructura de la ruta después de decodificarse, lo que también puede generar problemas de seguridad. servletPath Además, los contenedores de Servlet pueden normalizarse  en diferentes grados  , lo que hace que sea aún más imposible requestURI comparar  startsWith .

Esta es la razón por la que es mejor evitar confiar en el servletPath que viene con el tipo de mapeo servletPath basado en prefijos. Si DispatcherServlet se asigna como el Servlet predeterminado con el prefijo "/" o sin "/*", y el contenedor de Servlet está por encima de 4.0, Spring MVC puede detectar el tipo de asignación de Servlet y evitar por completo el uso de servletPath y pathInfo. En el contenedor de Servlet 3.1, asumiendo el mismo tipo de mapeo de Servlet, se puede realizar proporcionando UrlPathHelper con alwaysUseFullPath=true a través de la coincidencia de ruta (Path) en la configuración de MVC.

Afortunadamente, el mapeo de Servlet predeterminado  "/" es una buena opción. Sin embargo, todavía hay un problema que  requestURI debe decodificarse para poder comparar con el mapeo del controlador. Esto tampoco es deseable, ya que es posible decodificar caracteres reservados que cambian la estructura de la ruta. Si no se esperan estos caracteres, puede negarlos (como Spring Security HTTP Firewall), o puede configurarlos  UrlPathHelper ,  urlDecode=falsepero la asignación del controlador debe coincidir con la ruta codificada, lo que puede no ser siempre agradable. Además, a veces  DispatcherServlet es necesario compartir el espacio de la URL con otro servlet, lo que puede requerir un mapeo por prefijo.

El problema anterior se resuelve al usar  PathPatternParser y analizar el patrón, ya que se puede  AntPathMatcher comparar con una ruta de cadena alternativa. PathPatternParser Disponible en Spring MVC desde la versión 5.3 y habilitado de forma predeterminada desde la versión 6.0. AntPathMatcher En lugar de decodificar la ruta de búsqueda o codificar el mapa del controlador, el analizado se   compara con la representación analizada de la ruta llamada, un segmento de ruta a la vez PathPattern . RequestPathEsto permite que los valores de los segmentos de la ruta se decodifiquen y desinfecten individualmente sin el riesgo de alterar la estructura de la ruta. Parsed  PathPattern también admite el uso de  servletPath asignaciones de prefijos, siempre que se utilicen asignaciones de rutas de Servlet y los prefijos se mantengan simples, es decir, sin caracteres codificados.

7. Interceptación

Todas las implementaciones de HandlerMapping admiten interceptores de controladores, que son útiles cuando desea aplicar una funcionalidad específica a ciertas solicitudes, por ejemplo, verificar un principal. El interceptor debe implementar el HandlerInterceptor en el paquete org.springframework.web.servlet, que tiene tres métodos que deberían proporcionar suficiente flexibilidad para varios preprocesamientos y posprocesamientos:

  • preHandle(..): antes de que se ejecute el controlador real
  • postHandle (..): después de que se ejecuta el controlador
  • afterCompletion(..): después de completar toda la solicitud

El método preHandle(..) devuelve un valor booleano. Puede utilizar este método para interrumpir o continuar el procesamiento de la cadena de ejecución. Cuando este método devuelve verdadero, la cadena de ejecución del controlador continúa. Cuando devuelve falso, DispatcherServlet considera que el propio interceptor ha procesado la solicitud (por ejemplo, presentando una vista adecuada) y no procede a ejecutar otros interceptores y controladores reales en la cadena de ejecución.

Para ver un ejemplo de cómo configurar interceptores, consulte Interceptores en la sección de configuración de MVC. También puede registrar implementaciones individuales de HandlerMapping directamente utilizando sus configuradores.

El método postHandle es menos útil en los métodos @ResponseBody y ResponseEntity porque las respuestas para esos métodos se escriben y confirman en el HandlerAdapter antes de postHandle. Esto significa que es demasiado tarde para realizar modificaciones en la respuesta, como agregar un encabezado adicional. Para este caso, puede implementar ResponseBodyAdvice y declararlo como Controller Advice Bean, o configurarlo directamente en RequestMappingHandlerAdapter

8. Excepciones

Si se produce una excepción durante la asignación de solicitudes o  @Controllerse lanza desde un controlador de solicitudes (p. ej.),  DispatcherServlet el controlador se delega a la HandlerExceptionResolvercadena de beans ExceptionResolver ( ) para resolver la excepción y proporcionar un manejo alternativo, que suele ser una respuesta de error.

La siguiente tabla enumera las  HandlerExceptionResolver implementaciones disponibles:

HandlerExceptionResolver ilustrar

SimpleMappingExceptionResolver

Mapeo entre nombres de clase de excepción y nombres de vista de error. Útil para representar páginas de error en aplicaciones de navegador.

​DefaultHandlerExceptionResolver

Analiza las excepciones lanzadas por Spring MVC y las asigna a códigos de estado HTTP. Consulte también  ResponseEntityExceptionHandler Respuestas alternativas y de error.

ResponseStatusExceptionResolver

Analiza  @ResponseStatus las excepciones anotadas y las asigna a códigos de estado HTTP en función del valor de la anotación.

ExceptionHandlerExceptionResolver

 Resolver excepciones llamando  @Controller o  métodos @ControllerAdvice en la clase @ExceptionHandler

cadena de resolución

Puede formar una cadena de solucionadores de excepciones declarando varios beans HandlerExceptionResolver en su configuración de Spring y configurando sus propiedades de orden según sea necesario. Cuanto mayor sea el atributo de orden, más tarde se posicionará el analizador de excepciones. La convención de HandlerExceptionResolver, que puede devolver: Un ModelAndView que apunta a la vista de error. Un ModelAndView vacío si la excepción se manejó en el analizador. null si la excepción aún no se ha resuelto, para los intentos posteriores del analizador, para permitir la propagación en el contenedor de servlet si la excepción aún está al final. La configuración de MVC declara automáticamente los solucionadores integrados para las excepciones predeterminadas de Spring MVC, las excepciones anotadas con @ResponseStatus y la compatibilidad con los métodos @ExceptionHandler. Puede personalizar esta lista o reemplazarla 

Página de error del contenedor (Página de error)

Si aún no se  HandlerExceptionResolver analiza una excepción y, por lo tanto, se permite que se propague, o si el estado de respuesta se establece en un estado de error (es decir, 4xx, 5xx), el contenedor de servlet puede generar una página de error predeterminada en HTML. Para personalizar las páginas de error predeterminadas del contenedor, puede  web.xml declarar una asignación de página de error en . El siguiente ejemplo muestra cómo hacer esto:

<error-page>
    <location>/error</location>
</error-page>

/errorDado el ejemplo anterior, cuando se genera una excepción o la respuesta tiene un estado de error, el contenedor de servlet envía ERROR a la URL configurada (p. ej., ) dentro del contenedor . Luego se  DispatcherServlet procesa, posiblemente asignándolo a un  @Controller, que se puede implementar para devolver un nombre de vista de error con un modelo o para generar una respuesta JSON, como en el siguiente ejemplo:

@RestController
public class ErrorController {

    @RequestMapping(path = "/error")
    public Map<String, Object> handle(HttpServletRequest request) {
        Map<String, Object> map = new HashMap<>();
        map.put("status", request.getAttribute("jakarta.servlet.error.status_code"));
        map.put("reason", request.getAttribute("jakarta.servlet.error.message"));
        return map;
    }
}

9. Ver análisis

Spring MVC define  ViewResolver interfaces  View que le permiten renderizar modelos en el navegador sin estar vinculado a una tecnología de vista específica. ViewResolver Proporciona una asignación entre los nombres de las vistas y las vistas reales. View Aborda los problemas de preparación de datos antes de la transferencia a tecnologías de visualización específicas.

La siguiente tabla proporciona  ViewResolver más detalles sobre la jerarquía:

Tabla 3. Implementaciones de ViewResolver
VerResolver ilustrar

AbstractCachingViewResolver

AbstractCachingViewResolver Las subclases de almacenarán en caché las instancias de vista que resuelven. El almacenamiento en caché puede mejorar el rendimiento de algunas tecnologías de visualización. Puede desactivar el almacenamiento en caché configurando  cache la propiedad en  false . Además, si tiene que actualizar una determinada vista en tiempo de ejecución (por ejemplo, cuando  FreeMarker se modifica la plantilla), puede usar  removeFromCache(String viewName, Locale loc) el método.

UrlBasedViewResolver

ViewResolver La implementación simple de la interfaz puede realizar el análisis directo de nombres de vistas lógicas y direcciones URL sin definiciones de asignación explícitas. Esto está bien si sus nombres lógicos coinciden directamente con los nombres de sus recursos de vista sin requerir asignaciones arbitrarias.

InternalResourceViewResolver

UrlBasedViewResolver Subclases de conveniencia de , soporte  InternalResourceView(de hecho, Servlets y JSP) y subclases como  JstlView. setViewClass(..) Puede especificar la clase de vista para todas las vistas generadas por este analizador mediante  .

FreeMarkerViewResolver

UrlBasedViewResolver Subclases de conveniencia de , support  FreeMarkerView y sus subclases personalizadas.

ContentNegotiatingViewResolver

ViewResolver La implementación de la interfaz para resolver la vista de acuerdo con el nombre o  Accept encabezado del archivo de solicitud.

BeanNameViewResolver

ViewResolver 接口的实现,它将视图名称解释为当前应用程序上下文中的bean名称。这是一个非常灵活的变体,可以根据不同的视图名称混合和匹配不同的视图类型。每个这样的 View 都可以被定义为Bean,例如在XML或配置类中。

Supongo que te gusta

Origin blog.csdn.net/leesinbad/article/details/130030839
Recomendado
Clasificación