RSS

Ejemplo básico de Spring MVC con Maven

10 Sep

springsourcelogo

En el siguiente post vamos a hablar un poco de Spring MVC. Pero antes comentar que tengo pensado hacer una serie de post (el primero es este) sobre Spring. En concreto intentaré hablar un poco de proyecto de SpringSource como Spring MVC, Spring Web Flow, Spring Security (O Auth) ó Spring Faces. Además voy a hablar un poco de desarrollo e integración continua y pruebas (Hudson / Jenkins, Sonar, Cobertura, Selenium, Checkstyles, PMD, …). También me gustaría tratar temas de Cloud Computing , siempre como absoluto novato en la materia, analizando herramientas como Google App Engine (GAE) ó Micro Cloud Foundry. Pero para ello todavía queda …. así que empezaré con Spring MVC.

Introducción a Spring MVC.

Spring MVC es un proyecto de Springsource que nos permite utilizar el patrón MVC de una forma sencilla dentro de nuestras aplicación desarrolladas con Spring. En la siguiente imagen vemos el esquema de como funciona Spring MVC.

SpringMVCLifeCycle

Si hemos realizado previamente desarrollo web podemos identificar claramente que en Spring MVC, nuestro DispatcherServlet funciona bajo el patrón front controller. El patrón front controller nos da un punto de entrada único a nuestra peticiones. De manera que todas ellas van a pasar por un mismo Servlet, en el caso de Spring MVC, se trata de DispatcherServlet. Este servlet se va a encargar de gestionar toda la lógica en nuestra aplicación.

El flujo básico en una aplicación bajo Spring MVC es el siguiente:

  • La petición llega a nuestro DispatcherServlet (1)
  • El DispatcherServlet tendrá que encontrar que controlador va a tratar la petición. Para ello el DispatcherServlet tiene que encontrar el manejador asociado a la url de la petición. Todo ésto se realiza en la fase de HandlerMapping (2).
  • Una vez encontrado ese Controller, nuestro DispatcherServlet le dejará gestionar a éste la petición (3). En nuestro controlador se deberá realizar todo la lógica de negocio de nuestra aplicación, es decir, aquí llamaremos a nuestra capa de servicios. El controlador devolverá al Dispatcher un objeto de tipo ModelAndView (4). ¿Qué quiere decir ésto?. En pocas palabras Model serán los valores que obtengamos de nuestra capa de servicio y View será el nombre de la vista en la que queremos mostrar la información que va contenida dentro de ese Model.
  • Una vez pasado este objeto ModelAndView al DispatcherServlet, será éste el que tendrá que asociar el nombre de la vista retornada por el controlador a una vista concreta (página jsp, jsf, …). Este proceso viene indicado en la imagen como ViewResolver (4).
  • Finalmente y una vez resuelta la vista, nuestro DispatcherServlet tendrá que pasar nuestro Model (los valores recogidos en el controlador a través de nuestra capa de servicios) a la vista concreta View (5).

En los pasos de HandlerMapping, selección de Controller y ViewResolver podemos indicarle a nuestro DispatcherServlet qué estrategia queremos seguir. Os dejo una entrada del blog de Alex Fuentes donde explica de manera bastante concisa los tipos de estrategias disponibles.

Proyecto de Spring MVC con Maven

¿Por qúe utilizar GitHub?.

Bueno, el punto anterior era teoría, vamos a ver un proyecto donde explicar las clases Java y los ficheros de configuración básico para empezar con Spring MVC. Como siempre los proyectos estarán gestionados con Maven (2.2.1). Como novedad voy a subir el código a GitHub, así quien quiera probar los ejemplos simplemente tendrá que acceder mi repositorio público en la siguiente dirección:

git@github.com:IvanFernandez/hop2croftRepository.git

Si no conoces todavía Git, puedes empezar echando un vistazo a los siguientes post:

Las razones por la que subo el código a GitHub son varias. Por un lado creo que facilita seguir el post tener todo el código disponible y poder probarlo por uno mismo. Además, y personalmente no me gusta nada, cuando estoy leyendo un post y veo que por ejemplo no están incluidos los imports en el código ó poner todo el código de un método . Subiendo el código a GitHub se puede escribir un post sin necesidad de poner los imports ó sólo parte de una clase (y no hacer un post kilométrico), pero a la vez la persona que lo está leyendo no se pierde. Finalmente y se alguien se anima con GitHub se puede hacer un fork de los distintos proyectos, añadir funcionalidad y que esté a disposición de todos.

Explicación del código

Yendo ya al proyecto concreto y para hacer más claro una imagen general del proyecto creo que es bueno poner una imagen con la estructura del proyecto vista desde Eclipse.
PackageExplorerSpringMVC
Como podemos ver en la anterior imagen al estar creado nuestro proyecto con Maven se ha creado la estructura típica de un proyecto Maven, es decir:

Carpeta src/main/java

Aquí vamos a guardar nuestras clases Java. En concreto tenemos cuatro paquetes:

  • Controller. estará asociado a nuestras controladores. En nuestro caso vamos a tener dos controladores. CarController que se ejecutará cuando se solicite la página car.html y CarFormController que se ejecutará cuando se solicite carForm.html
  • Domain. nuestras clases de dominio. Vamos a utilizar un clase que modela un coche, Car.java. Ya hemos visto en otros post los servicios para crear, modificar y eliminar un coche (y por ende cualquier clase) en una base de datos.
  • Exception. en este paquete estarán las clases que tratarán las posibles excepciones que se generen por nuestro codigo.
  • Interceptor. en este paquete tenemos los interceptores de Spring MVC. Los interceptores sirven en Spring para tratar las peticiones y añadirles cierta lógica.
  • Services. las clases que llaman a nuestro servicio. En lugar de utilizar los servicios implementados en otros post, por sencillez y sobre todo para no sobrecargar los ficheros de configuración vamos a hacer que nuestros servicios no vayan contra la base de datos. Así el servicio CarService va a devolver una lista fija indicada en la propia clase.

Carpeta src/main/resources

En este paquete vamos a guardar los ficheros necesarios en nuestro proyecto.

  • Locale. Nuestra aplicación va a escribir sus páginas jsp en el idioma que por defecto que venga indicado en el navegador usado. Para ello habrá una serie de ficheros de propiedades nombrados como message*.properties. En estos ficheros estarán declaradas una serie de cadenas de texto y su valor. Cuando la página se pinte hará uso de estos ficheros de propiedades para escribir el texto.
  • Spring. En esta carpeta tendremos nuestro fichero de contexto de la aplicación, applicationContext.xml. En este fichero simplemente vamos a cargar en nuestro contexto la carpeta locale que acabamos de explicar.
  • view_es.properties. Este fichero todavía no lo vamos a utilizar. La idea es utilizar este fichero para hacer redirecciones en nuestro navegadores.

Carpeta src/main/test

De momento sólo he creado una clase de test. Esta clase de test está realizada usando el framework Selenium. Este test nos permite hacer pruebas de regresión, es decir, nuestra clase de test abrirá nuestro navegador y ejecutará una secuencia de acciones (rellenar una caja de texto, pulsar un botón, irnos de una página a otra, …). El siguiente post que escribiré será una pequeña explicación de como empezar con Selenium.

Carpeta src/main/webapp/WEB-INF

Tenemos una serie de carpetas y ficheros de configuración que vamos a explicar a continuación:

  • carpeta jsp. Aquí vamos a guardar nuestras páginas jsp. Por el momento vamos a escribir nuestra parte de vista lo más sencilla posible. Más adelante quizá se utilice otro framework como ZKoss ó alguna de las implementaciones de JSF.
  • carpeta spring. Dentro de está carpeta tenemos el fichero car-service.xml. En este fichero lo único que vamos a hacer es crear un bean que usará la clase de servicio CarService.
  • car-servlet.xml. Vamos a explicar el contenido de este fichero a continuación.

    Este fichero incluirá en el contexto de nuestro DispatcherServlet una serie de beans. Lo primero que vemos es que cargamos el fichero car-service.xml. De manera que en el contexto de nuestro DispatcherServlet tendremos a nuestra disposición un bean CarService.
    El siguiente fragmento nos permite entre otras cosas poder acceder a nuestro carService desde nuestros controller utilizando para ello la anotación @Autowired.

    <context:annotation-config />
    

    El siguiente bean se encargará de mapear las peticiones hechas desde car.html con el controlador implementado en la clase CarController. Para el resto de peticiones utilizaremos el mapeo hecho directamente por la clase ControllerClassNameHandlerMapping.

    		<bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping">
    			<property name="caseSensitive" value="true" />
    		</bean> 
    

    Como hemos visto las vistas tienen que resolverse en un paso intermedio entre la llamada al controlador y la generación de la vista. Para ello vamos a utilizar el servlet InternalResourceViewResolver. Este servlet buscará las páginas con formato jsp en la url /WEB-INF/jsp.

    	<bean
    		class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    		<property name="prefix" value="/WEB-INF/jsp/"></property>
    		<property name="suffix" value=".jsp"></property>
    		<property name="order" value="1" />
    	</bean>
    

    En la siguiente parte declaramos la parte de interceptores. Vamos a declarar un interceptor en la clase TimeResponseInterceptor que simplemente añadirá un valor a nuestra petición el tiempo que ha pasado desde que se inicia hasta que termina. Este valor podrá ser más tarde usado por nuestra vista. Para usar nuestro interceptor lo tenemos que declarar utilizando la clase BeanNameUrlHandlerMapping para poderlo usar en nuestras peticiones.

    	<bean id="timeResponseInterceptor" class="com.hopcroft.interceptor.TimeResponseInterceptor"></bean>
    	<bean
    		class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
    		<property name="interceptors">
    			<list>
    				<ref bean="timeResponseInterceptor"></ref>
    			</list>
    		</property>
    	</bean>
    

    Este bean lo utilizaremos para resolver las excepciones. Si salta una exception de tipo CarException se mostrará la página indicada, en este caso carsNotAvailable. Si se genera una excepción de java.lang se mostará la página error.jsp. Dentro de esta página podremos acceder a la excepción usando ${exception}.

    	<bean	class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
    		<property name="exceptionMappings">
    			<props>
    				<prop key="com.hopcroft.exception.CarException">carsNotAvailable</prop>
    				<prop key="java.lang.Exception">error</prop>
    			</props>
    		</property>
    	</bean>
    

    Finalmente el siguiente bean se encargará de gestionar un formulario para crear nuevos coches. En lugar de inyectar el carService con @Autowired en la clase CarFormController la pasaremos como referencia en nuestro bean. La vista de formulario será carForm y la vista cuando se haga el submit será carInsertSuccess.

    	<bean id="carFormController" class="com.hopcroft.controller.CarFormController">
    		<property name="carService" ref="carService"></property>
    		<property name="formView" value="carForm"></property>
    		<property name="successView" value="carInsertSuccess"></property>
    	</bean>
    
  • web.xml. Este será el fichero de despliegue de nuestra aplicación. Aquí haremos lo siguiente:
    • Cargamos el fichero de contexto de nuestra aplicación.
      	<context-param>
      		<param-name>contextConfigLocation</param-name>
      		<param-value>classpath*:spring/applicationContext.xml</param-value>
      	</context-param>
      	<listener>
      		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
      	</listener>
      
    • Anotamos nuestro DispatcherServlet e indicaremos el formato de las peticiones al mismo.
      	<servlet>
      		<servlet-name>car</servlet-name>
      		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
      		<load-on-startup>1</load-on-startup>
      	</servlet>
      
      	<servlet-mapping>
      		<servlet-name>car</servlet-name>
      		<url-pattern>*.html</url-pattern>
      	</servlet-mapping>
      
      

Ejecutar el proyecto

Hemos agregado el plug-in de Jetty para poder probar la aplicación sin necesidad de tener un contenedor web aparte en nuestro equipo. Por lo tanto nos vamos a la raíz de nuestro proyecto y ejecutamos desde consola:

mvn clean install -DskipTests
mvn jetty:run

Ahora tendremos que hacer una petición a las dos páginas principales que tenemos en nuestra aplicación.

  • http://localhost:9080/spring-mvc/car.html El Dispatcher transladará la petición a nuestro controlador CarController. La lógica de nuestro CarController será bastante sencilla.
    public class CarController implements Controller {
    	@Autowired
    	private CarService carService;
    
    	public ModelAndView handleRequest(HttpServletRequest arg0,
    			HttpServletResponse arg1) throws Exception {
    		return new ModelAndView("car", "cars",  carService.getCars());
    	}
    

    Casi todo está en la parte del Service. En realidad sería más lógico pasar el control de que se va a ejecutar al Controller.

    
    public class CarServiceImpl implements CarService {
    	public CarServiceImpl() {
    	}
    
    	public List<Car> getCars() throws CarException {
    		List<Car> cars = initCars();
    		if ((Calendar.getInstance().getTimeInMillis() % 2) == 0) {
    			return cars;
    		} else if ((Calendar.getInstance().getTimeInMillis() % 2) == 0) {
    			throw new CarException(1L, "Car service ", new Date());
    		} else
    			throw new RuntimeException();
    	}
    
    	// TODO: llamar al DAO de car.
    	private List<Car> initCars() {
    		List<Car> carList = new ArrayList<Car>();
    		Car car = new Car();
    		car.setBrand("Ferrari");
    		car.setName("F40");
    		car.setPrice(1000000L);
    
    		Car car2 = new Car();
    		car2.setBrand("Porsche");
    		car2.setName("Carrera");
    		car2.setPrice(200000L);
    
    		Car car3 = new Car();
    		car3.setBrand("Opel");
    		car3.setName("Astra");
    		car3.setPrice(18000L);
    
    		carList.add(car);
    		carList.add(car2);
    		carList.add(car3);
    
    		return carList;
    	}
    

    CarController solicitará una lista de coches al servicio carService. En función de a que hora se solicite el servicio nuestro CarController devolverá:

    • Una lista de coches que recogerá del servicio CarService.
    • carList

    • Una excepción CarException. Como hemos visto en el anteriormente las excepciones de este tipo tendrán su propia página jsp carsNotAvailable donde devolveremos la respuesta.
    • carService

    • Una excepción genérica java.lang.Exception que estará asociada a la página error
  • http://localhost:9080/spring-mvc/carForm.html. Tendremos un formulario generado con Spring MVC. La parte más interesante está en como podemos asociar un objeto de dominio a un formulario para que sea Spring MVC el que te genere automáticamente un campo porque cada atributo del objeto de dominio, Car en nuestro caso.
    public class CarFormController extends SimpleFormController {
    
    	public CarFormController() {
    		setCommandClass(Car.class);
    		setCommandName("carInsert");
    	}
    
  • CarForm MVC

    CarFormResponse MVC

Esto es todo en este post, espero que os sea de ayuda para entender y comenzar con Spring MVC.

Saludos !!!

 
15 comentarios

Publicado por en 10 septiembre, 2011 en Spring, Spring MVC

 

Etiquetas: , , , , ,

15 Respuestas a “Ejemplo básico de Spring MVC con Maven

  1. mexicano20012

    3 enero, 2013 at 12:01 am

    codigo es lo k se necesita ver..

     
  2. Cristian

    23 enero, 2013 at 10:54 pm

    Esa xvr !! la explicación lo que necesito es tener la fuente para poder iniciarme, se te agradeceria bastante Gracias!!

     
    • hop2croft

      26 febrero, 2013 at 10:33 am

      Hola Cristian,

      gracias por leer el blog. En los últimos ejemplos que escribo en el blog suelo poner la referencia al repositorio de código de Github. Desafortunadamente no tengo el código de post antiguos. He hecho un escaneo en el disco duro pero no he podido encontrar nada.

      Un saludo

       
  3. mariel

    31 julio, 2013 at 8:08 pm

    Se pueden tener dos carpetas para las vistas?

     
    • hop2croft

      31 julio, 2013 at 8:18 pm

      Por supuesto🙂. Puedes meter tus vistas jps, html, … dentro de la carpeta WEB-INF/jsp y usar * ó ** para que el internal resource view resolver las encuentre si las has distribuido en varias carpetas.

      Gracias por leer el blog!!!

       
  4. Eduardo Daniel de la Rosa

    14 noviembre, 2013 at 1:04 am

    como entro a tu repositorio del código en github?

     
  5. erreterremoto

    24 noviembre, 2013 at 4:05 pm

    ¡Muy buen post! Aquí dejo un tutorial sencillo de MVC con Java y Eclipse: Configuración proyecto web MVC con Eclipse y Spring 3.2

     
  6. zero

    16 julio, 2014 at 7:49 pm

    necesito un tutorial de spring 2

     
    • hop2croft

      21 julio, 2014 at 3:01 am

      Hola zero,

      lo mejor forma de aprender en mi opinión es seguir el manual de referencia (http://docs.spring.io/spring-framework/docs/2.0.x/reference/). Muchos de los tutoriales al final acaban utilizando código de este manual.

      Si te fuera posible también te recomendaría que, en la medida de lo posible, migrases a una nueva versión de Spring. Aquí puedes encontrar una gran variedad de casos de uso (http://spring.io/guides).

      Un saludo y gracias por leerme.

       

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

 
A %d blogueros les gusta esto: