Este documento presenta un proyecto básico de Spring MVC utilizando Maven. Explica la estructura del proyecto Maven y los archivos de configuración de Spring como applicationContext.xml y car-servlet.xml. También describe brevemente los controladores, servicios y vistas JSP utilizados en la aplicación de ejemplo.
Este documento presenta un proyecto básico de Spring MVC utilizando Maven. Explica la estructura del proyecto Maven y los archivos de configuración de Spring como applicationContext.xml y car-servlet.xml. También describe brevemente los controladores, servicios y vistas JSP utilizados en la aplicación de ejemplo.
Este documento presenta un proyecto básico de Spring MVC utilizando Maven. Explica la estructura del proyecto Maven y los archivos de configuración de Spring como applicationContext.xml y car-servlet.xml. También describe brevemente los controladores, servicios y vistas JSP utilizados en la aplicación de ejemplo.
Category:Spring, Spring MVC Tags:GitHub, i18n, Maven, Selenium, Spring, Spring MVC
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. Adems voy a hablar un poco de desarrollo e integracin continua y pruebas (Hudson / Jenkins, Sonar,Cobertura, Selenium, Checkstyles, PMD, ). Tambin me gustara 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 todava queda . as que empezar con Spring MVC. Introduccin a Spring MVC. Spring MVC es un proyecto de Springsource que nos permite utilizar el patrn MVC de una forma sencilla dentro de nuestras aplicaciones desarrolladas conSpring. En la siguiente imagen vemos el esquema de cmo funciona Spring MVC.
Si hemos realizado previamente desarrollo web podemos identificar claramente que en Spring MVC, nuestro DispatcherServlet funciona bajo el patrn front controller. El patrn front controller nos da un punto de entrada nico a nuestras 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 lgica en nuestra aplicacin. El flujo bsico en una aplicacin bajo Spring MVC es el siguiente: La peticin llega a nuestro DispatcherServlet (1) El DispatcherServlet tendr que encontrar que controlador va a tratar la peticin. Para ello el DispatcherServlet tiene que encontrar el manejador asociado a la url de la peticin. Todo esto se realiza en la fase de HandlerMapping (2). Una vez encontrado ese Controller, nuestro DispatcherServlet le dejar gestionar a ste la peticin (3). En nuestro controlador se deber realizar todo la lgica de negocio de nuestra aplicacin, es decir, aqu llamaremos a nuestra capa de servicios. El controlador devolver al Dispatcher un objeto de tipo ModelAndView (4). Qu quiere decir esto? En pocas palabras Model ser los valores que obtengamos de nuestra capa de servicio y View ser el nombre de la vista en la que queremos mostrar la informacin 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 (pgina 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 travs de nuestra capa de servicios) a la vista concreta View (5). En los pasos de HandlerMapping, seleccin 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 qe utilizar GitHub?. Bueno, el punto anterior era teora, vamos a ver un proyecto donde explicar las clases Java y los ficheros de configuracin bsica para empezar con Spring MVC. Como siempre los proyectos estarn gestionados con Maven (2.2.1). Como novedad voy a subir el cdigo a GitHub, as quien quiera probar los ejemplos simplemente tendr que acceder mi repositorio pblico en la siguiente direccin: [email protected]:IvanFernandez/hop2croftRepository.git Si no conoces todava Git, puedes empezar echando un vistazo a los siguientes post: Git como herramienta de control de versiones Arquitectura de Git GitHub , repositorio remoto de Git Las razones por la que subo el cdigo a GitHub son varias. Por un lado creo que facilita seguir el post tener todo el cdigo disponible y poder probarlo por uno mismo. Adems, y personalmente no me gusta nada, cuando estoy leyendo un post y veo que por ejemplo no estn incluidos los imports en el cdigo poner todo el cdigo de un mtodo . Subiendo el cdigo a GitHub se puede escribir un post sin necesidad de poner los imports slo parte de una clase (y no hacer un post kilomtrico), 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, aadir funcionalidad y que est a disposicin de todos. Explicacin del cdigo Yendo ya al proyecto concreto y para hacer ms claro una imagen general del proyecto creo que es bueno poner una imagen con la estructura del proyecto vista desde Eclipse.
Como podemos ver en la anterior imagen al estar creado nuestro proyecto con Maven se ha creado la estructura tpica 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 nuestros controladores. En nuestro caso vamos a tener dos controladores. CarController que se ejecutar cuando se solicite la pgina car.html y CarFormController que se ejecutar cuando se solicite carForm.html Domain. nuestras clases de dominio. Vamos a utilizar una 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 estarn las clases que tratarn 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 aadirles cierta lgica. 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 configuracin 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 aplicacin va a escribir sus pginas 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 estarn declaradas una serie de cadenas de texto y su valor. Cuando la pgina se pinte har uso de estos ficheros de propiedades para escribir el texto. Spring. En esta carpeta tendremos nuestro fichero de contexto de la aplicacin, applicationContext.xml. En este fichero simplemente vamos a cargar en nuestro contexto la carpeta locale que acabamos de explicar. view_es.properties. Este fichero todava no lo vamos a utilizar. La idea es utilizar este fichero para hacer redirecciones en nuestros navegadores. Carpeta src/main/test De momento slo he creado una clase de test. Esta clase de test est realizada usando el framework Selenium. Este test nos permite hacer pruebas de regresin, es decir, nuestra clase de test abrir nuestro navegador y ejecutar una secuencia de acciones (rellenar una caja de texto, pulsar un botn, irnos de una pgina a otra, ). El siguiente post que escribir ser una pequea explicacin de como empezar con Selenium. Carpeta src/main/webapp/WEB-INF Tenemos una serie de carpetas y ficheros de configuracin que vamos a explicar a continuacin: carpeta jsp. Aqu vamos a guardar nuestras pginas jsp. Por el momento vamos a escribir nuestra parte de vista lo ms sencilla posible. Ms adelante quiz se utilice otro framework como ZKoss alguna de las implementaciones de JSF. carpeta spring. Dentro de esta 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 continuacin.Este fichero incluir en el contexto de nuestroDispatcherServlet 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 disposicin un beanCarService. El siguiente fragmento nos permite entre otras cosas poder acceder a nuestro carService desde nuestros controller utilizando para ello la anotacin@Autowired. 1 <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.
1 <beanclass="org.springframework.web.servlet.mvc.support.ControllerCla ssNameHandlerMapping"> 2 <property name="caseSensitive" value="true" /> 3 </bean> Como hemos visto las vistas tienen que resolverse en un paso intermedio entre la llamada al controlador y la generacin de la vista. Para ello vamos a utilizar el servlet InternalResourceViewResolver. Este servlet buscar las pginas con formato jsp en la url /WEB-INF/jsp. 1 <bean 2 class="org.springframework.web.servlet.view.InternalResourceViewR esolver"> 3 <property name="prefix" value="/WEB-INF/jsp/"></property> 4 <property name="suffix" value=".jsp"></property> 5 <property name="order" value="1" /> 6 </bean> En la siguiente parte declaramos la parte de interceptores. Vamos a declarar un interceptor en la clase TimeResponseInterceptor que simplemente aadir un valor a nuestra peticin el tiempo que ha pasado desde que se inicia hasta que termina. Este valor podr ser ms tarde usado por nuestra vista. Para usar nuestro interceptor lo tenemos que declarar utilizando la clase BeanNameUrlHandlerMapping para poderlo usar en nuestras peticiones. 1 <bean id="timeResponseInterceptor"class="com.hopcroft.interceptor.TimeRespo nseInterceptor"></bean> 2 <bean 3 class="org.springframework.web.servlet.handler.BeanNameUrlHandler Mapping"> 4 <property name="interceptors"> 5 <list> 6 <ref bean="timeResponseInterceptor"></ref> 7 </list> 8 </property> 9 </bean> Este bean lo utilizaremos para resolver las excepciones. Si salta una exception de tipo CarException se mostrar la pgina indicada, en este caso carsNotAvailable. Si se genera una excepcin de java.lang se mostar la pgina error.jsp. Dentro de esta pgina podremos acceder a la excepcin usando ${exception}.
1 <bean class="org.springframework.web.servlet.handler.SimpleMappingExc eptionResolver"> 2 <property name="exceptionMappings"> 3 <props> 4 <propkey="com.hopcroft.exception.CarException">carsNotAva ilable</prop> 5 <prop key="java.lang.Exception">error</prop> 6 </props> 7 </property> 8 </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. 1 <bean id="carFormController"class="com.hopcroft.controller.CarFormCon troller"> 2 <property name="carService" ref="carService"></property> 3 <property name="formView" value="carForm"></property> 4 <property name="successView" value="carInsertSuccess"></property> 5 </bean> web.xml. Este ser el fichero de despliegue de nuestra aplicacin. Aqu haremos lo siguiente: o Cargamos el fichero de contexto de nuestra aplicacin. 1 <context-param> 2 <param-name>contextConfigLocation</param-name> 3 <param-value>classpath*:spring/applicationContext.xml</param- value> 4 </context-param> 5 <listener> 6 <listener- class>org.springframework.web.context.ContextLoaderListener</listener -class> 7 </listener> o Anotamos nuestro DispatcherServlet indicaremos el formato de las peticiones al mismo. 1 <servlet> 2 <servlet-name>car</servlet-name> 3 <servlet- class>org.springframework.web.servlet.DispatcherServlet</servlet- class> 4 <load-on-startup>1</load-on-startup> 5 </servlet> 6
7 <servlet-mapping> 8 <servlet-name>car</servlet-name> 9 <url-pattern>*.html</url-pattern> 10 </servlet-mapping> o o Ejecutar el proyecto Hemos agregado el plug-in de Jetty para poder probar la aplicacin sin necesidad de tener un contenedor web aparte en nuestro equipo. Por lo tanto nos vamos a la raz de nuestro proyecto y ejecutamos desde consola:
1 mvn clean install -DskipTests 2 mvn jetty:run Ahora tendremos que hacer una peticin a las dos pginas principales que tenemos en nuestra aplicacin. o http://localhost:9080/spring-mvc/car.html El Dispatchertransladar la peticin a nuestro controlador CarController. La lgica de nuestro CarControllerser bastante sencilla. 1 public class CarServiceImpl implements CarService { 2 public CarServiceImpl() { 3 } 4
5 public List<Car> getCars() throws CarException { 6 List<Car> cars = initCars(); 7 if ((Calendar.getInstance().getTimeInMillis() % 2) ==0) { 8 return cars; 9 } else if ((Calendar.getInstance().getTimeInMillis() % 2) == 0) { 10 throw new CarException(1L, "Car service ", newDate()); 11 } else 12 throw new RuntimeException(); 13 } 14
15 // TODO: llamar al DAO de car. 16 private List<Car> initCars() { 17 List<Car> carList = new ArrayList<Car>(); 18 Car car = new Car(); 19 car.setBrand("Ferrari"); 20 car.setName("F40"); 21 car.setPrice(1000000L); 22 23 Car car2 = new Car(); 24 car2.setBrand("Porsche"); 25 car2.setName("Carrera"); 26 car2.setPrice(200000L); 27 28 Car car3 = new Car(); 29 car3.setBrand("Opel"); 30 car3.setName("Astra"); 31 car3.setPrice(18000L); 32
37 return carList; 38 } o CarController solicitar una lista de coches al servicio carService. En funcin de a que hora se solicite el servicio nuestroCarControllerdevolver: o Una lista de coches que recoger del servicio CarService. o o Una excepcin CarException. Como hemos visto en el anteriormente las excepciones de este tipo tendrn su propia pgina jsp carsNotAvailabledonde devolveremos la respuesta. o o Una excepcin genrica java.lang.Exception que estar asociada a la pgina error o o http://localhost:9080/spring-mvc/carForm.html. Tendremos un formulario generado con Spring MVC. La parte ms interesante est en como podemos asociar un objeto de dominio a un formulario para que sea Spring MVC el que te genere automticamente un campo porque cada atributo del objeto de dominio, Caren nuestro caso. 1 public class CarFormController extends SimpleFormController { 2
3 public CarFormController() { 4 setCommandClass(Car.class); 5 setCommandName("carInsert"); 6 }
Esto es todo en este post, espero que os sea de ayuda para entender y comenzar con Spring MVC.
MVC en Spring En el siguiente apartado voy a detallar como esta implementado el patron Model-View-Controller sobre Spring framework. Como la mayoria de los framework que implementan el patron MVC, Spring tiene implementado un servlet que realiza las tareas de Front Controller, esto significa que cada uno de los request que son realizados por el usuario, pasan a traves de este servlet. El nombre que recibe este servlet es Dispatcher Servlet. Y como indicamos anteriormente, todos los request que son realizados por los distintos usuarios pasan por este componente. Como la imagen lo indica el Request llegan al Dispatcher el cual tiene la responsabilidad de delegar a otro componente el procesamiento del request. Para obtener el nombre del componente que recibira el request , Spring utiliza lo que se denomina el Handler Mapping , el cual tiene la funcion de determinar cual sera el Controller que recibira el request. El Handler Mapping como indicamos tiene el objetivo de indicar al dispatcher cual sera el componente que debe recibir el request enviado por el usuario. Por lo cual el Dispatcher Servlet , le pregunta a uno o mas Handler Mapping cual sera le Controller que recivira el Request. Dentro del Spring existe varios Handler Mapping los cuales tiene distintas capacidades de poder mapear a los controladores. En el siguiente cuadro se indican los Handler Mapping que posee Spring. Handler Mapping Como mapea el Request BeanNameUrlHandlerMapping Mapea controladores a URL basandose en el nombre del Bean SimpleUrlHandlerMapping Mapea controladores a URL basandose en una collecion de propiedades que se definen en el Spring application context. ControllerClassNameHandlerMapping Mapea controladores a URL utilizando el Controller Class Name CommonsPathMapHandlerMapping Mapea controladores a URL usando metadatas en el codigo del controlador. Esta metadata es definida usando Jakarta Commons Atributes. Luego de que el Handler Mapping le entrega nombre del Controller que se hara cargo del Request , el Distpacher Servlet le envia el request al Controller.Para poder implementar un Controller sobre Spring es necesario que se cree una clase que herede de los Controller que han sido implementados por Spring, los cuales dependiendo de la funcionalidad a realizar es el Controller que se debera utilizar. En el siguiente cuadro se puede ver que tipos de Controller estan disponibles sobre Spring.
Controller type Classes Usarlo cuando ... View ParameterizableViewController UrlFilenameViewController Para cuando un controlador solo necesita desplegar informacin Simple Controller (interface) AbstractController Para controladores simples que solo se utilizan como Simples Servlet Throwaway ThrowawayController Para manejar los request como un Comando Multiaction MultiActionController Para implementar una serie de acciones con similar lgica Command BaseCommandController AbstractCommandController Si tus controladores reciben parmetros estos son manejados dentro de un objeto Form AbstractFormController SimpleFormController Para desplegar y procesar un formulario, bajo el mismo componente Wizard AbstractWizardFormController Para realizar una interaccin ms rica con el usuario a travs de varias pantallas
Luego que el Controller recibe el Request , se construye un Objeto que se denomina ModelAndView ,este componente tiene como funcion la de : 1- Entregar un nombre logica a la vista que debera realizar el despliegue del Model 2- Entregar un nombre logico al Model que esta asociado a este componente 3- Inyectar el objeto Model el cual tiene los datos que seran desplegados en la Vista. Luego que el objeto ModelAndView es regresado al dispatcher y este componente delega la responsabilidad de la mapping del nombre logico de la vista, con el componente a utilizar al ViewResolver.El ViewResolver es el encargado de realizar el mapping entre el nombre logico de la vista y el componente. En el siguiente cuadro se indican los ViewResolver disponibles a ser configurados sobre Spring. View Resolver Como trabaja.... InternalResourceViewResolver Resuelve el nombre logico utilizando el mapping a velocity y JSP BeanNameViewResolver Resuleve el nombre logico utilizando Bean definidos en el Spring Context ResourceBundleViewResolver Define el mapping entre los nombres logicos y las vistas asociadas , definiendolo en un archivo de propiedades XmlViewResolver Define el mapping entre los nombres logicos y las vistas asociadas , definiendolo en un archivo XML