cómo funciona Spring Web MVC

Introducción

Esta es una mirada en profundidad a las poderosas características y el funcionamiento interno de Spring Web MVC, que es parte del Framework Spring.

El código fuente de este artículo está disponible en GitHub.

configuración del proyecto

a lo largo de este artículo, usaremos el último y mejor Spring Framework 5., Nos estamos centrando aquí en la pila web clásica de Spring, que ha estado disponible desde las primeras versiones del framework y sigue siendo la forma principal de crear aplicaciones web con Spring.

para empezar, para configurar su proyecto de prueba, usará Spring Boot y algunas de sus dependencias de inicio; también necesitará definir el padre:

tenga en cuenta que, para usar Spring 5, también necesita usar Spring Boot 2.x. en el momento de escribir este artículo, esta es una versión de milestone, disponible en el repositorio de Milestone de Spring., Vamos a añadir este repositorio a tu proyecto Maven:

puedes ver la versión actual de Spring Boot en Maven Central.

proyecto de ejemplo

para comprender cómo funciona Spring Web MVC, implementará una aplicación simple con una página de inicio de sesión. Para mostrar la página de inicio de sesión, cree una clase con anotaciones de @ Controller InternalController con una asignación GET para la raíz de contexto.

el método hello () no tiene parámetros. Devuelve una cadena que es interpretada por Spring MVC como un nombre de vista (en nuestro caso, el login.,plantilla html):

import org.springframework.web.bind.annotation.GetMapping;@GetMapping("/")public String hello() { return "login";}

para procesar un inicio de sesión de usuario, cree otro método que maneje las solicitudes POST con datos de inicio de sesión. Luego redirige al usuario a la página de éxito o error, dependiendo del resultado.,

tenga en cuenta que el método login() recibe un objeto de dominio como argumento y devuelve un objeto ModelAndView:

ModelAndView es un titular de dos objetos distintos:

  • Model – un mapa clave-valor de los datos utilizados para renderizar la página
  • View – una plantilla de la página que se llena con datos del modelo

estos se unen por conveniencia para que el método controlador pueda devolverlos a la vez.

para renderizar su página HTML, use Thymeleaf como un motor de plantillas de vista, que tiene una integración sólida y lista para usar con Spring.,

Servlets como la base de una aplicación Web Java

entonces, ¿qué sucede realmente cuando escribe http://localhost:8080/ en el navegador, presiona Enter y la solicitud llega al servidor web? ¿Cómo puede pasar de esta solicitud a ver un formulario web en el navegador?

dado que el proyecto es una aplicación de arranque de resorte simple, podrá ejecutarlo a través de la aplicación Spring5.

Spring Boot utiliza Apache Tomcat de forma predeterminada., Por lo tanto, al ejecutar la aplicación, es probable que vea la siguiente información en el Registro:

dado que Tomcat es un contenedor de Servlet, naturalmente cada solicitud HTTP enviada a un servidor web Tomcat es procesada por un servlet Java. Así que el punto de entrada de la aplicación web de primavera es, no es sorprendente, un servlet.

un servlet es, en pocas palabras, un componente central de cualquier aplicación Web Java; es de bajo nivel y no impone demasiado en el camino de patrones de programación específicos, como MVC.

un servlet HTTP solo puede recibir una solicitud HTTP, procesarla de alguna manera y enviar una respuesta.,

y, a partir de la API Servlet 3.0, ahora puede ir más allá de la configuración XML y comenzar a aprovechar la configuración Java (con restricciones menores).,

DispatcherServlet como el corazón de Spring MVC

lo que realmente queremos hacer como desarrolladores de una aplicación web es abstraer las siguientes tareas tediosas y repetitivas y enfocarnos en la lógica comercial útil:

  • mapear una solicitud HTTP a un determinado método de procesamiento
  • analizar los datos y encabezados de la solicitud HTTP en objetos de transferencia de datos (DTOs) u objetos de dominio
  • li> generación de respuestas desde dtos, objetos de dominio, etc.

El Spring DispatcherServlet proporciona exactamente eso., Es el corazón del framework Spring Web MVC; este componente central recibe todas las solicitudes a su aplicación.

como verás, DispatcherServlet es muy extensible.,plating engines, XML, XSLT o cualquier otra tecnología de vista (implementaciones de la interfaz ViewResolver)

  • Analizar solicitudes multiparte utilizando el archivo predeterminado de Apache Commons cargando la implementación o escribiendo su propio MultipartResolver
  • resolver la configuración regional con cualquier implementación de LocaleResolver, incluida la cookie, la sesión, el encabezado HTTP Accept o cualquier otra forma de determinar la configuración regional esperada por el Usuario
  • rastree el procesamiento de solicitudes HTTP simples a un método en su capa de controlador y de vuelta al navegador/Cliente.,

    El DispatcherServlet tiene una larga jerarquía de herencia; vale la pena entender estos aspectos individuales uno por uno, de arriba hacia abajo. Los métodos de procesamiento de solicitudes nos interesarán más.

    comprender la solicitud HTTP, tanto localmente durante el desarrollo estándar como de forma remota, es una parte crítica de comprender la arquitectura MVC.

    GenericServlet

    GenericServlet es una parte de la especificación de Servlet que no se centra directamente en HTTP. Define el método service () que recibe solicitudes entrantes y produce respuestas.,

    tenga en cuenta que los argumentos de los métodos ServletRequest y ServletResponse no están vinculados al protocolo HTTP:

    public abstract void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;

    Este es el método que finalmente se llama en cualquier solicitud al servidor, incluida una simple solicitud GET.

    HttpServlet

    la clase HttpServlet es, como su nombre indica, la implementación de servlet centrada en HTTP, también definida por la especificación.,

    en términos más prácticos, HttpServlet es una clase abstracta con una implementación de método service() que divide las solicitudes por el tipo de método HTTP y se ve más o menos así:

    HttpServletBean

    a continuación, HttpServletBean es la primera clase consciente de Spring en la jerarquía. Inyecta las propiedades del bean usando los valores init-param del servlet recibidos de la web.xml o desde WebApplicationInitializer.

    en el caso de las solicitudes a su aplicación, los métodos doGet(), doPost(), etc se llaman para esas solicitudes HTTP específicas.,

    FrameworkServlet

    FrameworkServlet integra la funcionalidad de Servlet con un contexto de aplicación web, implementando la interfaz ApplicationContextAware. Pero también es capaz de crear un contexto de aplicación web por su cuenta.

    como ya ha visto, la superclase HttpServletBean inyecta init-params como propiedades de bean. Por lo tanto, si se proporciona un nombre de clase de contexto en la contextClass init-param del servlet, entonces se creará una instancia de esta clase como un contexto de aplicación. De lo contrario, se utilizará una clase xmlwebapplicationcontext predeterminada.,

    como la configuración XML está fuera de estilo hoy en día, Spring Boot configura DispatcherServlet con AnnotationConfigWebApplicationContext de forma predeterminada. Pero podrías cambiar eso fácilmente.

    por ejemplo, si necesita configurar su aplicación Spring Web MVC con un contexto de aplicación basado en Groovy, puede usar la siguiente configuración de DispatcherServlet en la web.archivo xml:

     dispatcherServlet org.springframework.web.servlet.DispatcherServlet contextClass org.springframework.web.context.support.GroovyWebApplicationContext 

    la misma configuración se puede hacer de una manera más moderna basada en Java utilizando la clase WebApplicationInitializer.,

    DispatcherServlet: unificando el procesamiento de solicitudes

    El HttpServlet.la implementación de service (), que enruta las solicitudes por el tipo de verbo HTTP, tiene perfecto sentido en el contexto de servlets de bajo nivel. Sin embargo, en el nivel de abstracción de Spring MVC, el tipo de método es solo uno de los parámetros que se pueden usar para asignar la solicitud a su controlador.,

    y así, la otra función principal de la clase FrameworkServlet es unir la lógica de manejo de nuevo en un único método processRequest (), que a su vez llama al método doService ():

    @Overrideprotected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response);}@Overrideprotected final void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response);}// …

    DispatcherServlet: enriqueciendo la petición

    finalmente, el DispatcherServlet implementa el método doService (). Aquí, agrega a la solicitud algunos objetos útiles que pueden ser útiles en la canalización de procesamiento: contexto de aplicación web, resolución de configuración regional, resolución de tema, fuente de tema, etc.:

    Además, el método doService() prepara mapas flash de entrada y salida., Mapa de Flash es básicamente un patrón para pasar parámetros de una solicitud a otra solicitud que sigue inmediatamente. Esto puede ser muy útil durante redirecciones (como mostrar al usuario un mensaje de información de un solo disparo después de la redirección):

    entonces, el método doService() llama al método doDispatch() que es responsable del envío de solicitudes.

    DispatcherServlet: Dispatching the Request

    el propósito principal del método dispatch() es encontrar un manejador apropiado para la solicitud y alimentarla con los parámetros de solicitud / respuesta., El manejador es básicamente cualquier tipo de objeto y no se limita a una interfaz específica. Esto también significa que Spring necesita encontrar un adaptador para este controlador que sepa cómo «hablar» con el controlador.

    para encontrar el controlador que coincide con la solicitud, Spring pasa por las implementaciones registradas de la interfaz HandlerMapping. Hay muchas implementaciones diferentes que podrían adaptarse a sus necesidades.

    SimpleUrlHandlerMapping permite asignar una solicitud por su URL a un determinado bean de procesamiento. Por ejemplo, se puede configurar inyectando su propiedad mappings con un java.útil.,Propiedades instancia similar a esta:

    /welcome.html=ticketController/show.html=ticketController

    probablemente la clase más utilizada para la asignación de controladores es RequestMappingHandlerMapping, que asigna una solicitud a un método @RequestMapping con anotaciones de una clase @Controller. Esta es exactamente la asignación que conecta el dispatcher con los métodos hello() y login() de su controlador.

    tenga en cuenta que sus métodos de Spring-aware están anotados con @GetMapping y @PostMapping correspondientemente. Estas anotaciones, a su vez, están marcadas con la meta-Anotación @RequestMapping.,

    el método dispatch() también se encarga de otras tareas específicas de HTTP:

    • procesamiento de cortocircuito de la solicitud GET en caso de que el recurso no se haya modificado
    • aplicar el solucionador multiparte para las solicitudes correspondientes
    • procesamiento de cortocircuito de la solicitud si el controlador optó por manejarla de forma asíncrona

    manejar la solicitud

    ahora que Spring determinó el controlador para la solicitud y el adaptador para el controlador, es hora de manejar finalmente la solicitud. Aquí está la firma del HandlerAdapter.método handle ()., Es importante tener en cuenta que el controlador tiene una opción en cómo manejar la solicitud:

    • Escribir los datos en el objeto response por sí mismo y devolver null

    devolver un objeto ModelAndView para ser renderizado por el DispatcherServlet

    @NullableModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;

    hay varios tipos de controladores proporcionados. Así es como el Simplecontroller Handleradapter procesa una instancia de controlador MVC de Spring (no lo confunda con un POJO con anotaciones de @Controller).,

    observe cómo el controlador devuelve el objeto ModelAndView y no renderiza la vista por sí mismo:

    public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return ((Controller) handler).handleRequest(request, response);}

    El segundo es SimpleServletHandlerAdapter, que adapta un Servlet regular como un controlador de solicitud.

    un Servlet no sabe nada sobre ModelAndView y simplemente maneja la solicitud por sí mismo, renderizando el resultado en el objeto response., Así que este adaptador simplemente devuelve null en lugar de ModelAndView:

    en su caso, un controlador es un POJO con varias anotaciones @RequestMapping, por lo que cualquier controlador es básicamente un método de esta clase envuelto en una instancia de HandlerMethod. Para adaptarse a este tipo de controlador, Spring utiliza la clase RequestMappingHandlerAdapter.,

    argumentos de procesamiento y valores de retorno de métodos de controlador

    tenga en cuenta que los métodos de controlador no suelen tomar argumentos HttpServletRequest y HttpServletResponse, sino que reciben y devuelven muchos tipos diferentes de datos, como objetos de dominio, parámetros de ruta, etc.

    además, tenga en cuenta que no es necesario devolver una instancia de ModelAndView desde un método de controlador. Puede devolver un nombre de vista,o una ResponseEntity o un POJO que se convertirá en una respuesta JSON, etc.,

    El RequestMappingHandlerAdapter se asegura de que los argumentos del método se resuelvan desde el HttpServletRequest. Además, crea el objeto ModelAndView a partir del valor devuelto por el método.

    hay una parte importante de código en RequestMappingHandlerAdapter que asegura que toda esta magia de conversión tenga lugar:

    ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);if (this.argumentResolvers != null) { invocableMethod.setHandlerMethodArgumentResolvers( this.argumentResolvers);}if (this.returnValueHandlers != null) { invocableMethod.setHandlerMethodReturnValueHandlers( this.returnValueHandlers);}

    el objeto argumentResolvers es un compuesto de diferentes instancias de HandlerMethodArgumentResolver.

    hay más de 30 implementaciones de solucionadores de argumentos diferentes., Permiten extraer cualquier tipo de información de la solicitud y proporcionarla como argumentos de método. Esto incluye variables de ruta de URL, parámetros de cuerpo de solicitud,encabezados de solicitud, cookies, datos de sesión, etc.

    el objeto returnValueHandlers es un compuesto de objetos HandlerMethodReturnValueHandler. También hay muchos manejadores de valores diferentes que pueden procesar el resultado de su método para crear el objeto ModelAndView esperado por el adaptador.

    por ejemplo, cuando devuelve una cadena desde el método hello (), el Viewnamethodreturnvaluehandler procesa el valor., Pero cuando devuelves un ModelAndView listo desde el método login (), Spring usa el ModelAndViewMethodReturnValueHandler.

    renderizando la vista

    Por ahora, Spring ha procesado la solicitud HTTP y recibido un objeto ModelAndView, por lo que tiene que renderizar la página HTML que el usuario verá en el navegador. Lo hace basado en el modelo y la vista seleccionada encapsulada en el objeto ModelAndView.

    también tenga en cuenta que puede renderizar un objeto JSON, XML o cualquier otro formato de datos que se pueda transferir a través del protocolo HTTP., Trataremos más sobre eso en la próxima sección centrada en el descanso aquí.

    volvamos al DispatcherServlet. El método render() establece primero la configuración regional de la respuesta usando la instancia LocaleResolver proporcionada. Supongamos que su navegador moderno establece el encabezado Accept correctamente y que el AcceptHeaderLocaleResolver se usa por defecto.

    durante el renderizado, el objeto ModelAndView ya podía contener una referencia a una vista seleccionada, o simplemente un nombre de vista, o nada en absoluto si el controlador dependía de una vista predeterminada.,

    dado que los métodos hello() y login() especifican la vista deseada como un nombre de cadena, debe buscarse con este nombre. Por lo tanto, aquí es donde entra en juego la lista viewResolvers:

    Esta es una lista de instancias de ViewResolver, incluido nuestro ThymeleafViewResolver proporcionado por la biblioteca de integración thymeleaf-spring5. Este solucionador sabe dónde buscar las vistas y proporciona las instancias de vista correspondientes.,

    Después de llamar al método render() de la vista, Spring finalmente completa el procesamiento de la solicitud enviando la página HTML al navegador del usuario:

    soporte REST

    Más allá del típico escenario MVC, también podemos usar el framework para crear servicios Web REST.

    en pocas palabras, puede aceptar un recurso como entrada, especificar un POJO como argumento de método y anotarlo con @RequestBody., También puede anotar el método con @ResponseBody para especificar que su resultado tiene que ser transformado directamente a una respuesta HTTP:

    esto también es posible gracias a la extensibilidad de Spring MVC.

    para ordenar los DTOs internos a una representación REST, el framework hace uso de la infraestructura HttpMessageConverter. Por ejemplo, una de las implementaciones es MappingJackson2HttpMessageConverter, que es capaz de convertir objetos de modelo hacia y desde JSON utilizando la Biblioteca Jackson.,

    y para simplificar aún más la creación de una API REST, Spring introduce la anotación @RestController. Esto es útil para asumir la semántica de @ResponseBody por defecto y evitar establecer explícitamente que en cada controlador REST:

    conclusión

    en este artículo, ha pasado por el procesamiento de una solicitud en el framework MVC de Spring en detalle. Ha visto cómo diferentes extensiones del framework trabajan juntas para proporcionar toda la magia y ahorrarle la necesidad de manejar las partes difíciles del protocolo HTTP.,

    mejore continuamente sus aplicaciones Java mediante el uso de Stackify Retrace, la herramienta de gestión del rendimiento de las aplicaciones durante todo el ciclo de vida. Pruébalo.

    Author: admin

    Deja una respuesta

    Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *