Ejemplo básico de Spring MVC con Maven.docx
-
Upload
carlosupsjb -
Category
Documents
-
view
74 -
download
0
Transcript of Ejemplo básico de Spring MVC con Maven.docx
Ejemplo básico de Spring MVC con MavenPosted by : hop2croft | On : 10/09/2011Category:Spring, Spring MVCTags: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. 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 aplicaciones desarrolladas
conSpring. En la siguiente imagen vemos el esquema de cómo funciona Spring
MVC.
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
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 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 esto 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 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 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ásica 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:
[email protected]:IvanFernandez/hop2croftRepository.git
Si no conoces todavía 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 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.
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/javaAquí 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 página
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 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/resourcesEn 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 nuestros navegadores.
Carpeta src/main/testDe 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-INFTenemos 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 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 continuación.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 disposición un
beanCarService.
El siguiente fragmento nos permite entre otras cosas poder acceder a
nuestro carService desde nuestros controller utilizando para ello la
anotació[email protected] <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.ControllerClassNameHandlerMapping">
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 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.
1 <bean
2 class="org.springframework.web.servlet.view.InternalResourceViewResolver">
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 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.
1<bean id="timeResponseInterceptor"class="com.hopcroft.interceptor.TimeResponseInterceptor"></bean>
2 <bean
3 class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
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 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}.
1 <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
2 <property name="exceptionMappings">
3 <props>
4 <propkey="com.hopcroft.exception.CarException">carsNotAvailable</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.CarFormController">
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 aplicación. Aquí haremos lo
siguiente:
o Cargamos el fichero de contexto de nuestra aplicación.
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 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:
1 mvn clean install -DskipTests
2 mvn jetty:run
Ahora tendremos que hacer una petición a las dos páginas principales que tenemos
en nuestra aplicación.
o http://localhost:9080/spring-mvc/car.html El Dispatchertransladará la petición
a nuestro controlador CarController. La lógica 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
33 carList.add(car);
34 carList.add(car2);35 carList.add(car3);
36
37 return carList;
38 }
o
CarController solicitará una lista de coches al servicio carService. En función de a
que hora se solicite el servicio nuestroCarControllerdevolverá:
o Una lista de coches que recogerá del servicio CarService.
o
o Una excepción CarException. Como hemos visto en el anteriormente las
excepciones de este tipo tendrán su propia página jsp carsNotAvailabledonde
devolveremos la respuesta.
o
o Una excepción genérica java.lang.Exception que estará asociada a la página error
o
o 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, 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
ParameterizableViewContr
oller
UrlFilenameViewController
Para cuando un
controlador solo
necesita desplegar
información
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 lógica
Command BaseCommandController
AbstractCommandControlle
r
Si tus controladores
reciben parámetros
estos son manejados
dentro de un objeto
Form AbstractFormController
SimpleFormController
Para desplegar y
procesar un formulario,
bajo el mismo
componente
Wizard AbstractWizardFormControl
ler
Para realizar una
interacción más rica
con el usuario a través
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 Model2- Entregar un nombre logico al Model que esta asociado a este componente3- 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....
InternalResourceViewReso
lver Resuelve el nombre logico utilizando el mapping a velocity y JSP
BeanNameViewResolver Resuleve el nombre logico utilizando Bean definidos en el Spring Context
ResourceBundleViewResolv
er
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