Empezando con Spring MVC

(Fuente: http://blog.springsource.com/2011/01/04/green-beans-getting-started-with-spring-mvc/)

Spring MVC , como parte del core del Spring Framework, es un framework web al estilo de acción-respuesta, con un amplio rango de capacidades y opciones encaminadas a manejar una variedad de casos de uso web enfocados a interfaces gráficos o no gráficos.

Mostramos aquí como con muy poco podemos poner a funcionar una aplicación de Spring MVC.

Spring MVC incluye la mayoría de los conceptos básicos que tienen otros asi llamados MVC frameworks. Las requests entrantes entran al framework via un Front Controller. En el caso de Spring MVC, esto es de hecho una servlet de Java llamado DispatcherServlet. Pensemos en el DispatcherServlet como el guardián de la puerta. Realmente no ejecuta nada de lógica de negocio sino que delega en POJOs llamados Controllers que hacen realmente la tarea (que a su vez pueden invocar otros beans o el back-end). Cuando la tarea ha terminado, es responsabilidad de las Views producir una salida en el formato apropiado (tanto si es una página JSP, como un template de Velocity como una respuesta JSON). Diferentes estrategias se utilizan para decidir qué Controller (y qué método o métodos dentro del Controller) manejarán la request, y que View se utilizará para pintar la respuesta. El Spring container se utiliza para enlazar juntas todas las piezas. Se parece a algo como esto:

Configurando el inicio del DispatcherServlet y del Spring Container

Como mencionamos antes, todas las requests entrantes pasan por el DispatcherServlet. Como cualquier otro Servlet de una aplicación Java EE, le tenemos que decir al Java EE Container que cargue este Servlet en el comienzo de la aplicación web , para ello configuraremos el WEB-INF/web.xml.

El DispatcherServlet es también responsable de cargar un Spring ApplicationContext que será utilizado para realizar enlazado y inyecciones de dependencia de los componentes manejados. Sobre esta base, especificamos algunos parámetros de inicio al servlet los cuales configuran el Application Context. Veamos la configuración dentro del web.xml:

WEB-INF/web.xml:


<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<!-- Processes application requests -->
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>

Comentemos una serie de cosas sobre lo configurado:

  • Registramos el DispatcherServlet como un servlet llamado appServlet
  • Mapeamos el Servlet para manejar requests de entrada (relativas al path de la aplicación) empezando con “/”
  • Utilizamos el parámetro ContextConfigLocation para personalizar el sitio donde tiene que buscar el fichero de configuración XML para que se cargue el Spring Application Context por parte del DispatcherServlet ,en vez de dejar el sitio por defecto que es: <servletname>-context.xml

Pero qué pasa si alguien no quiere configurar el Spring con un archivo XML?El tipo por defecto de Application Context cargado por el DispatcherServlet espera cargar al menos un fichero XML con definiciones de Spring. Como veremos, también habilitaremos Spring para cargar configuraciones hechas desde Java , además de las que ya vienen desde el XML

Todo el mundo tendrá su propia opinión en este punto, pero personalmente prefiero la configuración hecha desde el Java, si bien reconozco lo bueno que es poder cambiar configuraciones sin tener que recompilar.

Decir que quieres una configuración de Spring basada totalmente en Java , sin utilizar archivos XML , es posible hacerlo: hay que poner como parámetro de inicio en el web.xml para sobreescribir el tipo de Application Context y que use una variante llamadaAnnotationConfigWebApplicationContext en su vez.

En este ejemplo vamos a utilizar una forma híbrida.

El controlador (Controller)

Creamos ahora un Controller simple:

package xyz.sample.baremvc;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

/**
* Handles requests for the application home page.
*/

@Controller
public class HomeController {

@RequestMapping(value = "/")
public String home() {
  System.out.println("HomeController: Passing through...");
  return "WEB-INF/views/home.jsp";
}

}

Veamos los aspectos clave de esta clase:

Esta clase ha sido anotada con la anotación @Controller, indicando que este es un Spring MVC Controller capaz de manejar peticiones web. Debido a que @Controller es una especialización de la anotación de estereotipo @Component , esta clase será automáticamente detectada por el contenedor de Spring como parte del proceso de escaneado de componentes del contenedor, creando una definición de bean y permitiendo a la instancias a ser inyectadas con dependencia como si el bean hubiera sido definido en el XML de Spring.

El método home ha sido anotado con @RequestMapping , especificando que este método debe manejar peticiones web  en el path “/” , es decir el path raíz de la aplicación.

Este método simplemente escribe una mensaje en el system out, y entonces retorna WEB-INF/views/home.jsp , indicando la vista que debe manejar la respuesta, en este caso una página JSP (veremos después como no tenemos porque hacer hardcoding de esta url).

Ahora necesitamos crear la vista. Esta página JSP simplemente imprimirá un saludo:

WEB-INF/views/home.jsp

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" %>
<html>
<head>
<title>Home</title>
</head>
<body>
<h1>Hello world!</h1>
</body>
</html>

Finalmente , como dijimos antes, necesitamos crear un fichero de definición de Spring Application Context mínimo:

WEB-INF/spring/appServlet/servlet-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"

xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd

http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd

http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

<!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->
<!-- Scans within the base package of the application for @Components to configure as beans -->
<!-- @Controller, @Service, @Configuration, etc. -->

<context:component-scan base-package="xyz.sample.baremvc" />
<!-- Enables the Spring MVC @Controller programming model -->

<mvc:annotation-driven />

</beans>

Examinemos los contenidos de este fichero:

Observamos que se utilizan unos cuantos Spring XML namespaces: context, mvc y el default beans.

Depués la declaración <context:component-scan> asegura que el contenedor Spring hará un escaneado , de forma que cualquier clase anotada con @Component o sus subtipos (tal como @Controller) es automáticamente descubierta. Notarás que por eficiencia, nos limitamos al paquete de nuestras clases.

La declaración <mvc:annotation-driven> configura un apoyo para Spring MVC de modo que enruta las requests a los @Controllers, asi como se manejan la conversión, formateado y validación.

LA web app está ahora lista para ejecutar. Asumiendo que el Servlet Container (el Server en nuestro caso) esté enganchado en localhost:8080, podemos iniciar la aplicación poniendo la URL http://localhost:8080/EjemploSimpleSpringMVC para que salga algo como esto:

Tan trivial como es, esta aplicación incluye todas las piezas principales de una aplicación Spring MVC. Veamos las consecuencias más importantes e interacciones entre los componentes:

Cuando la aplicación web comienza, el DispatcherServlet se carga e inicializa por la entrada que hay dentro del web.xml.

El DispatcherServlet carga un Application Context basado en anotaciones, que ha sido configurado para escanear para componentes anotados a través de la expresión regular que especifica el paquete base.

Los componentes anotados tales como el HomeController son detectados por el contenedor.

Cuando ponemos http://localhost:8080/EjemploSimpleSpringMVC en el navegador esto va al servlet engine y es enrutado a la aplicación web.

El path “/” implícito al final de la URL engancha con la regex que ha sido registrada por el DispatcherServlet, y la petición es enrutada a ella.

El DispatcherServlet necesita decidir qué hacer con la request. Utiliza una estrategia llamada una HandlerAdapter para decidir donde enrutar la request. El tipo de HandlerAdapter especifico (o tipos , ya que pueden estar encadenados) a ser utilizados pueden ser customizados , pero por defecto, una estrategia basada en anotaciones se usa, lo cual enruta la request apropiadamente a métodos específicos en clases anotadas como @Controller, basándose en el criterio de matching en las anotaciones @RequestMapping encontradas en esas clases. En este caso, la regex del método home es enganchado, y por tanto es el método invocado para manejar la request.

El método home hace el trabajo , en este caso solo saca un mensaje. Entonces retorna una string que en este caso, es un jsp explicito , pero que puede ser de otra forma y es lo que hace que la response sepa que View elegir.

El DispatcherServlet otra vez se apoya en la estrategia, llamando a un ViewResolver para decidir que View es responsable de renderizar la respuesta. Esto puede ser configurado como se necesite para la aplicación (de una forma simple o al estilo cadena), pero por defecto, se utiliza InternalResourceViewResolver . Este es un view resolver que produce una JstlView que simplemente delega al RequestDispatcher del motor de Servlet ,y por tanto se puede trabajar con páginas JSP y con paginas estáticas HTML.

Por último el Servlet engine renderiza la response via el JSP especificado.