Transcript

Ejemplo bsico 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 deSpring 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 comoSpring 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 deCloud Computing, siempre como absoluto novato en la materia, analizando herramientas comoGoogle App Engine(GAE) Micro Cloud Foundry. Pero para ello todava queda . as que empezar conSpring MVC.Introduccin aSpring MVC.Spring MVCes un proyecto de Springsource que nos permite utilizar elpatrn MVCde una forma sencilla dentro de nuestras aplicaciones desarrolladas conSpring. En la siguiente imagen vemos el esquema de cmo funcionaSpring MVC.

Si hemos realizado previamente desarrollo web podemos identificar claramente que enSpring MVC, nuestroDispatcherServletfunciona bajo elpatrn front controller. Elpatrn front controllernos da un punto de entrada nico a nuestras peticiones. De manera que todas ellas van a pasar por un mismoServlet, en el caso deSpring MVC, se trata deDispatcherServlet. 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 nuestroDispatcherServlet(1) ElDispatcherServlettendr que encontrar que controlador va a tratar la peticin. Para ello elDispatcherServlettiene que encontrar el manejador asociado a la url de la peticin. Todo esto se realiza en la fase de HandlerMapping(2). Una vez encontrado eseController, nuestroDispatcherServletle 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 alDispatcherun objeto de tipoModelAndView(4). Qu quiere decir esto? En pocas palabras Modelser los valores que obtengamos de nuestra capa de servicio yView ser el nombre de la vista en la que queremos mostrar la informacin que va contenida dentro de ese Model. Una vez pasado este objetoModelAndViewalDispatcherServlet, 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 comoViewResolver(4). Finalmente y una vez resuelta la vista, nuestroDispatcherServlettendr que pasar nuestro Model (los valores recogidos en el controlador a travs de nuestra capa de servicios) a la vista concretaView (5).En los pasos deHandlerMapping, seleccin deControlleryViewResolver podemos indicarle a nuestroDispatcherServletqu estrategia queremos seguir. Os dejo unaentrada del blogdeAlex Fuentesdonde explica de manera bastante concisa los tipos de estrategias disponibles.Proyecto deSpring MVCconMavenPor qe utilizarGitHub?.Bueno, el punto anterior era teora, vamos a ver un proyecto donde explicar las clases Java y los ficheros de configuracin bsica para empezar conSpring MVC. Como siempre los proyectos estarn gestionados conMaven(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.gitSi no conoces todavaGit, puedes empezar echando un vistazo a los siguientes post: Git como herramienta de control deversiones Arquitectura deGit GitHub , repositorio remoto deGitLas razones por la que subo el cdigo aGitHubson 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 aGitHubse puede escribir un post sin necesidad de poner losimports 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 conGitHubse puede hacer unforkde los distintos proyectos, aadir funcionalidad y que est a disposicin de todos.Explicacin del cdigoYendo 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 desdeEclipse.

Como podemos ver en la anterior imagen al estar creado nuestro proyecto con Mavense ha creado laestructura tpicade un proyectoMaven, 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.CarControllerque se ejecutar cuando se solicite la pgina car.html yCarFormControllerque 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 deSpring MVC. Los interceptores sirven enSpringpara 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 servicioCarServiceva 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 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 comomessage*.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/testDe momento slo he creado una clase de test. Esta clase de test est realizada usando el frameworkSelenium. 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 conSelenium.Carpeta src/main/webapp/WEB-INFTenemos 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 comoZKoss alguna de las implementaciones deJSF. carpeta spring. Dentro de esta carpeta tenemos el ficherocar-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 nuestroDispatcherServletuna serie de beans. Lo primero que vemos es que cargamos el ficherocar-service.xml. De manera que en el contexto de nuestroDispatcherServlettendremos a nuestra disposicin un beanCarService.El siguiente fragmento nos permite entre otras cosas poder acceder a nuestrocarServicedesde nuestros controller utilizando para ello la [email protected]

El siguiente bean se encargar de mapear las peticiones hechas desde car.html con el controlador implementado en la claseCarController. Para el resto de peticiones utilizaremos el mapeo hecho directamente por la claseControllerClassNameHandlerMapping.1

2

3

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 servletInternalResourceViewResolver. Este servlet buscar las pginas con formato jsp en la url /WEB-INF/jsp.1

3

4

5

6

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

2

4

5

6

7

8

9

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

2

3

4carsNotAvailable

5error

6

7

8

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

2

3

4

5

web.xml. Este ser el fichero de despliegue de nuestra aplicacin. Aqu haremos lo siguiente: Cargamos el fichero de contexto de nuestra aplicacin.1

2contextConfigLocation

3classpath*:spring/applicationContext.xml

4

5

6org.springframework.web.context.ContextLoaderListener

7

Anotamos nuestroDispatcherServletindicaremos el formato de las peticiones al mismo.1

2car

3org.springframework.web.servlet.DispatcherServlet

41

5

6

7

8car

9*.html

10

Ejecutar el proyectoHemos 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:1mvn cleaninstall-DskipTests

2mvn jetty:run

Ahora tendremos que hacer una peticin a las dos pginas principales que tenemos en nuestra aplicacin. http://localhost:9080/spring-mvc/car.htmlElDispatchertransladar la peticin a nuestro controladorCarController. La lgica de nuestroCarControllerser bastante sencilla.1publicclassCarServiceImplimplementsCarService {

2publicCarServiceImpl() {

3}

4

5publicList getCars()throwsCarException {

6List cars = initCars();

7if((Calendar.getInstance().getTimeInMillis() %2) ==0) {

8returncars;

9}elseif((Calendar.getInstance().getTimeInMillis() %2) ==0) {

10thrownewCarException(1L,"Car service ",newDate());

11}else

12thrownewRuntimeException();

13}

14

15// TODO: llamar al DAO de car.

16privateList initCars() {

17List carList =newArrayList();

18Car car =newCar();

19car.setBrand("Ferrari");

20car.setName("F40");

21car.setPrice(1000000L);

22

23Car car2 =newCar();

24car2.setBrand("Porsche");

25car2.setName("Carrera");

26car2.setPrice(200000L);

27

28Car car3 =newCar();

29car3.setBrand("Opel");

30car3.setName("Astra");

31car3.setPrice(18000L);

32

33carList.add(car);

34carList.add(car2);

35carList.add(car3);

36

37returncarList;

38}

CarControllersolicitar una lista de coches al servicio carService. En funcin de a que hora se solicite el servicio nuestroCarControllerdevolver: Una lista de coches que recoger del servicio CarService. Una excepcinCarException. Como hemos visto en el anteriormente las excepciones de este tipo tendrn su propia pgina jspcarsNotAvailabledonde devolveremos la respuesta. Una excepcin genricajava.lang.Exceptionque estar asociada a la pgina error http://localhost:9080/spring-mvc/carForm.html. Tendremos un formulario generado conSpring MVC. La parte ms interesante est en como podemos asociar un objeto de dominio a un formulario para que seaSpring MVCel que te genere automticamente un campo porque cada atributo del objeto de dominio,Caren nuestro caso.1publicclassCarFormControllerextendsSimpleFormController {

2

3publicCarFormController() {

4setCommandClass(Car.class);

5setCommandName("carInsert");

6}

Esto es todo en este post, espero que os sea de ayuda para entender y comenzar conSpring 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 dispatchercual 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 Mappingcual sera le Controller que recivira el Request.Dentro del Spring existe varios Handler Mapping los cuales tiene distintascapacidades de poder mapear a los controladores.En el siguiente cuadro se indican los Handler Mapping que posee Spring.Handler MappingComo mapea el Request

BeanNameUrlHandlerMappingMapea controladores a URL basandose en el nombre del Bean

SimpleUrlHandlerMappingMapea controladores a URL basandose en una collecion de propiedades que se definen en el Spring application context.

ControllerClassNameHandlerMappingMapea controladores a URL utilizando el Controller Class Name

CommonsPathMapHandlerMappingMapea 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 elrequest 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 typeClassesUsarlo cuando ...

ViewParameterizableViewControllerUrlFilenameViewControllerPara cuando un controlador solo necesita desplegar informacin

SimpleController (interface)AbstractControllerPara controladores simples que solo se utilizan como Simples Servlet

ThrowawayThrowawayControllerPara manejar los request como un Comando

MultiactionMultiActionControllerPara implementar una serie de acciones con similar lgica

CommandBaseCommandControllerAbstractCommandControllerSi tus controladores reciben parmetros estos son manejados dentro de un objeto

FormAbstractFormControllerSimpleFormControllerPara desplegar y procesar un formulario, bajo el mismo componente

WizardAbstractWizardFormControllerPara 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 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 ResolverComo trabaja....

InternalResourceViewResolverResuelve el nombre logico utilizando el mapping a velocity y JSP

BeanNameViewResolverResuleve el nombre logico utilizando Bean definidos en el Spring Context

ResourceBundleViewResolverDefine el mapping entre los nombres logicos y las vistas asociadas , definiendolo en un archivo de propiedades

XmlViewResolverDefine el mapping entre los nombres logicos y las vistas asociadas , definiendolo en un archivo XML