Spring Framework-Javier Sevilla
Spring Framework-Javier Sevilla
Spring Framework-Javier Sevilla
Quiero dar las gracias a Sergio, Julin, Elena, Javier y especialmente a Nieves, por todo su apoyo. A mis amigos, Fernando, Juan Carlos, Manuel, Miguel y a todo aquel que ya sea empujndome o tendindome la mano para alzarme me hace aprender.
Contenido
Introduccin al framework de Spring............................................................................................ 8 Inyeccin de dependencia e inversin de control ........................................................................ 8 Mdulos .................................................................................................................................... 8 El contenedor ........................................................................................................................ 9 Integracin y acceso de Datos............................................................................................. 10 Web ..................................................................................................................................... 10 AOP ...................................................................................................................................... 10 Test ...................................................................................................................................... 10 Primeros pasos con Spring .......................................................................................................... 11 Mi primer Hola mundo con Spring .......................................................................................... 11 Inyeccin de Dependencia ID (Dependency injection) ............................................................... 13 Inyeccin de dependencia en la prctica ................................................................................ 13 Programacin orientada a aspectos............................................................................................ 16 Qu ventajas tenemos con AOP? .......................................................................................... 17 Resumen...................................................................................................................................... 17
Mdulos
El framework de Spring consiste en elementos organizados en veinte mdulos. Estos mdulos se agrupan en el Contenedor (core container), Acceso a datos e integracin, modelo vista controlador (mdulo web MVC), aspectos (AOP), instrumentacin y test.
El contenedor
El contenedor consiste en un ncleo, objetos bean, un contexto y un lenguaje de expresiones. El ncleo y los beans son la parte fundamental de Spring, incluyendo la inversin de control y la inyeccin de dependencia. Este contenedor es una versin ms compleja del patrn Factory. Elimina la necesidad de programar singletons y permite desacoplar la configuracin y especificacin de dependencias de la lgica de programacin. El contexto se construye sobre la slida base del ncleo. As permite determinadas configuraciones. As la internacionalizacin, propagacin de eventos, lectura de recursos o la creacin de contextos (como el web) formarn parte de este mdulo. Los Lenguajes de expresin permiten una potente herramienta de consulta y manipulacin de un objeto en tiempo de ejecucin. Es una extensin del Unified EL, especificado en la especificacin JSP 2.1. El lenguaje permite asignar y obtener valores de las propiedades, asignar propiedades, invocar mtodos, acceder al contexto de matrices, listas, ndices, operadores aritmticos y lgicos, variables, y obtencin de objetos por nombre del contendor de Spring.
La capa de Integracin y acceso a datos consiste en la integracin de los mdulos JDBC, ORM, OXM, JMS y de transaccin. El mdulo JDBC otorga una capa de abstraccin que elimina la necesidad de crear cdigo tedioso y trasforma las excepciones generadas por el proveedor de base de datos. El mdulo ORM otorga una integracin con los APIS ms populares de ORM como puedan ser JPA, JDO, Hibernate o iBatis. El mdulo OXM otorga una capa de abstraccin para el mapeo Objeto/XML en distintas implementaciones como JAXB, Castor, XMLBeans, JiBX o XStream. El mdulo JMS contiene caractersticas para la produccin y consumo de mensajes. El mdulo de Transaccin permite transacciones programticas y declarativas para las clases que implementan interfaces especiales y para todos los POJO. Web
La capa web consiste en los mdulos Web, Web-Servlet, Web-Struts y Web-Portlet. El mdulo Web permite integracin bsica de caractersticas como la subida multiparte de un fichero, la inicializacin de la inversin de control del contenedor usando listeners Servlet y un contexto de lgica web. El mdulo Servlet contiene la implementacin modelo vista controlador. El framework Spring MVC permite una separacin entre el modelo, el cdigo y los formularios web y se integra con todas las otras caractersticas de Spring. El mdulo Web-Struts permite la integracin de clases integrando Struts, actualmente este soporte esta obsoleto en Spring 3.0. El mdulo Web-Portlet permite las caractersticas web en sistemas empotrados. AOP
El mdulo AOP de Spring permite una implementacin de programacin orientada a aspectos permitiendo definir mtodos e interceptores, puntos de corte, etc. Para desacoplar el cdigo. Permite la integracin con AspectJ l mdulo de instrumentacin otorga instrumentacin de clases as como un cargador de claes a ser usadas en determinadas aplicaciones de servidor. Test
11
El mdulo de test permite probar las aplicaciones de Spring y los componentes con JUnit o TestNG. Permite la carga consistente de contextos de Spring. As se permiten objetos mock que prueban tu cdigo de manera aislada.
Vemos cmo esta interfaz hace que cualquier clase que la implemente cumpla con la caracterstica de los beans de tener mtodos getters y setters. El siguiente paso es crear una clase que implemente esa interfaz, la llamaremos SaludoImp. Crearemos un constructor vaco al igual que un constructor con argumento para poder ilustrar las dos maneras que tiene Spring de inyectar dependencias a un Bean. Implementacin SaludoImpl
package es.uah.tfc.javiersevilla.holamundo; public class SaludoImpl implements Saludo { private String saludo; public SaludoImpl(String saludo){ this.saludo = saludo; } public SaludoImpl(){ } public String getSaludo() {
return this.saludo; } public void setSaludo(String saludo) { this.saludo = saludo; } public void saluda() { System.out.println(saludo); } }
Hay que hacer hincapi en que en ningn momento se instancia un objeto para el atributo saludo de la clase String sino que ste ser inyectado. Para comprender esto veamos el siguiente fichero de configuracin xml. Fichero de configuracin 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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd"> <bean id="saludoMetodo" class="es.uah.tfc.javiersevilla.holamundo.SaludoImpl"> <property name="saludo" value="Hola mundo!! (mtodo)" /> </bean> <bean id="saludoConstructor" class="es.uah.tfc.javiersevilla.holamundo.SaludoImpl"> <constructor-arg value="Hola mundo!! (constructor)"/> </bean> </beans>
Este fichero ser el que Spring utilice para la creacin de Beans, vemos como se crean dos uno haciendo uso de la inyeccin por parmetro y otro haciendo uso de la inyeccin por constructor.
Ya estn listos todos los componentes, slo falta ver la puesta en marcha. Para ello crearemos una clase con un mtodo main.
package es.uah.tfc.javiersevilla.holamundo; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class HolaMundo {
public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("holamundo.xml"); Saludo saludoConstructor = (Saludo) ctx.getBean("saludoConstructor");
13
Fijmonos en el cdigo, vemos como ApplicationContext es el contenedor el cual hemos cargado con el fichero XML. Este contenedor tiene los beans definidos en el fichero y podemos acceder a ellos mediante su identificacin.
Con este ejemplo queda esbozado el concepto de inyeccin de dependencia, pasemos a explicar este trmino de un modo ms amplio.
dependen, es decir, los objetos slo han de conocer el interfaz y su implementacin ha de ser inyectada en las clases dependientes. Cdigo del Interface Carrera:
package es.uah.uahdi.model; public interface Carrer { public void setName(String carrerName); public void setUniversityName(String universityName); public Certificate graduate(); }
15
En el fichero de configuracin vemos como hemos declarado dos beans el primero con el nombre itig el cual tiene dos propiedades y el otro juanito que tiene inyectado el bean itig. La clase principal cargar el contexto, obtendr el bean juanito y obtendr un diploma al ejecutar el mtodo de estudiar de juanito. Es importante destacar que gracias a que hemos creado interfaces y a su vez implementaciones de estas hemos podido crear un cdigo ms mantenible y reutilizable ya que cada implementacin no conoce las implementaciones de las dems, tan slo conoce su interfaz. Esto hace un cdigo menos acoplado y probable de manera unitaria.
17
implemente, en vez de en un sitio slo y que los componentes tienen cdigo que no es de su funcionalidad principal.
Resumen
En este captulo hemos presentado Spring, viendo que es un framework que facilita la creacin de aplicaciones hacindolas ms comprensibles, desacopladas y fciles de mantener. Hemos presentado sus principales componentes haciendo una breve visin de lo que Spring puede hacer por nosotros.
Hemos hecho una primera visin de los mdulos en los que se divide Spring, Estos mdulos se agrupan en el Contenedor (core container), Acceso a datos e integracin, modelo vista controlador (mdulo web MVC), aspectos (AOP), instrumentacin y test.
Ms tarde, a modo de prueba, hemos creado nuestra primera aplicacin con Spring, viendo de una manera ms prctica su funcionamiento. Hemos hecho una breve introduccin a la inyeccin de dependencia as como a la programacin orientada a aspectos.
21
23
Contenido
Conexin de Beans ...................................................................................................................... 24 Introduccin ............................................................................................................................ 24 Bean dentro de un contenedor ............................................................................................... 24 Contenedor de beans .............................................................................................................. 24 BeanFactory ............................................................................................................................ 25 ApplicationContext.................................................................................................................. 25 Pasos de la vida de un Bean ................................................................................................... 26 Ejemplo de creacin y conexin de beans .............................................................................. 27 Conectar tipos mltiples ......................................................................................................... 31 Tipos de conexin de beans ........................................................................................................ 34 Auto-conexin byName .......................................................................................................... 34 Auto-conexin byType ............................................................................................................ 35 Auto-conexin constructor ..................................................................................................... 35 Auto-conexin autodetect ...................................................................................................... 35 La auto-conexin default ........................................................................................................ 36 La Auto-conexin no ............................................................................................................... 36 Configuracin de la creacin de los beans .................................................................................. 37 Delimitacin de un bean ......................................................................................................... 37 Creacin de beans con clases singleton .................................................................................. 38 Uso de init-method y de destroy-method (inicializar y destruir)............................................ 38
Conexin de Beans
Introduccin
En el captulo anterior hemos dado los primeros pasos con Spring, hemos visto por primera vez la inyeccin de dependencia (DI) y la programacin orientada a aspectos (AOP) creando unos sencillos proyectos a modo de ejemplo.
A lo largo de este captulo veremos ms en profundidad el contenedor de Spring y sus distintas implementaciones. Las principales categoras de las implementaciones son la factora de beans y el contexto de aplicacin. En este captulo veremos cmo conectar beans dentro del contenedor y qu informacin daremos al fichero XML para crear una aplicacin con una DI potente.
Contenedor de beans
Como ya dijimos, en el enfoque tradicional las asociaciones llevan a cdigo complejo y no reutilizable ni testeable. En Spring el contenedor es el responsable de la interconexin de beans. El contenedor ser el responsable de la inyeccin de dependencia y del ciclo de vida de los beans, crendolos, interconectndolos y destruyndolos. Spring tiene diversos contenedores, pero responden a dos grandes grupos segn la implementacin del interfaz BeanFactory o AplicationContext.
25
BeanFactory
La implementacin ms usual es la XmlBeanFactory al cual se le pasa un objeto que implemente Resource (hay ocho implementaciones) que proporcionar el XML con las definiciones de los beans. Es el contenedor ms bsico, pero es ms que la instanciacin y entrega de beans. Cuando una fbrica entrega un bean lo entrega configurado, consciente de sus dependencias y listo para usar. El siguiente ejemplo crea un XmlBeanFactory como implementacin de BeanFactory con un FileSystemResource como implementacin de Resource y obtiene el bean llamado beanEjemplo.
ApplicationContext
Las aplicaciones creadas con Spring con normalidad usan implementaciones de AplicationContext en vez de BeanFactory. Esto es debido a que AplicationContext tiene funcionalidades adicionales dejando la BeanFactory para aplicaciones con recursos ms limitados como pueda ser un mvil, o sistemas empotrados. Otra diferencia es cmo abren los beans. Mientras la factora crea el bean cuando se llama a getBean() el contexto abre previamente todos los beans. ApplicationContext tiene la posibilidad de internacionalizar aplicaciones (I18N), generalizar la apertura de recursos de archivo as como otorgar la posibilidad de publicar eventos para beans receptores. De las diversas implementaciones las ms comunes son ClassPathXmlApplicationContext, FileSystemXmlApplicationContext y XmlWebApplicationContext. sta ltima la trataremos ms adelante en el captulo destinado a Spring MVC. La diferencia entre FileSystemXmlApplicationContext y ClassPathXmlApplicationContext es que el primero buscar en el sistema de archivos y el segundo segn el path de la clase incluyendo archivos jar.
1. 2. 3. 4.
5.
6. 7.
Instanciar: Spring instancia el bean. Informar de las propiedades: Spring hace la inyeccin a las propiedades. Asignacin del nombre: Spring pasa el nombre del bean si este implementa BeanNameAware. Establecer nombre de la fbrica y el contexto: Si el bean implementa BeanNameAware le pasar el nombre de la fbrica y en el caso de implementar ApplicationContextAware y de estar contenido en un contexto se le pasara el nombre del contexto. Iniciacin del Bean: Si el bean implementa InitializingBean, se llamara a su mtodo afterPropertiesSet(). Existen dos mtodos que se llamaran si hubiese algn BeanPostProccessor, uno antes de la inicializacin y otro despus. Estado de listo para su uso: Tras esto, el bean ya estara listo para usarse. Destruccin del bean: Si el bean implementa DisposableBean se llamar al mtodo destroy().
27
this.coffeesServedDaily = coffeesServedDaily; } public void work() throws WorkingException { System.out.println("el camarero est sirviendo " + coffeesServedDaily + " cafs"); } }
Esta es la definicin ms bsica de un bean, tan slo se le da un identificador y la clase a la que pertenece el objeto. De los dos constructores que tienen los camareros, Joselito ser creado con el constructor sin parmetros. Tal y como vemos no es muy complicado que se te considere como camarero, con servir un caf al da vale. Pero no creo que Joselito consiga el trabajo con tan poco esfuerzo. Pepe viene con ms entusiasmo, l asegura que es capaz de servir al menos 30 cafs al da. Para ello hacemos uso del constructor con parmetro, en Spring el modo de definirlo es as:
<bean id="pepe" class="es.uah.uahconnection.cafe.model.Waiter"> <constructor-arg value="30"/> </bean>
Con la etiqueta <constructor-arg> se pasan los parmetros que el constructor precise siempre que coincidan en nmero, tipo y posicin as obtenemos la inyeccin mediante constructor. Pepe es capaz de servir 30 cafs, pero hay competidores ms preparados. Alberto es capaz de poner el mismo nmero de cafs mientras limpia el local. Veamos como es el cdigo del camarero-limpiador.
public class WaiterDustman extends Waiter { private Local local; public WaiterDustman(int coffeesServedDaily, Restaurant restaurant) { super(coffeesServedDaily); this.local = restaurant; } @Override public void work() throws WorkingException{ super.work(); local.clean(); } }
29
public class Restaurant implements Local { public void clean() { System.out.println("El ha sido limpiado"); } }
La definicin del bean Alberto que haremos en Spring le inyectaremos otro bean cafeteriaPolitecnica el cual es de la clase restaurante.
<bean id="alberto" class="es.uah.uahconnection.cafe.model.WaiterDustman"> <constructor-arg value="25"/> <constructor-arg ref="cafeteriaPolitecnica"/> </bean>
Como hemos visto en anteriores ejemplos y hemos comentado al principio de este punto, Spring permite configurar dependencias con los mtodos setter como es el caso del cocinero y su receta.
public class Kitchener implements Worker { private Recipe recipe;//receta private int diners;//comensales public Kitchener() { } public void setRecipe(Recipe recipe) { } public void setDiners(int diners) { this.diners = diners; } public void work() throws WorkingException { System.out.println("Se dispone a cocinar " + recipe.getName()); recipe.develop(); } }
private static final String NAME = "Paella valenciana"; public void develop() { fryTheMeat(); sauteVegetables(); pourWater(); addRice(); } public String getName() { return NAME; } public void fryTheMeat(){ System.out.println("La carne se est friendo"); } public void sauteVegetables(){ System.out.println("las verduras se estn rehogando"); } public void pourWater(){ System.out.println("se vierte agua"); } public void addRice(){ System.out.println("se aade el arroz"); } }
Ernesto es un buen cocinero cuya especialidad es la paella valenciana receta de Arguiano. La definicin de Ernesto as como de su paella valenciana sera la siguiente.
<bean id="paellaValencianaArguiano" class="es.uah.uahconnection.cafe.model.PaellaValenciana"/> <bean id="ernesto" class="es.uah.uahconnection.cafe.model.Kitchener"> <property name="recipe" ref="paellaValencianaArguiano"/> <property name="diners" value="6"/> </bean>
Como vemos en el cdigo podemos asignar valores mediante el tag <property>. Esto slo ser as si en la clase tenemos un mtodo setter para tal propiedad. Para inyectar valores simples se har con el campo value mientras que para inyectar un bean definido en Spring se har mediante el campo ref. Spring sabr si los valores simples son de tipo numrico, cadena o booleano. Para definir a Ernesto se ha usado una inyeccin de bean y otra de valor simple, se ha pasado la receta de paella que aprendi de Arguiano y el nmero de comensales para el que es capaz de cocinar.
Inyectar beans internos Los beans internos, al igual que pasara con las clases declaradas de manera interna dentro de otra clase, son Beans definidos dentro del rango de bean. El siguiente aspirante al puesto de trabajo es tambin cocinero, pero su receta de la paella valencia la guarda celosamente, no quiere compartirla con el resto.
<bean id="alfredo" class="es.uah.uahconnection.cafe.model.Kitchener"> <property name="diners" value="5"/> <property name="recipe">
31
Si la clase cocinero pudiera configurarse por constructor tambin se podra crear un bean interno de la siguiente manera:
<bean id="alfredo" class="es.uah.uahconnection.cafe.model.Kitchener"> <constructor-arg value="5"/> <constructor-arg> <bean class="es.uah.uahconnection.cafe.model.PaellaValenciana"/> </constructor-arg> </bean>
La clase de la propiedad recipes podra ser tambin una lista o incluso un array al igual que a la hora de definirlo podramos haberlo hecho con la etiqueta <set> en vez de <list>, esto asegurara que no hubiese repetidos. El trato con la gente es algo muy valorado, ya que todo cliente estar ms contento si alguien le atiende de una manera amable. Borja Mari es un poco pedante, pero cae bien. Mientras sirve cafs suele amenizar con frases amables. Borja Mari pertenece a la clase camarero amable cuyo cdigo es el siguiente:
public class KindWaiter extends Waiter { private Properties kindSpeach; public void setKindSpeach(Properties kindSpeach) { this.kindSpeach = kindSpeach; } @Override public void work() throws WorkingException { super.work(); System.out.println("Mientras da una buena conversacin a sus clientes: "); for (Iterator it = kindSpeach.keySet().iterator(); it.hasNext();) { String speachType = (String) it.next(); System.out.println(" - para " + speachType + " dice \"" + kindSpeach.getProperty(speachType) + "\""); } } }
Para poder definirlo en el contenedor de Spring incluiremos el siguiente cdigo en el fichero xml.
<bean id="borjaMari" class="es.uah.uahconnection.cafe.model.KindWaiter"> <property name="kindSpeach"> <props> <prop key="buenosDias">Buenos das, que bonita maana hace hoy</prop> <prop key="buenasTardes">Buenas y maravillosas tardes</prop> <prop key="servir">Aqu est su caf</prop> <prop key="cuenta">Le traigo su cuenta enseguida</prop> <prop key="despedirse">Muchas gracias y vuelva otra vez</prop> </props> </property> </bean>
Hasta ahora todos nuestros aspirantes al puesto vacante para la cafetera de la universidad han sido muy buenos, pero tambin se valoran dotes de liderazgo. El seor Antunez es un buen coordinador y no slo trabaja sino que sabe organizar a los dems. Para la definicin de la clase coordinador utilizaremos un mapa en el cual queden reflejados los nombres de los trabajadores para que el seor Antunez pueda referirse por su nombre (la clave de la clase Map) y el propio trabajador.
33
La autoconexin y la conexin especificada pueden perfectamente convivir. Tambin hay que decir que hay otro debate si es til la auto-conexin o no.
Auto-conexin byName
Si el nombre que le damos a un bean coincide con el nombre de la propiedad, ste se autoconectar con la propiedad si lleva el campo autowire=byName. Como ejemplo podramos cambiar la definicin de estudiante y carrera del ejemplo de la Inyeccin de dependencia:
<bean id="itig" class="es.uah.uahdi.model.CarrerImp"> <property name="universityName" value="Universidad de Alcal de Henares"/> <property name="name" value="Ingeniera Tcnica en Informtica de Gestin"/> </bean> <bean id="juanito" class="es.uah.uahdi.model.StudentImp"> <property name="name" value="Juan Aurelio Gonzalez"/>
35
Cuando asignamos la auto-conexin por nombre estamos dicindole a Spring que busque en el contenedor beans no los mismo nombres que las propiedades. Las propiedades que definamos de la manera convencional no se auto-conectarn. El ejemplo slo podramos auto-conectar la misma carrera, si quisisemos inyectar ms beans de distinto tipo en otros lo tendramos que hacer sin auto-conexin.
Auto-conexin byType
La auto-conexin por tipo es similar a por nombre. Su funcionamiento es que Spring busca beans del mismo tipo de la propiedad y las inyecta. En el caso de que Spring encuentre varias se lanzar una excepcin de tipo UnsatisfiedDepedencyException. Si en el ejemplo anterior slo podamos auto-conectar aquellas propiedades cuyo nombre fuese el mismo que el del bean y si queramos conectar otras de distinto nombre tena que ser con el mtodo normal, con la auto-conexin por tipo no podremos conectar ningn otro bean del mismo tipo. Es decir, la propiedad que tenga autowire=byType obliga a que no haya ms beans de la misma clase en el contenedor.
<bean id=exampleBean class=example.ExampleClass autowire=byType/>
Auto-conexin constructor
La auto-conexin por constructor tiene las mismas limitaciones que la auto-conexin por tipo pero slo para las propiedades del constructor, Spring no intentar adivinar que bean autoconectar si hay ms de uno del mismo tipo.
<bean id=exampleBean class=example.ExampleClass autowire=constructor/>
Auto-conexin autodetect
La conexin por auto-deteccin hace que Spring primero intente conectar mediante constructor y luego mediante tipo.
<bean id=exampleBean class=example.ExampleClass autowire=autodetect/>
La auto-conexin default
Si en el tag de beans configuramos el campo default-autowire los beans que definamos en su cuerpo se autoconectarn de esa forma. Tambin se puede incluir explcitamente en el tag del bean el valor default.
<beans default-autowire=byType> </beans>
La Auto-conexin no
Si se especifica en la propiedad del bean autowire=no no se har autoconexin, incluso si en el tag beans indiquemos lo contrario en el campo default-autowire, no se har.
37
Delimitacin de un bean
Si no se especifica nada, Spring instancia cada bean de forma nica, por cada peticin siempre se entrega el mismo bean. Con el campo prototype del tag bean podemos alterar este comportamiento. Las delimitaciones de bean de Spring permiten declarar el lmite bajo en el que se crean los beans sin tener que codificar las reglas de limitacin en la clase del bean. Al igual que pasaba con bean, el trmino Singleton en Spring no es sinnimo del aplicado tradicionalmente en java. Como veremos, a diferencia de ste, en Spring no son otorgados por la obligatoriedad de la clase en s, sino que Spring otorga este diseo de manera ajena a la propia clase.
Tpo de lmite Singleton Limita a una nica instancia por contenedor. Es la opcin predeterminada. Permite que un bean sea instanciado cualquier nmero de veces (se instanciar cada vez que se use). En Spring MVC es usado para que el bean se instanciado una vez por peticin HTTP (request). Al igual que request es usado en entorno web y la instancia del bean durar lo mismo que dure la sesin en el servidor. Limita el bean a una sesin global en el servidor en entornos web.
Prototype
Request
Session
Global-session
Dependiendo de las necesidades del sistema de informacin, unas veces ser til dejar la configuracin por defecto, es decir, la instanciacin singleton y otras veces ser til tener varias instancias con prototype. Con respecto al servidor web, ms tarde veremos todas las utilidades que podemos encontrar para la construccin de distintos beans.
En el anterior ejemplo hemos definido una clase esttica interna cuya funcin es devolver una instancia de la clase restaurante, cumpliendo el patrn de diseo tradicional de singleton. Para instanciar correctamente este bean en Spring le deberemos especificar en su definicin el campo factory-method el mtodo de instanciacin. El cdigo xml sera el siguiente.
<bean id="cafeteriaPolitecnica" class="es.uah.uahconnection.cafe.model.Restaurant" factory-method="getInstance"/>
39
Tambin si tenemos varios beans a los cuales tenemos que inicializar y todos ellos tienen el mismo nombre de mtodo (usualmente init y clean) podramos definirlo por defecto en el tag de beans como muestra el cdigo de ejemplo.
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd" default-init-method="init" default-destroy-method="clean">
Continuando con el ejemplo de la cafetera, algo muy importante para todo cocinero es limpiarse las manos antes de cocinar y vestirse adecuadamente con el delantal. Cuando ste acabe con su jornada laboral deber quitarse el delantal. As que, como queremos cocineros limpios en la universidad modifiquemos el cdigo del cocinero.
public class Kitchener implements Worker { private Recipe recipe;//receta private int diners;//comensales public Kitchener() { } public void setRecipe(Recipe recipe) { this.recipe = recipe; } public void setDiners(int diners) { this.diners = diners; } public void work() throws WorkingException { System.out.println("Se dispone a cocinar " + recipe.getName()); recipe.develop(); System.out.println("Para " + diners + " comensales"); } public void init(){ System.out.println("El cocinero se pone el delantal"); System.out.println("El cocinero se lava las manos"); } public void clean(){ System.out.println("El cocinero se quita el delantal"); } }
init-method="init" destroy-method="clean"> <property name="diners" value="5"/> <property name="recipe"> <bean class="es.uah.uahconnection.cafe.model.PaellaValenciana"/> </property> </bean>
Como no slo el cocinero manipula alimentos creemos que todos los chef deberan antes de comenzar su jornada laboral limpiarse y ponerse vestimenta adecuada as como de la misma forma, cuando terminen su jornada laboral cambiarse de ropa, con lo que todos los chef implementarn el mtodo init y tambin el mtodo destroy. Spring tiene una forma ms til de poder definir un mtodo por defecto de inicio as como otro de destruccin como ya hemos visto. El cdigo perteneciente a esto sera:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd" default-init-method="init" default-destroy-method="clean">
An sigue teniendo Spring una forma ms de hacer todo esto. Si la clase que definimos implementa el interfaz InitializingBean y el DisposableBean. Utilizar los mtodos afterPropertiesSet y destroy, como ya comentamos al inicio de ste captulo. Vemos como sera el cdigo del cocinero si implementase esto:
public class Kitchener implements Worker, InitializingBean, DisposableBean { private Recipe recipe;//receta private int diners;//comensales public Kitchener() { } public void setRecipe(Recipe recipe) { this.recipe = recipe; } public void setDiners(int diners) { this.diners = diners; } public void work() throws WorkingException { System.out.println("Se dispone a cocinar " + recipe.getName()); recipe.develop(); System.out.println("Para " + diners + " comensales"); } public void init(){ System.out.println("El cocinero se pone el delantal"); System.out.println("El cocinero se lava las manos"); } public void clean(){ System.out.println("El cocinero se quita el delantal"); } public void afterPropertiesSet() throws Exception { init(); }
41
Como seguramente se haya percatado, esta opcin liga el desarrollo de las clases de nuestro sistema de informacin al framework de Spring, hacindolas menos reutilizables. Es por ello que an que la opcin est y haga que nuestro xml de configuracin sea ms limpio esta opcin se descarte en muchas ocasiones.
43
45
Contenido
Introduccin ................................................................................................................................ 46 Conceptos bsicos ................................................................................................................... 46 Advice .................................................................................................................................. 46 Join point ............................................................................................................................. 46 Pointcut ............................................................................................................................... 46 Aspect .................................................................................................................................. 47 Introduction ........................................................................................................................ 47 Target .................................................................................................................................. 47 Proxy.................................................................................................................................... 47 Weaving............................................................................................................................... 47 AOP en Spring.............................................................................................................................. 47 Ejemplo de aplicacin en Spring ................................................................................................. 48 Creacin de una clase Advice (Notificacin) ........................................................................... 49 MethodInterceptor (Around Advice o notificacin alrededor)............................................... 50 Definir Pointcuts y Advice ....................................................................................................... 50 Declaraciones con expresiones regulares (Regular Expressions Pointcuts)........................ 51 Definicin de puntos de corte con AspectJ ......................................................................... 51 Configuracin con ProxyFactoryBean ..................................................................................... 52 Abstraccin de ProxyFactoryBean .......................................................................................... 53 Autoproxying ............................................................................................................................... 54 Autoproxy bsico de beans. .................................................................................................... 54 @AspectJ ................................................................................................................................. 55 Creacin de aspectos POJO ..................................................................................................... 56
Introduccin
La Programacin Orientada a Aspectos (POA) es un paradigma de programacin relativamente reciente cuya intencin es permitir una adecuada modularizacin de las aplicaciones y posibilitar una mejor separacin de conceptos. Spring puede encapsular los diferentes conceptos que componen una aplicacin en entidades bien definidas, eliminando las dependencias entre cada uno de los mdulos. As tenemos un cdigo menos acoplado, sin cdigo dependiente y disperso y con POJOS sencillos, adaptables y reusables. Spring se ha erigido como un lder dentro de la programacin orientada a aspectos junto a otros frameworks como pueda ser JBoss o AspectJ. En el desarrollo de software, las funciones que abarcan varios puntos de una aplicacin se suelen llamar aspectos transversales (cross-cutting concerns) y suelen estar separados de la lgica empresarial. La AOP intenta separar estos aspectos de la lgica de la aplicacin. En el captulo segundo vimos cmo utilizar la DI para desacoplar los objetos dependientes. AOP desacopla los objetos transversales. Veremos a lo largo del captulo funcionalidades AOP de Spring, as como cmo AspectJ se acopla perfectamente.
Conceptos bsicos
Antes de meternos en harina, es recomendable entender cada uno de los trminos que se manejaran ms adelante. La programacin Orientada a Aspectos ha creado su propia jerga y las traducciones al castellano comnmente no convergen. Advice Notificacin o consejo es la implementacin del aspecto, es decir, contiene el cdigo que implementa la nueva funcionalidad. Se insertan en la aplicacin en los Puntos de Cruce. Join point Un punto de unin es un punto en la ejecucin de la aplicacin en el que se puede insertar un aspecto, puntos de ejecucin dentro del sistema donde un aspecto puede ser tejido, como una llamada a un mtodo, el lanzamiento de una excepcin o la modificacin de un campo (ste ltimo no contemplado por Spring). El cdigo del aspecto ser insertado en el flujo de ejecucin de la aplicacin para aadir su funcionalidad. Pointcut Los Puntos de Corte definen los Consejos que se aplicarn a cada Punto de Cruce. Se especifica mediante Expresiones Regulares o mediante patrones de nombres (de clases, mtodos o campos), e incluso dinmicamente en tiempo de ejecucin segn el valor de ciertos parmetros.
47
Aspect Es la fusin de notificacin y de punto de corte. El Aspecto es una funcionalidad transversal (cross-cutting) que se va a implementar de forma modular y separada del resto del sistema. El ejemplo ms comn y simple de un aspecto es el logging (registro de sucesos) dentro del sistema, ya que necesariamente afecta a todas las partes del sistema que generan un suceso. Otros ejemplos podran ser transacciones, seguridad o verificacin de credenciales. Introduction La Introduccin permite aadir mtodos o atributos a clases ya existentes. Un ejemplo en el que resultara til es la creacin de un Consejo de Auditora que mantenga la fecha de la ltima modificacin de un objeto, mediante una variable y un mtodo setUltimaModificacion(fecha), que podran ser introducidos en todas las clases (o slo en algunas) para proporcionarles esta nueva funcionalidad. Target El destinatario (Target) es el objeto que est siendo notificado. Puede ser un objeto que usted escriba o un objeto de terceros al que quiere aadir un comportamiento personalizado. Proxy El proxy o resultante, es un objeto creado despus de aplicar una notificacin al objeto destinatario. En tanto concierne a los objetos clientes, el objeto destinatario (Pre-AOP) y el objeto proxy (Post-AOP) son el mismo, el resto de la aplicacin cree que el proxy es el destinatario y no tendr que cambiar nada para darle soporte. Weaving El Tejido es el proceso de aplicar Aspectos a los Objetos Destinatarios para crear los nuevos Objetos Resultantes en los especificados Puntos de Cruce. Este proceso puede ocurrir a lo largo del ciclo de vida del Objeto Destinatario: Momento de compilacin: Los aspectos se tejen cuando se compila la clase destinataria. Esto requiere un compilador especial. El compilador de AspectJ teje aspectos de esta forma. Momento de apertura de la clase: Los aspectos se entretejen cuando se abre la clase destinataria en el JMV. Esto requiere un ClassLoader especial que resalta el cdigo de esa clase destinataria antes de que la clase se introduzca en la aplicacin. Tiempo de ejecucin: Es la manera que tiene Spring de hacerlo. Los aspectos se entretejen en algn momento durante la ejecucin del programa. Normalmente, un con tenedor AOP generar dinmicamente un objeto resultante que delegar al objeto destinatario mientras teje los aspectos.
AOP en Spring
No todos los marcos de trabajo AOP son iguales. Algunos permiten notificaciones a nivel de modificacin de campo, mientras slo a invocacin de mtodos. Ha habido cambios en el enfoque AOP y han convergido diversos frameworks y otros han desaparecido. Los tres marcos de trabajo principales son AspectJ, JBoss AOP y Spring AOP.
En Spring se da soporte de cuatro maneras algunas de ellas no incluidas en Spring 1: 1. 2. 3. 4. AOP clsico, basado en proxy. Notacin AspectJ (slo en Spring 2.x) Aspectos POJO puros (slo en Spring 2.x) Aspectos AspectJ inyectados.
Como hemos comentado, Spring slo da soporte a la interceptacin de mtodos, pero teniendo en cuenta la filosofa de los bean de Java Enterprise Edition, esto lo convertira en una buena prctica. El cdigo del aspecto as como la configuracin sern muy familiares a los desarrolladores, ya que todo se escribe en Java. Sin embargo, AspectJ tiene su propia sintaxis y es una extensin. En Spring hay cinco tipos de notificacin. Cada una de ellas definida por un interfaz. Tipos Para antes del mtodo Despus del mtodo Alrededor del mtodo Introduccin Interfaz org.springframework.aop.MethodBeforeAdvice org.springframework.aop.AfterReturningAdvice org.aopalliance.intercept.MethodInterceptor org.springframework.aop.IntroductionInterceptor
49
public void setName(String name) { this.name = name; } public Subject getSubject() { return subject; } public void setSubject(Subject subject) { this.subject = subject; } public void setConserje(Conserje conserje) { this.conserje = conserje; } public void teach() { conserje.openClassRoom(this); System.out.println("El profesor " + name + " est dando clase de " + subject.getName()); conserje.closeClassRoom(this); } }
Con esta implementacin el profesor debera estar detrs del conserje para que le abriese la puerta, perdiendo as su tiempo. Al igual que en ejemplo del primer captulo, no queremos que eso ocurra.
public class WatchmanAdvice implements MethodBeforeAdvice, AfterReturningAdvice, ThrowsAdvice { private Watchman watchman; public WatchmanAdvice(Watchman watchman) { this.watchman = watchman; } public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable { watchman.openClassRoom((Teacher)arg2);//al empezar la clase } public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable { watchman.closeClassRoom((Teacher)arg3);//al terminar la clase } public void afterThrowing (Object target, Throwable throwable){ watchman.teacherDidNotCome((Teacher target);//si el profesor no est }
Como vemos en el cdigo, la notificacin tiene un conserje como dependencia as que en la definicin le inyectaremos una implementacin de conserje.
<bean id="watchmanAdvice" class="es.uah.uahaop.aop.WatchmanAdvice"> <property name="watchman" ref="julian"/> </bean>
Echando un vistazo al cdigo vemos cmo son los mtodos que se tienen que implementar. Al implementar MethodBeforeAdvice se requiere el mtodo before(). Los tres parmetros que tiene corresponden a java.lang.reflect.Method que es el mtodo ejecutado para aplicar la notificacin. El segundo, la matriz, corresponde a los parmetros del mtodo que se han pasado. El ltimo es el objeto del cual se ha ejecutado el mtodo. El mtodo afterReturning es requisito de la interfaz ReturningAdvice y se ejecuta despus de la ejecucin del mtodo. Su primer parmetro es el objeto devuelto tras la ejecucin del mtodo siendo el resto igual que los de before(). Por ltimo hemos implementado la interfaz ThrowsAdvice la cual no es requisito implementar ningn mtodo, se toma como un mero marcador para Spring, dicindole que esta notificacin es capaz de interceptar una excepcin. Este mtodo puede implementar distintos mtodos teniendo parmetros opcionales. Comparte cada uno de los parmetros de before() e incluye otro ms de tipo Throwable todos ellos excepto ste son opcionales. En nuestro caso, si algo ocurre en el trascurso de la clase que impida que se imparta saltar una excepcin y ser entonces cuando nuestra notificacin (consejo o Advice) entrar en escena haciendo que nuestro querido conserje cierre el aula.
51
Ya que tenemos los Advice lo ideal sera poder darles uso, para ello crearemos puntos de corte. Los puntos de corte seleccionan uno o ms puntos de unin donde debe aplicarse una notificacin mediante un aspecto. Para ello hay distintas maneras. Los puntos de corte seleccionan el qu se va a notificar, en el caso de Spring a qu mtodo se va a notificar. Declaraciones con expresiones regulares (Regular Expressions Pointcuts)
En Spring se disponen de dos clases que soportan expresiones regulares para la definicin de puntos de corte ellas son Per15RegexpMethodPointcut y JdkRegexMethodPointcut. La primera requiere Jakarta ORO y ejecutarse en un jre anterior a 1.4 de Java. La definicin sera as:
<bean id="watchmanPointcut" class="org.springframework.aop.support.JdkRegexpMethodPointcut"> <property name="pattern" value=".*teach"/> </bean>
La propiedad pattern es la expresin regular con la que diremos qu mtodos queremos que sean un punto de corte. En nuestro caso sern todos aquellos que se llamen teach ya sean de cualquier clase. En el bean watchmanAdvisor estamos definiendo que punto de corte tendr la notificacin.
<bean id="watchmanAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor"> <property name="advice" ref="watchmanAdvice"/> <property name="pointcut" ref="watchmanPointcut"/> </bean>
Pero esto seguramente acabe siendo muy repetitivo y acabaremos teniendo un bean patrn para cada notificacin. No hay que preocuparse, porque Spring tiene una clase que soluciona tanto cdigo xml. La clase org.springframework.aop.support.RegexpMethodPointcutAdvisor nos lo soluciona.
<bean id="watchmanAdvisor2" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <property name="advice" ref="watchmanAdvice"/> <property name="pattern" value=".*teach"/> </bean>
A diferencia de las expresiones regulares, el lenguaje de puntos de corte AspectJ se defini especialmente para los puntos de corte. As tiene una sintaxis especial. Si se prefiere usar expresiones de tipo AspectJ se usar la clase AspectJExpressionPointcut para definir los puntos de corte.
Si se quiere reducir el cdigo xml, al igual que pasaba con RegexpMethodPointcutAdvisor existe una clase en la que podremos definir notificacin y punto de corte, sta es AspectJExpressionPointcutAdvisor.
Como hemos dicho la definicin es igual a la que hicimos salvo que hemos modificado el nombre del bean. En vez de llamarlo aurelioMartinez, lo hemos llamado aurelioMartinezTarget. Esto es debido a que el bean que se le pedir al contenedor no ser la implementacin de profesor que tenemos, sino el proxy que hemos creado para la ocasin. El siguiente cdigo ilustra lo comentado.
<bean id="aurelioMartinez" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target" ref="aurelioMartinezTarget"/> <property name="interceptorNames" value="watchmanAdvisor"/> <property name="proxyInterfaces" value="es.uah.uahaop.model.Model" /> </bean>
En esta definicin se utiliza la clase ProxyFactoryBean a la cual se le ha de pasar el destinatario (target), las posibles notificaciones (interceptorNames) y los interfaces que el destinatario implementa.
53
Figura 1: Lo que realmente est pasando es que la clase que instancie el bean aurelioMartinez cuando ejecute su mtodo teach() Spring interceptar esa llamada gracias al proxy y se le entregar a la notificacin antes de la ejecucin del mtodo.
Como vemos en el cdigo quin es aurelioMartinez es el proxy, no la clase de tipo profesor. Lo que ocurrir es que cuando una clase pida al contenedor de Spring el bean aurelioMartinez ste le dar el proxy y no el destinatario, target u objetivo. Como seguramente haya pensado, habr bastantes profesores y cada uno de ellos tendr la misma definicin en el xml, con lo que hay algo que nos permita definir todos aquellos beans de tipo target que respondan al mismo patrn? La respuesta es s, utilizando beans abstractos.
Abstraccin de ProxyFactoryBean
Como hemos dicho, cuando en una aplicacin se tienen varios destinatarios de la misma clase, stos tienen una configuracin similar en el xml. Para eliminar cdigo xml existe la posibilidad de hacerlo con ProxyFactoryBean como un bean abstracto. La definicin de ste ser la siguiente:
<bean id="watcherProxyBase" abstract="true" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="interceptorNames" value="watchmanAdvisor"/> <property name="proxyInterfaces" value="es.uah.uahaop.model.Model" /> </bean>
As crearemos la base del bean abstracto con el que luego instanciaremos los proxy de una manera ms sencilla.
<bean id="julianRebolloTarget" class="es.uah.uahaop.model.TeacherImp"> <constructor-arg value="Julian Rebollo"/> </bean> <bean id="adolfoRenatoTarget" class="es.uah.uahaop.model.TeacherImp"> <constructor-arg value="Adolfo Renato"/>
</bean> <bean id="julianRebollo" parent="watcherProxyBase"> <property name="target" ref="julianRebolloTarget"/> </bean> <bean id="adolfoRenato" parent="watcherProxyBase"> <property name="target" ref="adolfoRenatoTarget"/> </bean>
Renombrar a los beans que en principio seran los que la aplicacin instanciar aadindoles la palabra target (es decir julianRebollo por julianRebolloTarget) y luego utilizar la clase proxy en su lugar es un concepto confuso y que cuesta acostumbrarse. Con el autoproxying Spring crea las conexiones necesarias sin tener que confundirnos con los nombres (como ya vimos en el captulo primero).
Autoproxying
Spring da soporte a la conversin en proxy de bean. El Autoproxying nos ofrece una implementacin AOP ms completa dejando que la definicin de punto de corte de un aspecto decida qu beans sern proxy. Se pueden hacer de dos maneras. La primera con el autoproxying bsico de beans basado en beans notificadores declarados en el contexto Spring y la otra con anotacin AspectJ.
La propiedad advice dice que notificacin aplicar y la propiedad expression dice dnde aplicarla. Spring tiene una implementacin de BeanPostProccessor que crea automticamente beans proxy con notificadores coincidentes llamada DefaultAdvisorProxyCreator, cuya declaracin es la siguiente.
<bean class="org.springframework.aop.framework.autoproxy. DefaultAdvisorAutoProxyCreator"/>
Este bean no tiene id, eso es debido a que nunca nos referiremos a l. Despus de esto ya no necesitamos declarar ProxyFactoryBean ni poner otro nombre al target. Esto funcionar en entornos java inferiores a 5. Pero si nuestra aplicacin est en Java 5 puede utilizar anotacin de AspectJ.
55
@AspectJ
Con las anotaciones de @AspectJ se pueden trasformar POJOs normales en aspectos. Volvamos a la clase conserje (watchman), en ella podemos hacer las anotaciones oportunas.
package es.uah.uahaop.model; import import import import import org.aspectj.lang.annotation.AfterReturning; org.aspectj.lang.annotation.AfterThrowing; org.aspectj.lang.annotation.Aspect; org.aspectj.lang.annotation.Before; org.aspectj.lang.annotation.Pointcut;
@Aspect public class WatchmanImp implements Watchman { private String name; public WatchmanImp(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Pointcut("execution (* *.teach(..))") public void teaching() { } @Before("teaching()") public void openClassRoom(Teacher teacher) { Subject subject = teacher.getSubject(); ClassRoom classRoom = subject.getClassRoom(); System.out.println("El conserje " + getName() + " abre la puerta del aula " + classRoom.getName() + " para que pueda dar clase el profesor " + teacher.getName() + " de " + subject.getName()); classRoom.setOpen(true); } @AfterReturning("teaching()") public void closeClassRoom(Teacher teacher) { Subject subject = teacher.getSubject(); ClassRoom classRoom = subject.getClassRoom(); System.out.println("El conserje " + getName() + " cierra la puerta del aula " + classRoom.getName() + " al haber acabado la clase el profesor " + teacher.getName() + " de " + subject.getName()); classRoom.setOpen(false); } @AfterThrowing("teaching()") public void teacherDidNotCome(Teacher teacher) { Subject subject = teacher.getSubject(); ClassRoom classRoom = subject.getClassRoom(); System.out.println("El conserje " + getName() + " cierra la puerta del aula " + classRoom.getName() + " al no haber venido el profesor " + teacher.getName() + " de " +
subject.getName()); classRoom.setOpen(false); } }
La anotacin @Aspect indica que es un aspecto, no un simple POJO. @Pointcut y el mtodo teaching aadido marcan el punto de corte. Los mtodos openClassRoom y closeClassRoom han sido anotados para que se ejecuten antes y despus del mtodo teach y el mtodo teacherDidNotCome se le ha hecho una anotacin para que se ejecute si hay una excepcin.
Para que esto funcione se ha de crear una clase creadora de AutoProxy en el contendor de Spring. Esta clase se llama AnnotationAwareAspectJAutoProxyCreator y es la encargada de convertir clases con anotaciones @AspectJ en notificaciones proxy. En vez de registrarlo como un bean, Spring da la oportunidad de hacerlo de una manera sencilla:
<aop:aspectj-autoproxy/>
Con esta etiqueta crearemos la clase AnnotationAwareAspectJAutoProxyCreator en el contexto y automticamente crear proxy cuyos mtodos correspondan a los puntos de corte definidos por las anotaciones @Pointcut.
Si no incluimos el namespace aop nada de esto funcionar, as que, como ya hicimos en el primer captulo incluiremos la siguiente definicin de la etiqueta beans:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
Tambin AnnotationAwareAspectJAutoProxyCreator hace lo mismo que DefaultAdvisorAutoProxyCreator. Existe tambin la posibilidad de crear una notificacin alrededor, sta sera con la anotacin @Around(metodoAEjecutar()) y definiramos todo de la misma manera que con la anterior anotacin.
57
podemos convertir cualquier clase en un aspecto. Por fin hemos llegado al punto en el que dejamos el primer captulo, as que escribiremos de nuevo la definicin que hicimos en el xml.
<aop:config> <aop:aspect ref="julian"> <aop:pointcut id="classRoomDoorToTeach" expression="execution(* *.teach(..)) and target(bean)" /> <aop:before method="openClassRoom" pointcut-ref="classRoomDoorToTeach" arg-names="bean" /> <aop:after-returning method="closeClassRoom" pointcut-ref="classRoomDoorToTeach" arg-names="bean" /> </aop:aspect> </aop:config>
Utilidad Configura un notificador Configura una notificacin antes de la ejecucin. Configura una notificacin posterior (haya habido xito en la ejecucin o no) Configura una notificacin alrededor Configura una notificacin de excepcin Configura una notificacin despus del retorno Punto de corte definido Definicin de un aspecto, etiqueta contenida en <aop:config> Elemento padre de las etiquetas aop
<aop:config>
Cabe destacar que a pesar de tener un gran motor AOP, como ya hemos dicho, Spring est basado en proxy y las notificaciones han de ser de mtodo a pesar de que comparta la anotacin @Aspect. Si necesitamos algo ms potente deberemos crear aspectos con AspectJ. Spring permite inyectar beans creados con AspectJ y hacer uso de ellos.
59
Captulo 4: Persistencia
Peticiones a bases de datos, persistencia y patrones DAO
Hoy en da, la construccin de cualquier aplicacin empresarial es inviable sin una base de datos. La informacin con la que el sistema informtico trabaja es de vital importancia y de uso continuado. Es por ello que debemos definir mecanismos que simplifiquen y aseguren la consistencia de los datos. Javier Sevilla Snchez
61
Contenido
Spring y el acceso a datos ........................................................................................................... 62 Patrn DAO.............................................................................................................................. 62 Excepciones en Spring ............................................................................................................. 63 Plantillas ...................................................................................................................................... 64 Uso de clases de soporte DAO ................................................................................................ 65 Configuracin de una fuente de datos ........................................................................................ 66 Configuracin JDBC ................................................................................................................. 66 Uso de plantillas JDBC ................................................................................................................. 67 JdbcTemplate .......................................................................................................................... 67 Utilizar parmetros nombrados .............................................................................................. 70 Simplificacin de JDBC ............................................................................................................ 70 Clases de Spring para trabajar con JDBC ..................................................................................... 72 Plataformas de persistencia en Spring ........................................................................................ 73 Integracin de Hibernate en Spring ............................................................................................ 73 Instanciar SessionFactory de Hibernate .................................................................................. 74 LocalSessionFactoryBean ........................................................................................................ 74 AnnotationSessionFactoryBean .............................................................................................. 74 HibernateTemplate ................................................................................................................. 75 HibernateDaoSupport ......................................................................................................... 76 JPA (Java Persistence API) ........................................................................................................... 78 Configurar EntityManagerFactory........................................................................................... 78 Configurar LocalEntityManagerFactoryBean ...................................................................... 78 Configurar LocalContainerEntityManagerFactoryBean .......................................................... 79 Plantillas JPA (JpaTemplate) .................................................................................................... 80 Extender de JpaDaoSupport para construir un DAO............................................................... 81
Patrn DAO
El problema que viene a resolver este patrn es el de contar con diversas fuentes de datos (base de datos, archivos, servicios externos, etc.). De tal forma que se encapsula la forma de acceder a la fuente de datos. Este patrn surgi de la necesidad de gestionar una diversidad de fuentes de datos, aunque su uso se extiende al problema de encapsular no slo la fuente de datos, sino adems ocultar la forma de acceder a los datos. Se trata de que el software cliente se centre en los datos que necesita y se olvide de cmo se realiza el acceso a los datos o de cul es la fuente de almacenamiento. Las aplicaciones pueden utilizar el API JDBC para acceder a los datos de una base de datos relacional. Este API permite una forma estndar de acceder y manipular datos en una base de datos relacional. El API JDBC permite a las aplicaciones JEE utilizar sentencias SQL, que son el mtodo estndar para acceder a tablas y vistas. La idea de este patrn es ocultar la fuente de datos y la complejidad del uso de JDBC a la capa de presentacin o de negocio. Un DAO define la relacin entre la lgica de presentacin y empresa por una parte y por otra los datos. El DAO tiene un interfaz comn, sea cual sea el modo y fuente de acceso a datos. Los DAO existen para proporcionar un medio para leer y escribir datos en una base de datos. Deberan exponer esta funcionalidad mediante una interfaz por la que el resto de la aplicacin accediera a ellos.
63
Figura 2 Los objetos de servicio delegan el acceso a datos a los DAO. La interfaz DAO mantiene el acoplamiento dbil.
Los objetos de servicio acceden mediante interfaces DAO. As los objetos de servicio son comprobables y no estn acoplados a una nica implementacin.
Excepciones en Spring
Al escribir cdigo JDBC estamos obligados a capturar excepciones SQLException. sta ha podido producirse por varias razones, que deberamos estar averiguando. Otro de los problemas es que la mayora de las excepciones responden a una situacin fatal que no se puede tratar en el bloque catch, ya que no poco se puede hacer. Spring es consciente de que poco se puede hacer, as que hace que no tengamos que atrapar la excepcin.
JDBC de Spring proporciona una jerarqua ms amplia de excepciones con las que podemos resolver ms problemas ya que son ms descriptivas.
Excepciones de DB Spring CannotAcquierLockException CannotSerializeTtransactionException CleanupFailureDataAccessException ConcurrencyFailureDataAccessException DataAccessException DataAccesResourceFailureException DataIntegrityViolationException DataRetrievalFailureException DeadlockLoserDataAccessException
Todas las excepciones tienen como padre a DataAccessException la cual es una excepcin sin comprobacin, con lo que no se tiene por qu atrapar nada si no se desea. S, Spring prefiere dejar libre al programador la eleccin de si quiere capturarla o no, ya que en otras ocasiones ste se ve obligado a capturar un sinfn de bloques try-catch que a menudo se dejan vacos.
Plantillas
Un mtodo de plantilla define el esqueleto de un proceso que es fijo en una operacin. As las plantillas se responsabilizan de una serie de acciones comunes y devuelve el control a la retrollamada. Spring as separa las partes fijas y variables del proceso de acceso a datos en dos clases distintas: las plantillas y las retrollamadas.
Figura 3 Las plantillas se responsabilizan de las tareas comunes de acceso a datos. Para las especficas se usa el objeto retrollamada.
Las plantillas gestionan las partes fijas del acceso a datos, controlan las excepciones, asignan los recursos y manejan las transacciones. Spring tiene varias plantillas dependiendo de la
65
persistencia que vayamos a usar. Usar una plantilla simplifica mucho el cdigo de acceso a datos y se configura como un bean del contexto de Spring. Plantilla CciTemplate JdbcTemplate NamedParameterJdbcTemplate SimpleJdbcTemplate HibernateTemplate HibernateTemplate (orm.hibernate3.*) SqlMapClientTemplate JdoTemplate JpaTemplate TopLinkTemplate
Conexiones JCA CCI Conexiones JDBC JDBC con soporte para nombrados JDBC simplificadas Hibernate 2 Hibernate 3 iBATIS SqlMap JDO JPA Java Persistence Api TopLink de Oracle
parmetros
Tambin se pueden usar las clases DAO de Spring para simplificar ms la aplicacin. Hay clases bsicas DAO de Spring que pueden gestionar la plantilla por nosotros.
Figura 4 La relacin entre un DAO de aplicacin y el soporte DAO de Spring y las clases plantilla
Spring ofrece varias clases de soporte DAO. Cada plantilla tendr su soporte DAO de Spring. En la siguiente tabla se enumeran. SOPORTE DAO CciDaoSupport JdbcDaoSupport NamedParameterJdbcDaoSupport HibernateDaoSupport HibernateDaoSupport (orm.hibernate3.support) SqllMapClientDaoSupport JdoDaoSupport JpaDaoSupport TopLinkDaoSupport TIPO JCA CCI JDBC JDBC con nombrados Hibernate 2 Hibernate 3 iBatis JDO JPA TopLink
soporte
para
parmetros
A pesar de la cantidad de frameworks de persistencia con los que Spring puede trabajar empezaremos con lo ms sencillo, una conexin JDBC. Antes veamos cmo se configura una fuente de datos.
Como vemos hemos de configurar las propiedades url que ser la url de acceso a la base de datos, el usuario y su contrasea as como qu driver usar, en nuestro caso el driver de MySql.
67
JdbcTemplate
Es la implementacin ms sencilla, permite el acceso a datos mediante JDBC y consultas sencillas de parmetros indexados. Tan slo necesita un DataSource para funcionar as que el cdigo xml ser muy sencillo. Utilizaremos el DataSource definido anteriormente:
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSourceJDBC"/> </bean>
La propiedad dataSource puede ser cualquier implementacin de javax.sql.DataSource. Interfaz persona y su implementacin Para poder llevar a cabo el ejemplo crearemos una interfaz sencilla de una persona que tenga mtodos de obtener y asignar tanto un id como un nombre.
public interface Person { String getName(); void setName(String name); int getId(); void setId(int id); }
private int id; private String name; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
sta clase tan sencilla nos servir para poder ilustrar la importante labor de los DAO. Primero definamos un interfaz DAO:
public interface PersonDao { Person getPerson(int personID); List<Person> getAllPerson(); void savePerson (Person person); }
69
public Object mapRow(ResultSet rs, int rowNum) throws SQLException, DataAccessException { Person person = new PersonImp(); person.setId(rs.getInt(1)); person.setName(rs.getString(2)); return person; } }); return matches.size() > 0 ? (Person) matches.get(0) : null; } public void savePerson(Person person) { jdbcTemplate.update(PERSON_INSERT, new Object[]{ new Integer(person.getId()), person.getName()}); } public List<Person> getAllPerson() { List matches = jdbcTemplate.query(PERSON_SELECT, new RowMapper() { public Object mapRow(ResultSet rs, int rowNum) throws SQLException, DataAccessException { Person person = new PersonImp(); person.setId(rs.getInt(1)); person.setName(rs.getString(2)); return person; } }); return matches; } }
Como vemos esta clase se sirve de una plantilla JDBC, la cual inyectaremos desde el fichero de definicin de Spring con el bean que hemos definido anteriormente. La definicin sera as de sencilla:
<bean id="jdbcPersonDao" class="es.uah.jdbcspringexample.dao.JdbcPersonDao"> <constructor-arg ref="jdbcTemplate"/> </bean>
Analizando el mtodo savePerson vemos que es muy intuitivo y sencillo, gracias a la plantilla se inserta una cadena que contiene una sentencia SQL y un Array de objetos que responden a cada una de las interrogaciones de la sentencia. La obtencin de datos, en los mtodos getPerson y getAllPerson son tambin sencillas, es clave el uso del objeto RowMapper y el uso de la plantilla del mtodo query. Vemos que el mtodo query toma por parmetro la propia query en cadena, una matriz con los objetos que insertara en cada ? que se encontrar en la cadena y un objeto RowMapper. El objeto RowMapper es abstracto y as implementamos el mtodo abstracto rowMap. ste mtodo se ejecutar una vez por fila devuelta en la base de datos.
sta implementacin est muy bien, pero an hay pegas. Para el mtodo savePerson se utilizan parmetros que han de llevar un orden con lo que si algn da hay algn cambio tendremos que tener en cuenta este orden.
Es decir, especificaremos cada nombre correspondiente del objeto que vayamos a guardar. Pero esto no es compatible con JdbcTemplate, as que crearemos una nueva clase que en vez de usar un JdbcTemplate use un NamedParameterJdbcTemplate. La clase NamedParameterJdbcPersonDao hace es un ejemplo y su definicin en el fichero xml es la siguiente.
<bean id="namedJdbcTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate"> <constructor-arg ref="dataSourceJDBC"/> </bean> <bean id="namedParameterJdbcPersonDao" class="es.uah.jdbcspringexample.dao.NamedParameterJdbcPersonDao"> <constructor-arg ref="namedJdbcTemplate"/> </bean>
Para ello hemos tenido que definir otra plantilla de la clase NamedParameterJdbcTemplate a cuyo constructor hemos de pasarle la fuente de datos definida.
Simplificacin de JDBC
A partir de la versin 5 de java es posible pasar listas de parmetros de longitud variable a un mtodo. As la clase SimpleJdbcPersonDao la codificaremos de la siguiente manera:
public class SimpleJdbcPersonDao implements PersonDao { private SimpleJdbcTemplate simpleJdbcTemplate; private static final String PERSON_INSERT = "insert into person (id, name) " + "values (?,?)"; private static final String PERSON_SELECT = "select id, name from person "; private static final String PERSON_SELECT_ID = PERSON_SELECT + " where id=?"; public SimpleJdbcPersonDao(SimpleJdbcTemplate simpleJdbcTemplate) { this.simpleJdbcTemplate = simpleJdbcTemplate;
71
} public void setJdbcTemplate(SimpleJdbcTemplate simpleJdbcTemplate) { this.simpleJdbcTemplate = simpleJdbcTemplate; } public SimpleJdbcTemplate getSimpleJdbcTemplate() { return simpleJdbcTemplate; } public Person getPerson(int personID) { List matches = simpleJdbcTemplate.query(PERSON_SELECT_ID, new ParameterizedRowMapper() { public Person mapRow(ResultSet rs, int rowNum) throws SQLException { Person person = new PersonImp(); person.setId(rs.getInt(1)); person.setName(rs.getString(2)); return person; } }, personID); return matches.size() > 0 ? (Person) matches.get(0) : null; } public void savePerson(Person person) { simpleJdbcTemplate.update(PERSON_INSERT, person.getId(), person.getName()); } public List<Person> getAllPerson() { List matches = simpleJdbcTemplate.query(PERSON_SELECT, new ParameterizedRowMapper() { public Object mapRow(ResultSet rs, int rowNum) throws SQLException, DataAccessException { Person person = new PersonImp(); person.setId(rs.getInt(1)); person.setName(rs.getString(2)); return person; } }); return matches; } }
Como vemos la manera de utilizar esta plantilla es muy similar a la JdbcTemplate salvo que ahora nos beneficiamos de las nuevas especificaciones del lenguaje en la versin 5. En el mtodo getPerson vemos que los parmetros del mtodo query del objeto SimpleJdbcTemplate relativos a la formulacin de la query (es decir qu parmetros se utilizaran a la hora de ser sustituidos por cada ?) se podrn como los ltimos parmetros no teniendo lmite y pudiendo ser primitivos (no como en el ejemplo de JdbcPersonDao en el cual tenamos que crear objetos Integer), es decir, poder hacer autobox. Como vemos tambin se usa un nuevo objeto abstracto, el ParametrizedRowMapper, del cual tendremos que implementar el mtodo mapRow(). La definicin del bean en el fichero xml es la siguiente:
<bean id="simpleJdbcTemplate" class="org.springframework.jdbc.core.simple.SimpleJdbcTemplate"> <constructor-arg ref="dataSourceJDBC"/> </bean> <bean id="simpleJdbcPersonDao" class="es.uah.jdbcspringexample.dao.SimpleJdbcPersonDao"> <constructor-arg ref="simpleJdbcTemplate"/> </bean>
public class JdbcPersonDao extends JdbcDaoSupport implements PersonDao{ private static final String PERSON_INSERT = "insert into person (id, name) " + "values (?,?)"; private static final String PERSON_SELECT = "select id, name from person "; private static final String PERSON_SELECT_ID = PERSON_SELECT + " where id=?"; public Person getPerson(int personID) { List matches = getJdbcTemplate().query(PERSON_SELECT_ID, new Object[]{Long.valueOf(personID)}, new RowMapper() { public Object mapRow(ResultSet rs, int rowNum) throws SQLException, DataAccessException { Person person = new PersonImp(); person.setId(rs.getInt(1)); person.setName(rs.getString(2)); return person; } }); return matches.size() > 0 ? (Person) matches.get(0) : null; } public void savePerson(Person person) { getJdbcTemplate().update(PERSON_INSERT, new Object[]{ new Integer(person.getId()), person.getName()}); } public List<Person> getAllPerson() { List matches = getJdbcTemplate().query(PERSON_SELECT, new RowMapper() { public Object mapRow(ResultSet rs, int rowNum) throws SQLException, DataAccessException { Person person = new PersonImp(); person.setId(rs.getInt(1));
73
Con esto hemos eliminado la tarea de crear mtodos getter y setter para la plantilla. De hecho ni siquiera tendremos que inyectar una plantilla, sino que si inyectamos una fuente de datos el objeto la crear automticamente.
Al igual que los anteriores ejemplos (Parmetros nombrados y simplificacin de JDBC) tambin existen clases abstractas de soporte en Spring con estas plantillas. Estas son NamedParameterJdbcDaoSupport y SimpleJdbcDaoSupport.
LocalSessionFactoryBean
El objeto LocalSessionFactoryBean de Spring es un bean factory de Spring que produce una instancia local de SessionFactory de Hibernate que toma sus parmetros de mapeo de los archivos XML. La definicin de este bean sera la siguiente:
<bean id="localSessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="mappingResources" > <list> <value>es/uah/hibernateaddendum/model/Exam.hbm.xml</value> <value>es/uah/hibernateaddendum/model/Student.hbm.xml</value> <value>es/uah/hibernateaddendum/model/Subject.hbm.xml</value> <value>es/uah/hibernateaddendum/model/SubjectUnit.hbm.xml</value> </list> </property> <property name="hibernateProperties"> <props> <prop key="hibernte.dialect">${hibernate.dialect}</prop> </props> </property> </bean>
La propiedad mappingResources define una lista con una entrada por fichero hbm.xml que definamos para nuestro mapeo objeto relacional. La propiedad hibernateProperties define propiedades necesarias para la configuracin de Hibernate.
AnnotationSessionFactoryBean
Si lo que deseamos es utilizar objetos cuyas clases implementen las anotaciones JPA as como especficas de Hibernate AnnotationSessionFactoryBean es la clase que deberemos usar. Es muy similar a LocalSessionFactoryBean salvo que en vez de hacer una lista con los ficheros hbm.xml la haremos con cada objeto que implemente anotaciones.
<bean id="annotationSessionFactoryBean" class="org.springframework.orm.hibernate3.annotation. AnnotationSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="annotatedClasses" > <list> <value>es.uah.hibernateaddendum.model.Exam</value> <value>es.uah.hibernateaddendum.model.Student</value> <value>es.uah.hibernateaddendum.model.Subject</value> <value>es.uah.hibernateaddendum.model.SubjectUnit</value> </list>
75
Como vemos la definicin es muy similar a LocalSessionFactoryBean salvo que tenemos la propiedad annotatedClasses cuya lista se confecciona con las clases con anotaciones persistentes.
HibernateTemplate
Esta plantilla simplifica trabajar con el objeto Session de Hibernate, siendo responsable de abrir y cerrar sesiones y gestionar excepciones principalmente. Para crear la plantilla le tendremos que pasar una instancia de la factora de sesin de Hibernate de cualquiera de las maneras vistas. El siguiente XML muestra cmo se configura HibernateTemplate en Spring:
<bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate"> <property name="sessionFactory" ref="localSessionFactory" /> </bean>
Ambas definiciones son idnticas, cabe sealar que en atributo sessionFactory podremos pasarle cualquier clase que implemente FactoryBean. Una vez hecho todo esto estamos en situacin de crear un DAO que haga uso de la plantilla y as poder persistir y recuperar objetos. El siguiente cdigo pertenece al DAO HibernateExamsDaoImp y sirve de ejemplo de cmo se hace uso de la plantilla:
public class HibernateExamsDaoImp { private private private private private static final String INSERT = "insert"; static final String DELETE = "delete"; static final String GET_EXAM = "getExam"; static final String EXAM = Exam.class.getName(); static final String SELECT_ID = "from " + EXAM + " where id = ?"; private static final String SELECT_ALL = "from " + EXAM; private HibernateTemplate hibernateTemplate; public HibernateTemplate getHibernateTemplate() { return hibernateTemplate; }
public void setHibernateTemplate(HibernateTemplate hibernateTemplate) { this.hibernateTemplate = hibernateTemplate; } public void update(Exam exam) { getHibernateTemplate().saveOrUpdate(exam); } public void insert(Exam exam) { getHibernateTemplate().saveOrUpdate(exam); } public void insert(Set<Exam> exams) throws ExamsException { for (Iterator<Exam> it = exams.iterator(); it.hasNext();) { this.insert(it.next()); } } public Exam getExam(int id) throws ExamsException { List results = getHibernateTemplate().find(SELECT_ID, id); if (results.size() == 0) { return (Exam) results.get(0); } throw new ExamNotFoundException(id); } public Collection<Exam> getAllExams() { return getHibernateTemplate().find(SELECT_ALL); } public void delete(int id) throws ExamsException { getHibernateTemplate().delete(this.getExam(id)); } }
As las operaciones montonas necesarias para operar con Hibernate sern controladas por la plantilla. La definicin dentro del contenedor de Spring es la siguiente:
<bean id="hibernateExamsDaoImp" class="es.uah.hibernatespringintegration.dao.HibernateExamsDaoImp"> <property name="hibernateTemplate" ref="hibernateTemplate" /> </bean>
HibernateDaoSupport
Al igual que hemos visto con JdbcDaoSupport, Spring tambin facilita la construccin de DAO en Hibernate. La clase HibernateDaoSupport otorga una plantilla HibernateTemplate con lo que slo le tendremos que inyectar un objeto SessionFactory. El siguiente cdigo pertenece a la clase HibernateStudentsDaoImp que como vemos hereda de HibernateDaoSupport:
public class HibernateStudentsDaoImp extends HibernateDaoSupport { private static final String STUDENT = Student.class.getName();
77
private static final String SELECT_ID = "from " + STUDENT + " where id = ?"; private static final String SELECT_ALL = "from " + STUDENT; public void update(Student student) { getHibernateTemplate().saveOrUpdate(student); } public void insert(Student student) { getHibernateTemplate().saveOrUpdate(student); } public void insert(Set<Student> students) throws StudentsException { for (Iterator<Student> it = students.iterator(); it.hasNext();) { this.insert(it.next()); } } public Student getStudent(int id) throws StudentsException { List results = getHibernateTemplate().find(SELECT_ID, id); if (results.size() == 0) { return (Student) results.get(0); } throw new StudentNotFoundException(id); } public Collection<Student> getAllStudents() { return getHibernateTemplate().find(SELECT_ALL); } public void delete(int id) throws StudentsException { getHibernateTemplate().delete(this.getStudent(id)); } }
Con las clases de soporte Spring facilita al mximo la codificacin de nuevas clases, reduciendo el nmero de lneas de cdigo de estas y reducindolas a la mnima expresin.
Configurar EntityManagerFactory
Para usar JPA se ha de usar una implementacin de EntityManagerFactory para obtener una instancia de un EntityManager. La especificacin JPA define dos tipos de gestores de entidad, gestionados por la aplicacin o por el contenedor. Los gestionados por la aplicacin se crean cuando la aplicacin directamente pide un gestor de entidad de una fbrica de gestor de entidad. Cuando sta se usa es la aplicacin la responsable de abrir y cerrar los gestores de entidad y de implicar al gestor de entidad en las transacciones. Sin embargo, si utilizamos un contenedor Java EE la aplicacin no interacta en absoluto con la fbrica de gestor de entidad y es por ello por lo que debemos usar gestores de entidad gestionados por el contenedor. As los obtendremos mediante JNDI o mediante inyeccin. Ambos gestores implementan EntityManager. Para los desarrolladores, al utilizar la interfaz, no tendremos que saber en muchos casos ni siquiera cul estamos utilizando. Spring gestionar esto por nosotros. Para ello Spring tiene dos beans, LocalEntityManagerFactoryBean y LocalContainerEntityManagerFactoryBean, siendo la primera gestionada por la aplicacin y la segunda por el contenedor. Ya que como hemos dicho Spring hace trasparente el acceso a ambas, la nica diferencia ser la forma en la que las definiremos.
Configurar LocalEntityManagerFactoryBean
En el archivo persistence.xml se hallarn la mayor parte de la configuracin. En este fichero definimos tantas unidades de persistencia como queramos, enumerando las clases persistentes.
79
<?xml version="1.0" encoding="UTF-8"?> <persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0"> <persistence-unit name="CbosCommonsPU" transaction-type="RESOURCE_LOCAL"> <provider>org.apache.openjpa.persistence.PersistenceProviderImpl</provider> <properties> <property name = "openjpa.ConnectionDriverName" value = "org.postgresql.Driver"/> <property name = "openjpa.ConnectionURL" value = "jdbc:postgresql://192.168.1.214:5432/arcadia_cbos"/> <property name = "openjpa.ConnectionUserName" value = "postgres"/> <property name = "openjpa.ConnectionPassword" value = "postavalon"/> </properties> </persistence-unit> </persistence>
Ms tarde definiremos el bean en el fichero de Spring y le deberemos pasar la unidad de persistencia como parmetro.
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean"> <property name="persistenceUnitName" value="youdiversity" /> <property name="jpaVendorAdapter"> <bean class="org.springframework.orm.jpa.vendor.TopLinkJpaVendorAdapter"> <property name="showSql" value="true" /> <property name="generateDdl" value="true" /> <property name="database" value="HSQL" /> </bean> </property> </bean>
Configurar LocalContainerEntityManagerFactoryBean
Lo que haremos ser obtener una EntityManagerFactory utilizando la informacin proporcionada por el contendor. Esta es la configuracin tpica cuando usamos JBoss o WebLogic, es decir, servidores de aplicacin JEE. El siguiente cdigo muestra LocalContainerEntityManagerFactoryBean. cmo configurar un bean
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="dataSource" ref="youdiversityDataSource" /> <property name="jpaVendorAdapter"> <bean class="org.springframework.orm.jpa.vendor.TopLinkJpaVendorAdapter"> <property name="showSql" value="true" /> <property name="generateDdl" value="true" /> <property name="database" value="HSQL" /> </bean>
</property> </bean>
Como vemos hemos tenido que configurar un bean interno de tipo TopLinkJpaVendorAdapter, eso es porque la propiedad jpaVendorAdapter puede tener varios valores y hace referencia a la implementacin de JPA a utilizar.
La propiedad entityManagerFactory de JpaTemplate debe conectarse con una implementacin de la interfaz javax.persistence.EntityManagerFactory de JPA. JpaTemplate al igual que otras plantillas tiene muchos de los mtodos de acceso a datos ofrecidos por un EntityManager nativo. Pero implica a los EntityManager en transacciones y maneja excepciones. El siguiente DAO es un ejemplo de cmo se utiliza JpaTemplate:
import org.springframework.orm.jpa.JpaTemplate; ... public class UserJpaDao{ private JpaTemplate jpaTemplate; public JpaTemplate getJpaTemplate() { return jpaTemplate; } public void setJpaTemplate(JpaTemplate jpaTemplate) { this.jpaTemplate = jpaTemplate; } ... }
81
83
84
85
Contenido
Qu es Spring MVC? .................................................................................................................. 87 Caractersticas de Spring MVC .................................................................................................... 87 DispatcherServlet ........................................................................................................................ 88 Configuracin de DispatcherServlet y ContextLoaderListener ............................................... 89 Usando Spring MVC sin anotaciones........................................................................................... 91 Creando nuestro primer Controlador ..................................................................................... 92 El objeto ModelAndView ........................................................................................................ 93 Configuracin del controlador ................................................................................................ 93 View Resolvers ........................................................................................................................ 93 Crear un JSP ............................................................................................................................. 94 Pasos de la peticin ..................................................................................................................... 94 Usando Spring MVC con Anotaciones y Con Spring 3 ................................................................. 95 Definir el controlador en el fichero *-servlet.xml. .................................................................. 96 Mapeando peticiones con @RequestMapping ....................................................................... 96 Peticiones URL, usando @RequestMapping ....................................................................... 97 Mapeo avanzado con @RequestMapping .......................................................................... 98 Tipos devueltos soportados .............................................................................................. 100 Enlazando parmetros de la peticin a parmetros de mtodos con @RequestParam ...... 101 @RequestBody ...................................................................................................................... 102 @ResponseBody ................................................................................................................... 102 HttpEntity .............................................................................................................................. 102 Proporcionando un enlace a los datos del modelo con @ModelAttribute .......................... 103 Especificando atributos para almacenar en la sesin ........................................................... 103 Uso de cookies con @CookieValue ....................................................................................... 104 Mapeando cabeceras de peticiones con @RequestHeader ................................................. 104 Personalizando el enlace a Datos.......................................................................................... 105 Mapeo de controladores........................................................................................................... 105 Interceptando peticiones con HandlerInterceptor ............................................................... 106 Resolver vistas ........................................................................................................................... 106 ViewResolver ......................................................................................................................... 106 Encadenando ViewResolvers ................................................................................................ 107 Redireccionando a vistas ....................................................................................................... 107 RedirectView ..................................................................................................................... 107
El prefijo redirect: .......................................................................................................... 108 El prefijo forward: .......................................................................................................... 108 ContentNegotiatingViewResolver ......................................................................................... 108 Configuracin regional .............................................................................................................. 110 AcceptHeaderLocaleResolver................................................................................................ 110 CookieLocaleResolver ........................................................................................................... 110 SessionLocaleResolver .......................................................................................................... 110 LocaleChangeInterceptor ...................................................................................................... 111 Uso de temas............................................................................................................................. 111 Definiendo los temas ............................................................................................................ 111 Resolutores de tema ............................................................................................................. 112 Subida de ficheros ..................................................................................................................... 112 Subiendo un fichero desde un formulario ............................................................................ 113 Manejo de Excepciones............................................................................................................. 114 @ExceptionHandler .............................................................................................................. 115 Convenios .................................................................................................................................. 116
87
Qu es Spring MVC?
La arquitectura MVC (Modelo vista controlador) se basa en la separacin de los datos y modelo de la aplicacin (Modelo), la interfaz de usuario (comnmente un navegador que recibe cdigo HTML) y la interaccin entre ambos, el controlador. En una aplicacin MVC, la gestin de estado, la validacin y el flujo de trabajo son temas fundamentales y principal foco de atencin. Debido a la naturaleza del protocolo HTTP no se dispone de estado, con lo que se dificulta la tarea. Spring construye su parte MVC entorno al DispatcherServlet, el cual despacha las peticiones a los manejadores, con asignaciones de controlador configurables, resolutores de vistas, resolutor de la configuracin local, de temas as como para la subida de ficheros. A lo largo de Spring ha habido cambios desde la definicin inicial de los controladores en el fichero xml (que ser lo primero que explicaremos) hasta las actuales anotaciones @Controller y @RequestMapping que ofrecen mayor flexibilidad. En Spring se pueden utilizar tambin objetos command (anteriores a la versin 3) pero actualmente se pueden utilizar cualquier objeto como un command. Este objeto ser til para la extraccin de informacin de formularios. As con Spring no tendremos que duplicar los objetos teniendo, por ejemplo, un POJO con cadenas que trasformaremos en el objeto ms complejo. El ViewResolutor (o resolutor de vista) de Spring es extremadamente flexible, a pesar de que un controlador puede escribir directamente la respuesta, normalmente devuelve un objeto ModelAndView que contiene el nombre de la vista y los objetos del modelo. El modelo es pasado a la vista la cual puede ser JSP o Velocity. El framework de Spring, especficamente el MVC, est diseado para facilitar tanto la construccin de controladores, como las vistas que estn asociadas as como la interaccin con los objetos del modelo. Todo esto de la manera ms flexible y con la posibilidad de integrar otros marcos de trabajo conocidos como Struts o JSF.
Validaciones y enlaces personalizados, enlazando valores reales como fechas y nmeros evitando la conversin de cadenas y duplicado. Mapeo y resolutores de vista personalizables, distintas estrategias de mapeo y de resolutores de vista que van desde la simple URL hasta sofisticadas estrategias. Trasferencia del modelo flexible basada en pares nombre/valor, compatible con cualquier tecnologa de vista. Se pueden configurar tanto temas como configuracin local de diversas maneras compatibles con JSP o Velocity. Spring tambin tiene una potente librera de tag que ofrece tanto el enlace de datos, temas o formularios. Los beans tienen un mbito de aplicacin de peticin o sesin, esto no es especfico de Spring pero Spring lo potencia. Spring MVC es compatible con otros marcos de trabajo web como Struts, WebWork etc. Si no se desea usar Spring MVC se pueden utilizar otras caractersticas de Spring e integrar otro framework que har uso del contexto de Spring.
DispatcherServlet
Con cada una de las peticiones que llegan desde el navegador del cliente al servidor, este tendr que darle sentido, tanto por las distintas URL, mtodos, (principalmente POST y GET) as como los parmetros, objetos JSON etc. El servidor analizar la peticin y deber decidir qu controlador ser quin se haga cargo de ella. En las aplicaciones JEE comunes todo esto se define en el fichero web.xml en el que asociamos patrones de las peticiones para que se haga cargo un Servlet. Spring con DispatcherServlet lo que hace es aadir una capa ms, de manera que todas las peticiones que lleguen (y que queramos que se haga cargo el framework de Spring) llegarn a el Servlet DispatcherServlet. ste ser el encargado, tras analizar la peticin, de pasar la responsabilidad de el qu hacer al controlador definido, es decir, a otra clase. El HandlerMapping es la clase encargada de decidir qu clase controladora est mapeada para determinada peticin. Es decir, cuando una peticin sale del navegador tiene la URL solicitada y tambin puede tener datos adicionales como datos de un formulario, esto llegar al DispatcherServlet (siempre que cumpla el patrn que para l hayamos configurado). Este Servlet frontal lo que har ser consultar al HandlerMapping si la peticin est contenida y de ser as que Controlador est asociado. Si existe coincidencia se le devolver un controlador y un mtodo de este el cul ejecutar y tras su ejecucin devolver un objeto ModelAndView el cual encapsula una vista y los datos generados tras la ejecucin del controlador y que deber interpretar la vista (normalmente un JSP). Para resolver la vista Spring se valdr de la clase ViewResolver. Al cliente le llegar el cdigo HTML tras la ejecucin e interpretacin del JSP.
89
El framework de Spring MVC, as es como otros marcos de trabajo web en los que hay un controlador principal que atiende la llamada y la delega en un secundario. Pero es algo ms que eso, ya que est integrado al contenedor de Spring y as puede disfrutar de todas las ventajas de la inyeccin de dependencia y de la inversin de control as como cualquier caracterstica de Spring. El flujo de trabajo del procesado de una peticin est en el siguiente diagrama, la peticin llega ofrecida por el servidor (por ejemplo Tomcat o Glassfish) es delegada al controler frontal (el DispatcherServlet) este delega en un controlador secundario que devolver un objeto ModelAndView (que contendr el modelo y la vista) tras su ejecucin al controlador frontal que se encargar de buscar una vista, pasarle el modelo para despus dar una respuesta al cliente, el navegador.
Esto ha sido una breve introduccin y resumen de lo que Spring MVC hace ms comnmente, aunque cada una de las piezas comentadas realmente puede tener un comportamiento ms complejo, esta introduccin nos da una primera impresin de los fundamentos de ste marco de trabajo, ms tarde daremos una explicacin ms detallada, pero de momento lo primero que debemos hacer es configurar nuestro DispatcherServlet en el contenedor de Spring.
<servlet> <servlet-name>helloWorldMvc</servlet-name> <servlet-class> org.springframework.web.servlet.DispatcherServlet </servlet-class> </servlet> <servlet-mapping> <servlet-name>dispatcherServlet</servlet-name> <url-pattern>*.html</url-pattern> </servlet-mapping>
El nombre que le demos, en este caso le hemos llamado helloWorldMvc es importante si lo que queremos es configurar varios, Spring te permite tener tantos como queramos. Por otro lado, por defecto Spring intentar abrir el fichero helloWorldMvc-servlet.xml. En cuanto al patrn hemos utilizado ste (tambin el ms utilizado en la comunidad Spring junto a *.htm) como podramos haber elegido cualquier otro. Bien es cierto que es una buena prctica darle extensin ya que es muy posible que quisiramos dejar absolutamente todo en manos del Servlet de Spring. DispatcherServlet ahora abrir el contexto de aplicacin web que hayamos definido (o el por defecto, nombre-servlet.xml). Como ya comentamos en captulos anteriores y como seguiremos viendo ms adelante, es una prctica muy buena dividir los ficheros de configuracin del contexto de igual modo que las capas lgicas ms significativas. As tendramos cuatro ficheros dividiendo las 4 capas ms importantes, la capa de seguridad, la capa de servicio, la capa de persistencia y con esta la capa web. Si dividimos los ficheros de configuracin de contexto de forma coherente con la lgica de la aplicacin ganaremos en reusabilidad, mantenibilidad y facilitaremos la comprensin. De hecho, cualquiera de estos cuatro iniciales podran ser reemplazables, es decir, imaginemos que tenemos un fichero *-data.xml en el que hay una configuracin de contexto que hace uso del motor de persistencia JPA y otro *-data.xml con Hibernate, reemplazar uno por otro sera facilsimo y no tendra que afectar a las otras capas. Para asegurarnos que todos los archivos de configuracin se abren necesitamos configurar un cargador de contexto en el archivo web.xml. ste ser ContextLoaderListener cuya configuracin en el web.xml es:
<context-param> <param-name>contextConfigLocation</param-name> <param-value> /WEB-INF/exampleapp-service.xml /WEB-INF/exampleapp-data.xml /WEB-INF/exampleapp-security.xml </param-value> </context-param> <listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener>
91
Dentro de la etiqueta context-param definimos tanto el nombre del parmetro (ser contextConfigLocation) y una lista de valores que hacen referencia a cada uno de los archivos de configuracin de contexto de Spring. Aqu se han definido por servicio, datos y seguridad. An no se han comentado la seguridad pero es conveniente que se vaya tratando este tema ya que una buena modularizacin es crucial. El DispatcherServlet es una clase que hereda de HttpServlet y como tal se declara en el web.xml, por ello se le debe mapear las peticiones que queremos que maneje, este proceso es un estndar de JEE. El WebApplicationContext es una extensin del ApplicationContext con algunas caractersticas necesarias para las aplicaciones web se diferencia de un AplicationContext normal en que es capaz de resolver temas y que sabe en que Servlet est asociado. El WebApplicationContext est ligado al ServletContext y usando mtodos estticos en la clase RequestContextUtils podemos acceder al WebApplicationContext si es que necesitamos acceder a l. El DispatcherServlet de Spring usa beans especiales para procesar la peticin y devolver una vista apropiada. Estos beans son parte del marco de trabajo de Spring y se pueden configurar en el WebApplicationContext de la misma manera que configuras cualquier otro bean. Sin embargo para la mayora de los beans se proporcionan parmetros por defecto, con lo que no es necesario configurarlo. Los beans especiales del WebApplicationContext son los siguientes: Controladores. Manejadores de mapeo o handlerMappings, manejan la ejecucin de una lista de preprocesos y post-procesos y controlan si son ejecutados si se cumple una determinada condicin. Resolutores de vista o ViewResolvers, los cuales resuelven los nombres de las vistas con las vistas. localeResolver o resolutor de configuracin local, el cual resuelve la localizacin del cliente para darle una vista internacionalizada. Resoltores de tema o ThemeResolver, resuelven un tema y apariencia, (por ejemplo dar un layout personalizado) MultipartFileResolver, que ofrece funcionalidades para subir ficheros desde formularios. HandlerExceptionResolvers, contiene funcionalidades para manejar excepciones.
Hacer una clase Controladora que interprete la peticin del cliente y sus parmetros, manipule, busque, cree o destruya los objetos del modelo y le devuelva una respuesta. Configurar el controlador en el fichero *-servlet.xml (este paso no ser necesario con las anotaciones). Crear una pgina JSP la cual ser la parte de la vista, es decir una plantilla para mostrar los datos y que, como ya sabemos, tras su interpretacin se le enviar un .html al cliente. Configurar el archivo que resuelva la pgina.
/** * * @author javiersevilla */ public class WelcomeController extends AbstractController { public WelcomeController(){ } protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception { return new ModelAndView("home","dateMessage",new Date()); } }
Este controlador es de lo ms sencillo que podemos hacer con Spring, pero sirve para ilustrarnos cmo tras ejecutar el mtodo se devuelve un objeto ModelAndView el cual
93
contendr el nombre de la vista asociada (home) y un objeto (u objetos) que representarn el modelo.
El objeto ModelAndView
Una vez que tengamos los resultados se han de mandar al navegador. En el controlador anterior tan slo se devolva un objeto de nombre dateMessage que era de tipo Date con la fecha del da de hoy. Tal y como hemos visto, al crear un objeto ModelAndView hemos pasado en el primer parmetro el nombre de la vista asociada. El encargado de interpretar este nombre con el fichero .jsp correspondiente ser el resolutor de visualizacin. As la definicin del controlador en el fichero *-servlet.xml ser de la siguiente manera: Existen varios constructores del objeto ModelAndView, entre ellos se pueden encontrar el constructor vaco, con el parmetro de la vista (ya sea el nombre en String o un objeto View) y tambin con la vista y los objetos del modelo.
Algo que puede sorprender es que no lleva propiedad id mientras que s que lleva name y ste es un patrn URL. Esto es tan sencillo como que el atributo id no puede llevar caracteres especiales, como la barra inclinada. Cuando llegue una peticin que cumpla dicho patrn le pasar la llamada.
View Resolvers
Las Java Servlet Pages (JSP) se han impuesto como la tecnologa java para desarrollo web. La forma en la que se presentan, como un .xml y con todos los tags de una .html, hacen que sea una forma muy amigable de presentar contenido web.
Para ayudar a Spring a buscar qu JSP utilizar para cada visualizacin se ha de incluir un nuevo bean de tipo ViewResolver. El ms sencillo es InternalResourceViewResolver cuya definicin es la siguiente.
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" p:prefix="/WEB-INF/jsp/" p:suffix=".jsp"/>
Spring tiene varios ViewResolvers pero para las visualizaciones mostradas por JSP InternalResourceViewResolver es el ms sencillo. Su funcionamiento es tan simple como aadir tanto el prefijo como el sufijo al nombre de la vista devuelta en el objeto ModelAndView. As si nuestro controller devuelve welcome como nombre de la vista en el objeto ModelAndView Spring buscar el archivo /WEB-INF/jsp/welcome.jsp.
Crear un JSP
Como ltimo paso deberamos crear el fichero welcome.jsp, el siguiente cdigo ilustra su contenido:
<%@ page language="java" import="java.util.*" pageEncoding="ISO-88591"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>WELCOME</title> </head> <body> ${dateMessage}<br> </body> </html>
Pasos de la peticin
Ahora lo tenemos todo, el controlador, la pgina jsp, lo hemos registrado en el contexto de DispatcherServlet y tenemos un ViewResolver. Pero, Qu ocurre cuando se recibe una peticin?, los acontecimientos son los siguientes: 1. El servidor le pasa al DispatcherServlet la peticin (para ello lo configuramos y le dimos un patrn en el web.xml). 2. DispatcherServlet buscar qu controlador se encarga de la URL.
95
3. Nuestro controlador devuelve un ModelAndView con un nombre lgico de la vista y un objeto o un mapa de objetos con los objetos del modelo. 4. DispatcherServlet buscar en el ViewResolver la vista con el nombre lgico (en nuestro caso welcome) y devolver la ruta completa (WEB-INF/jsp/welcome.jsp). 5. DispatcherServlet reenviar la peticin al jsp.
Con esto hemos completado la manera ms bsica de operar con controladores sin anotaciones o con marcos de trabajo inferiores a Spring 3. En los siguientes puntos veremos todas las ventajas que la versin 3 nos aporta.
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.ModelAndView; @Controller public class HelloWorldController { @RequestMapping("/helloWorld") public ModelAndView helloWorld() { ModelAndView mav = new ModelAndView(); mav.setViewName("helloWorld"); mav.addObject("message", "Hello World!"); return mav; } }
Como se puede ver, las anotaciones @Controller y @RequestMapping permiten una gran flexibilidad de mtodos y firmas. En este ejemplo no hay parmetros y devuelve un ModelAndView. Existen mltiples estrategias como se explicarn ms adelante en este captulo. Con ModelAndView, @Controller y @RequestMapping tenemos los pilares bsicos para construir con Spring MVC.
97
En el ejemplo el @RequestMapping es usado para cada mtodo. Lo que har que se diferencien ser tanto los parmetros de la peticin, si hay sub-URL o si la peticin es GET o POST. El @RequestMapping a nivel de clase no es necesario, si no lo hacemos cada uno de los mtodos ser a nivel absoluto. Como en el siguiente ejemplo:
package org.youdiversity.web.controllers; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.ModelAndView; @Controller public class HelloWorldController { @RequestMapping("/helloWorld") public ModelAndView helloWorld() { ModelAndView mav = new ModelAndView(); mav.setViewName("helloWorld"); mav.addObject("message", "Hello World!"); return mav; } }
Para acceder a partes de la peticin URL se pueden usar plantillas del modo http://www.ejemplo.com/usuarios/546 con @ReuestMapping lo mapearemos del siguiente modo @RequestMapping(value=/usuarios/{usuarioId}). Usaremos @PathVariable para designar cada uno de las valores contenidos en la URL. El siguiente ejemplo muestra este uso:
@RequestMapping("/user/{userId}") public ModelAndView userHandler (@PathVariable("userId") Long userId) { ModelAndView mav = new ModelAndView("user/profile"); mav.addObject(userDao.get(userId)); return mav; }
De este modo si recibisemos una peticin /user/12 el nmero 12 sera una variable de path que en este caso nos servir como en id del usuario. Slo si se ha compilado en modo debug no se necesitar la anotacin @PathVariable para enlazar con el valor de la variable. Tambin se pueden usar varias variables
@RequestMapping("/user/{userId}/{subjectId") public ModelAndView userHandler (@PathVariable("userId") Long userId, @PathVariable("subjectId") Long subjectId) {
@RequestMapping("/subject/{subjectId}") public ModelAndView userHandler (@PathVariable("userId") Long userId, @PathVariable("subjectId") Long subjectId) { ... return mav; } }
Adems de las URL la anotacin @RequestMapping soporta expresiones de direcciones, como al estilo de Ant (por ejemplo: /users/*/subject/{subjectId}). Los resolutores de direcciones MethodNameResolver y PathMethodNameResolver buscaran primero rutas explcitas y tras descartar cada una de ellas las rutas que tengan una expresin como la citada sern las que queden por defecto si no ha habido una coincidencia anterior. Si se tienen un nico mtodo por defecto (sin un mapeo especfico), entonces todas las peticiones sin mtodos con mapeos ms especficos sern servidas a l. Se pueden especificar que el mtodo est esperando determinados parmetros que han de estar o no estar, es decir, @RequestMapping (value = "/subject/{subjectd}", params = "myParam=myValue"). As este mtodo slo ser mapeado si adems de cumplir el path de la URL tambin tenga el parmetro myParam y con el valor myValue. Para indicar lo contrario sera indicar !myParam, as ese mtodo nunca ser seleccionado si encuentra el parmetro myParam. De igual forma, los mapeos pueden ser configurados teniendo en cuenta tambin el contenido de la cabecera.
@RequestMapping(value="/subject/{subjectId}",headers = "content-type=text/*") public ModelAndView userHandler(@PathVariable("userId") Long userId, @PathVariable("subjectId") Long subjectId) { ... return mav; }
99
El mtodo anterior slo es ejecutado cuando el contenido es text/* como por ejemplo text/html.
Los mtodos anotados con @RequestMapping pueden tener una firma muy flexible. Muchos de ellos pueden ser usados de modo arbitrario. Se pueden elegir distintas implementaciones del objeto Request o Response del API de Servlet como ServletRequest o HTTPServletRequest. Que aparezca un objeto de sesin del API de Servlet fuerza la presencia de una sesin. Con lo que nunca tendr un valor nulo. Los objetos org.springframework.web.context.request.WebRequest o org.springframework.web.context.request.NativeWebRequest Te permiten el acceso genrico a un parmetro de la peticin as como el acceso a los atributos Request/Session sin que haya vnculos al API nativo Servlet/Portlet. java.util.Locale es determinado segn el mayor resolutor de locale que est disponible, de hecho, el LocaleResolver es un Servlet. Se puede acceder a la peticin con java.io.InputStream o con java.io.Reader de igual manera que con el API Servlet. Tambin se puede generar una respuesta con java.io.OutputStream as como con java.io.Writer como ussemos el API de Servlet. El objeto java.security.Principal contiene el usuario autenticado. Los parmetros de acceso de la anotacin @PathVariable obtienen el acceso a las variables de la URI. Con la anotacin @RequestParam se hace referencia a los parmetros de la peticin, es decir a los parmetros especficos de la peticin de la Request. Los parmetros son convertidos en el tipo declarado en el mtodo. Ms tarde haremos referencia a como enlazar (o hacer binding) estos valores. La anotacin @RequestHeader se utiliza para acceder a los parmetros de la cabecera de la peticin. Los valores de los parmetros sern convertidos en los tipos declarados. La anotacin @RequestBody se utiliza para anotar parmetros que se hallen en el cuerpo de la peticin. Los valores sern convertidos en los tipos declarados usando HttpMessageConverteres. Ms tarde veremos cmo hacerlo.
Con el parmetro HttpEntity<?> podremos acceder a las cabeceras HTTP y su contenido de la peticin. El flujo de la peticin ser convertido en la entidad body usando HttpMessageConverteres. Tambin lo veremos ms tarde. Podemos usar java.util.Map, org.springframework.ui.Model org.springframework.ui.ModelMap para guardar los objetos de modelo. o
Los objetos Command o Form los podemos enlazar con parmetros como propiedades de los beans, campos, conversiones con algn tipo personalizado, dependiendo de los mtodos de @InitBinder y de la configuracin del HandlerAdapter.
Con org.springframework.validation.Errors o org.springframework.validation.BindingResult se valida un comando o formulario. Con org.springframework.web.bind.support.SessionStatus se maneja el estado para hacer el proceso de un formulario completo. El cual desencadena la limpieza de los atributos de la sesin que se han quedado indicados por la anotacin @SessionAttributes. Los parmetros de errores (Errors) o de resultado del enlace (BindingResult) han de seguir el modelo del objeto que ha sido enlazado inmediatamente. Es decir, si se enlaza un modelo, y un @ModelAttribute en la firma del mtodo no podremos poner un nico BindingResult, sino que tendremos que poner uno a continuacin de cada uno de los objetos enlazados.
El objeto ModelAndView en el que se haya el modelo con los objetos y los resultados de la referencia a los datos de la anotacin @ModelAttribute. El objeto Model, en este caso el nombre de la vista ser determinado implcitamente a travs RequestToViewNameTranslator y el objeto Model, de igual forma a ModelAndView, contendr los objetos y resultados. Un mapa (implementacin de Map), que representar los objetos del modelo. De igual forma cuando se devuelve un Model la vista ser determinada por RequestToViewNameTranslator. Una vista, es decir, un objeto View. El modelo estar implcitamente determinado por los objetos del comando y los objetos con anotaciones @ModelAttribute. De igual forma, los objetos de comando estarn implcitos como modelo. Una cadena que representa la vista lgica. De igual modo, tendr implcito el modelo con los objetos de comando y los objetos anotados con @ModelAttribute. Nada, es decir un mtodo con firma void. En este caso el mtodo manejar la peticin por s mismo, es decir, escribiendo la respuesta directamente con contenido, declarando un
101
argumento de tipo ServletResponse o HttpResponse o si el nombre de la vista se ha de deducir a travs del objeto RequestToViewNameTranslator. Si el mtodo est anotado con @ResponseBody, el tipo a devolver tendr que estar escrito en el cuerpo de la respuesta HTTP. El valor del tipo devuelto ser convertido en el tipo del argumento del mtodo declarado usando HttpMessageConverters. Una entidad HttpEntity<?> o ResponseEntity<?>, objeto que proporciona acceso a las cabeceras de respuesta HTTP y a sus contenidos. El cuerpo de la entidad ser convertida en el flujo de respuesta usando HttpMessageConverters. Cualquier otro tipo devuelto es considerado un nico objeto de modelo. De este modo, como pasa de otros muchos, se le aadirn los objetos command as como los anotados con @ModelAttribute y la vista ser determinada.
Los parmetros que usan esta anotacin se requieren por defecto, si queremos que sean opcionales tendremos que aadir el atributo required con un valor false.
@RequestBody
La anotacin @RequestBody cuando se usa sobre un parmetro del mtodo indica a Spring que ese parmetro del mtodo ha de ser enlazado con el valor de la peticin HTTP. Por ejemplo:
@RequestMapping(value = "/something", method = RequestMethod.PUT) public void handle(@RequestBody String body, Writer writer) throws IOException { writer.write(body); }
Se puede convertir el cuerpo de la peticin usando HttpMessageConverter. Este objeto tambin convierte el mensaje de solicitud a un objeto de respuesta. DispatcherServlet soporta el procesamiento de la anotacin.
@ResponseBody
La anotacin @ResponseBody es la anloga de la respuesta de @RequestBody. En este caso puesto sobre un mtodo indica a Spring que el valor de retorno del mtodo ser una cadena con el cuerpo de la respuesta.
@RequestMapping("answerSingleQuestion") @ResponseBody public String addAnswer(@RequestParam Long questionId, @RequestParam(required = false) List<String> answersId) { List<Long> answersIdLongs = new ArrayList<Long>(); if (answersId != null) { for (String answerId : answersId) { answersIdLongs.add(new Long(answerId)); } } QuestionAnswered questionAnswered = this.questionDao.saveQuestionAnsweredByUser(questionId, answersIdLongs); return questionAnswered.getCorrect() ? "Pregunta contestada correctamente" : "Pregunta no contestada correctamente"; }
HttpEntity
103
Similar a @RequestBody o @ResponseBody, el HttpEntity tiene acceso al cuerpo de la peticin o de la respuesta y permite acceder a las cabeceras de las peticiones o de las respuestas. El siguiente mtodo es un ejemplo:
@RequestMapping("/something") public ResponseEntity<String> handle(HttpEntity<byte[]> requestEntity) throws UnsupportedEncodingException { String requestHeader = requestEntity.getHeaders().getFirst("MyRequestHeader")); byte[] requestBody = requestEntity.getBody(); HttpHeaders responseHeaders = new HttpHeaders(); responseHeaders.set("MyResponseHeader", "MyValue"); return new ResponseEntity<String>("Hello World", responseHeaders, HttpStatus.CREATED);
La anotacin @SessionAttributes indica que atributos de sesin son usados por un manejador en particular. Esto lista una serie de nombres de atributos del modelo que se almacenaran como atributos de sesin.
@Controller @SessionAttributes(types = TeacherDataPicker.class) @RequestMapping("/admin/createTeacher") public class CreateTeacherController {
105
Para externalizar y normalizar el enlace de datos a nivel de aplicacin implementaremos una clase de WebBindingInitializer.
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"> <property name="webBindingInitializer"> <bean class="com.campus.web.binding.CampusBindingInitializer"/> </property> </bean>
Mapeo de controladores
Como hemos visto, en las versiones anteriores a Spring 2.5, era necesario definir HandlerMapping en el contexto de la aplicacin web. Ahora el DispatcherServlet busca anotaciones @RequestMapping en los controladores. Hay diversas propiedades que estn inicializadas por defecto y que para modificarlas tendremos que sobrescribir como puedan ser interceptores, manejadores por defecto, orden de los mapeos, si queremos usar expresiones o rutas completas entre otras.
<beans> <bean id="handlerMapping" class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotatio nHandlerMapping"> <property name="interceptors"> <bean class="example.MyInterceptor"/> </property> </bean> <beans>
Resolver vistas
Todos los marcos de trabajo MVC otorgan maneras de direccionar las vistas. Spring proporciona resolutores de vistas los cuales permiten enlazar el modelo sin atarse a ninguna tecnologa de vista especfica. Spring permite usar vistas JSP, Velocity o XSLT. ViewResolver y View son los interfaces que Spring utiliza.
ViewResolver
Como hemos comentado, todos los mtodos en los controladores de Spring MVC deben resolver con el nombre de una vista lgica, de manera implcita o explcita. Las vistas de Spring son mapeadas con una vista lgica resuelta por el resolutor de vista. Spring proporciona los siguientes resolutores:
AbstractCachingViewResolver XmlViewResolver ResourceBundleViewResolver UrlBasedViewResolver InternalResourceViewResolver VelocityViewResolver / FreeMarkerViewResolver ContentNegotiatingViewResolver
Con UrlBasedViewResolver las traducciones de resolucin de vista se hacen desde una URL
<bean id="viewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver"> <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/> <property name="prefix" value="/WEB-INF/jsp/"/> <property name="suffix" value=".jsp"/> </bean>
107
Cuando se devuelve ejemplo como vista lgica se traducir el fichero fsico /WEBINF/jsp/ejemplo.jsp.
Cuando
se
combinan
vista
se
pueden
usar
<bean id="viewResolver" class="org.springframework.web.servlet.view.ResourceBundleViewResolver"> <property name="basename" value="views"/> <property name="defaultParentView" value="parentView"/> </bean>
Encadenando ViewResolvers
Spring permite tener mltiples resolutores de vista encadenados. Se puede especificar el orden con el atributo order, a mayor valor de este ms tarde se posicionar.
Redireccionando a vistas
Como hemos dicho un controlador devuelve un nombre de vista lgica. Para tecnologas de vista como JSP que se procesan a travs de motores JSP o Servlet, esta resolucin est tpicamente manejada a travs de la combinacin de InternalResourceViewResolver e InternalResourceView los cuales redireccionan o incluyen a travs del API de Servlet con los mtodos RequestDispatcher.forward() o RequestDispatcher.include(). En ocasiones es deseable una tarea de redireccin HTTP de vuelta al cliente antes de devolver la vista. Esto es deseable cuando un controlador ha sido llamado con datos enviados con POST y la respuesta es en verdad una delegacin de otro controlador que tambin ve los mismos datos enviados con POST. Esto es potencialmente problemtico si puede confundir con otros datos esperados tambin enviados por POST. Otra razn por la que se redirige antes de enviar a la vista es para eliminar la posibilidad de que el usuario haga un submit mltiple, es decir, un envo de un formulario varias veces. En este escenario, el navegador enviar un primer POST con datos entonces el recibir una respuesta con una redireccin a una URL distinta, finalmente el navegador ejecutar una redireccin GET a la URL contenida en la respuesta de redireccin. As, desde la perspectiva del navegador, la pgina actual no refleja el resultado de un POST sino de un GET. El efecto final es que no hay manera que el usuario pueda accidentalmente mandar los mismos datos en mltiples ocasiones. El refresco fuerza un GET del resultado de la pgina, no un re-envo de los datos por POST. RedirectView
Una manera de forzar una redireccin como resultado de la respuesta de un controlador es crear y devolver una instancia de RedirectView, en este caso, DispatcherServlet no har uso del mecanismo normal de resolucin de vista. En cambio, como ya se ha dado la redireccin a la vista el DispatcherServlet simplemente deja que la vista haga su trabajo. La una llamada al mtodo de redireccin HttpServletResponse.sendRedirect(), el cual devuelve al navegador una redireccin HTTP. Todos los atributos del modelo sern parmetros de consulta HTTP, esto significa que el
RedirectView
tarea
de
es
modelo debe contener solo objetos (Cadenas o valores convertibles a cadenas), los cuales han de estar preparados para ser convertidos en parmetros textuales de una consulta HTTP.
El prefijo redirect:
Aunque el uso de RedirectView funciona bien, si es controlador se crea en RedirectView, no hay manera de evitar que el controlador sea consciente de que est pasando una redireccin. Esto no es del todo ptimo, el controlador no debera preocuparse de que la respuesta sea controlada. En general slo se debera operar en trminos de los nombres de las vistas. El prefijo especial redirect: permite lograr esto. Si un nombre de vista es devuelto con un prefijo redirect: el UrlBasedViewResolver reconocer este aspecto especial como una redireccin. El resto del nombre de la vista ser tratado como la redireccin URL. El efecto final es como si el controlador devolviese una redireccin RedirectView, pero ahora el mismo controlador puede operar en trminos de nombre de vista lgica. Lo importante de esto es que el mismo controlador no sabe que la redireccin est pasando. El prefijo forward:
Tambin existe la posibilidad de usar un prefijo especial forward: para los nombres de vistas que sern resueltas por UrlBasedViewResolver. Esto crea un InternalResourceView, el cual ejecuta un RequestDispatcher.forward(). Este prefijo no es til con InternalResourceViewResolver y InternalResourceView pero puede ser til cuando se est usando otro tipo de tecnologa para la vista pero se quiere forzar un forward a un recurso controlado por un motor Servlet o JSP. Como con redirect: el prefijo forward: es inyectado en el controlador, el controlador no detecta que nada especial est pasando en trminos de manejar la respuesta.
ContentNegotiatingViewResolver
La clase ContentNegotiatingViewResolver no resuelve las vistas ella misma, en su lugar delega a otros resolutores seleccionando la vista que se asemeja a la representacin de la peticin del cliente. Dos estrategias son llevadas a cabo: Usar una URI distinta para cada recurso, normalmente usando una extensin distinta. Por ejemplo la URI, http://ejemplo.org/usuarios.pdf y http://ejemplo.org/usuarios.xml, uno pedira una representacin PDF de los usuarios y la otra una representacin XML. Para permitir mltiples representaciones de un recurso Spring dispone del ContentNegotiantingViewResolver el cual resuelve una vista basada en la extensin del fichero o de la cabecera de la peticin HTTP. ContentNegotiantingViewResolver no interpreta la
109
resolucin de la vista sino que delega a una lista de resolutores de vista que especificarn a travs de la propiedad ViewResolver. La clase ContentNegotiatingViewResolver selecciona un resolutor de vista comparando el tipo de la peticin (content-type) con los distintos ViewResolvers. El primer ViewResolver que sea compatible con el content-type devolver la representacin al cliente. Si no hubiese una coincidencia de contenido por los distintos ViewResolvers se consultar la propiedad DefaultViews. Esta ltima opcin es apropiada para vistas singleton que har una representacin adecuada de los recursos sin importar el nombre de vista lgico. Se pueden incluir caracteres comodn, como por ejemplo text/* en el que el content-type podr ser text/html, text/xml etc. Para ayudar a la resolucin de la vista basada en extensiones de ficheros se usa la propiedad mediaTypes en el bean ContentNegotiantingViewResolver para especificar un mapeo de extensiones de content-type.
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver"> <property name="mediaTypes"> <map> <entry key="atom" value="application/atom+xml"/> <entry key="html" value="text/html"/> <entry key="json" value="application/json"/> </map> </property> <property name="viewResolvers"> <list> <bean class="org.springframework.web.servlet.view.BeanNameViewResolver"/> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/"/> <property name="suffix" value=".jsp"/> </bean> </list> </property> <property name="defaultViews"> <list> <bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" /> </list> </property> </bean>
El InternalRessourceViewResolver maneja la traduccin de la visa en pginas JSP, mientras que el BeanNameViewResolver devuelve una vista basada en el nombre del bean. En este ejemplo el content bean es una clase que hereda de AbstractAtomFeedView la cual devuelve un Atom feed RSS. En la siguiente configuracin si una configuracin es creada con la extensin .html el resolutor de vista buscar una vista cuyo media type coincida con text/html. El InternalResourceViewResolver otorga la coincidencia para la vista text/html. Si la peticin es
creada con una extensin .atom, el resolutor de vista buscar una vista que coincida con el media type application/atom+xml. Esta vista ser proporcionada por el resolutor BeanNameViewREsolver que mapea SampleContentAtomView como si el nombre de la vista devuelto fuera el contenido. Si la peticin es creada por una extensin .json la instancia del bean MappingJacksonJsonViewser elegido ya que es la opcin por defecto. Por otro lado, las solicitudes se pueden hacer sin extensin del archivo pero con la cabecera Accept definida con un media-type, en tal caso se har la misma resolucin.
Configuracin regional
La mayor parte de la arquitectura de Spring soporta internacionalizacin, incluida la parte Spring MVC. DispatcherServlet permite resolver mensajes de manera automtica usando el locale del cliente gracias a los objetos LocaleResolver. Cuando llega una peticin el DispatcherServlet busca un resolutor local, si se encuentra uno se intentar usar para asignar una configuracin regional. Mediante el mtodo RequestContext.getLocale() se pueden obtener el Locale que fue resuelto por el LocaleResolver. A parte de la configuracin automtica de resolucin de Locale, tambin se puede configurar un interceptor para que maneje el mapeo para cambiar el Locale en determinadas circunstancias, como por ejemplo, como parte de la URI, o por parmetros. Los resolutores de configuracin regional y los interceptores estn definidos en el paquete org.springframework.web.wervlet.i18n.
AcceptHeaderLocaleResolver
Este resolutor busca en el parmetro de la cabecera de la peticin accept-language que fue enviada por el cliente. Normalmente este parmetro contiene el Locale del sistema operativo.
CookieLocaleResolver
Este resolutor busca en una cookie que debe existir en el cliente para ver si hay un Locale especfico. Si lo hay, se usar ese locale especfico. Se puede definir el nombre de la cookie, su vida as como aplicarle un path el cual ser visible para una parte especfica de la aplicacin (/ para toda la aplicacin).
SessionLocaleResolver
111
El SessionLocaleResolver permite recibir Locales desde la sesin que quiz fue asociada con una peticin del usuario.
LocaleChangeInterceptor
Se puede activar la opcin de poder cambiar los Locale aadiendo LocaleChangeInterceptor a uno de los manejadores de mapeo. Eso har que detecte un parmetro en la peticin y cambie el Locale. Se llamar a setLocale del objeto LocaleResolver que est en el contexto. El siguiente ejemplo muestra cmo las llamadas a los recursos *.view contienen un parmetro que se llama siteLanguage que cambiar el Locale.
<bean id="localeChangeInterceptor" class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"> <property name="paramName" value="siteLanguage"/> </bean> <bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver"/> <bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="interceptors"> <list> <ref bean="localeChangeInterceptor"/> </list> </property> <property name="mappings"> <value>/**/*.view=someController</value> </property> </bean>
Uso de temas
Se pueden aplicar temas al marco de trabajo de Spring MVC para el conjunto del look-and-feel de una aplicacin. La resolucin del tema es muy similar a la resolucin del Locale. Un tema es una coleccin de recursos estticos, hojas de estilo e imgenes que afectan al aspecto visual de la aplicacin.
<link rel="stylesheet" href="<spring:theme code='styleSheet'/>" type="text/css"/> ... <body style="background=<spring:theme code='background'/>"> ...
Por defecto ResourceBundleThemeSource usa un nombre base vaco como prefijo. As, el fichero de properties es cargado desde la raz del classpath. ResourceBundleThemeSource utiliza el mecanismo de carga del paquete estndar de Java para leer los recursos, siendo as tambin internacionalizable.
Resolutores de tema
Despus de definir los temas hay que elegir cul de ellos usar. El DispatcherServlet buscar un bean llamado themeResolver para saber que implementacin de ThemeResolver se usar. ThemeResolver funciona de manera muy similar a LocaleResolver, se detecta que tema se usar en una peticin en particular y tambin se puede cambiar el tema. Spring ofrece los siguientes resolutores de tema: FixedThemeResolver, Selecciona un tema fijo usando la propiedad defaultThemeName. SessionThemeResolver, El tema es mantenido en la sesin HTTP del usuario el cul permanecer toda la sesin, pero no estar persistida entre sesiones. CookieThemeResolver, El tema se guarda en una cookie en el navegador del cliente.
Spring tambin tiene la clase ThemeChangeInterceptor que permite los cambios de tema en cada una de las peticiones con un nico parmetro.
Subida de ficheros
Spring permite la subida de ficheros multipart. Se puede activar esta caracterstica con el objeto MultipartResolver. Spring proporciona un resolutor multiparte para usar Commons FileUpload de Apache. El siguiente ejemplo muestra cmo usar CommonsMultipartResolver:
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="maxUploadSize" value="100000"/> </bean>
Por supuesto tambin se necesita poner los .jar necesarios en el classpath para que el resolutor multipart funcione, es decir, el fichero commons-fileupload.jar. Cuando el DispatcherServlet de Spring detecta una peticin multi-part activa el resolutor que haya sido declarado en el contexto. El resolutor entonces envuelve la peticin HTTP actual en una MultipartHttpServletRequest que permite la subida por partes de ficheros.
113
El siguiente paso ser crear un controlador que maneje la peticin de la subida del fichero. Este controlador es muy similar a cualquier otro, con la salvedad que usa MultipartHttpServletRequest o MultipartFile como parmetros de mtodo. El siguiente cdigo muestra cmo hacerlo:
@Controller public class FileUpoadController { @RequestMapping(value = "/form", method = RequestMethod.POST) public String handleFormUpload(@RequestParam("name") String name, @RequestParam("file") MultipartFile file) { if (!file.isEmpty()) { byte[] bytes = file.getBytes(); // store the bytes somewhere return "redirect:uploadSuccess"; } else { return "redirect:uploadFailure"; } } }
Cabe destacar cmo los atributos anotados con @RequestParam son los elementos input del fichero. Finalmente, se tendrn que declarar el controlador y su resolutor en el contexto de la aplicacin.
Manejo de Excepciones
La clase de Spring HandlerExceptionResolver se encarga de la excepcin de excepciones que puedan ocurrir mientras se hace se ejecuta la peticin a un controlador. HandlerExceptionResolver tiene un comportamiento similar al mapeo que se puede hacer en el descriptor xml de la aplicacin web. Sin embargo, proporciona mayor flexibilidad para manejar las excepciones. Proporciona informacin sobre qu manejador se estaba ejecutando mientras se lanz la excepcin. Por otra parte, una manera programtica de manejar excepciones te da ms opciones de responder apropiadamente antes de que la peticin sea pasada a otra URL. Aunque se pueda implementar la interfaz HandlerExceptionResolver, que se implementa nicamente con el mtodo resolverException(Exception, Handler) y devolviendo un ModelAndView. Tambin se puede usar el SimpleMappingExceptionResolver el cual te permite resolver el nombre de la clase de la excepcin que haya podido ser lanzada y mapearla con el nombre de una vista. Esta funcionalidad es equivalente al mapeo de excepciones del API de Servlet, pero tambin se puede implementar de una manera ms ajustada mapeos de excepciones de diferentes controladores. Por defecto, el DispatcherServlet registra DefaultHandlerExceptionResolver, este resolutor maneja una serie de excepciones estndar de Spring asignndolas un especfico cdigo de respuesta. La siguiente tabla muestra esta equivalencia: Excepcin
ConversionNotSupportedException HttpMediaTypeNotAcceptableException HttpMediaTypeNotSupportedException HttpMessageNotReadableException HttpMessageNotWritableException HttpRequestMethodNotSupportedException MissingServletRequestParameterException NoSuchRequestHandlingMethodException TypeMismatchException
Cdigo de estado HTTP 500 (Error interno del servidor) 406 (No aceptable) 415 (Tipo de medio no soportado) 400 (Mala peticin) 500 (Error interno de servidor) 405 (Mtodo no soportado) 400 (Mala peticin) 404 (No encontrado) 400 (Mala peticin)
115
@ExceptionHandler
Otra alternativa al interfaz HandlerExceptionResolver es la anotacin @ExceptionHandler. Se usa @ExceptionHandler como una anotacin de mtodo, para especificar qu mtodo ser invocado cuando una excepcin en particular sea lanzada mientras la ejecucin de algn mtodo del controlador.
@Controller public class SimpleController { @ExceptionHandler(IOException.class) public String handleIOException(IOException ex, HttpServletRequest request) { return ClassUtils.getShortName(ex.getClass()); } }
De este modo se invocar este mtodo cuando una excepcin sea lanzada.
Convenios
La mejor manera de mantener y crear un proyecto es seguir determinadas pautas y convenios que harn ms sostenible la configuracin necesaria, el mapeo de controladores, la resolucin de vistas, las instancias ModelAndView y dems. Esto har ms consistente y mantenible el cdigo.
117
Este captulo se centrar en la seguridad y cmo Spring a travs de su proyecto Spring Security se hace cargo de ella. Javier Sevilla Snchez
119
Contenido
Qu es Spring Security? ........................................................................................................... 121 Autenticacin en Spring Security .............................................................................................. 121 AuthenticationManager ............................................................................................................ 122 Seguridad en aplicaciones Web ................................................................................................ 123 Autorizacin en Spring Security ................................................................................................ 127 Seguridad de la capa de servicio ............................................................................................... 129 Configuracin de seguridad a nivel global (namespace)....................................................... 129 Seguridad a nivel de bean ..................................................................................................... 129 Con el objeto MethodSecurityInterceptor ............................................................................ 129 Seguridad a nivel de anotacin ............................................................................................. 130
121
Qu es Spring Security?
Spring Security se ha convertido en la librera de referencia dentro del mundo Java, para dar soporte a los servicios de autenticacin y autorizacin de una aplicacin. Esto se debe principalmente a que es un proyecto proyecto enmarcado dentro de Spring y como ya hemos visto gracias a Spring podremos hacer cdigo mantenible, reutilizable, gil y robusto. El cdigo y diseo del componente es inmejorable, es sabido que dentro de la comunidad de desarrolladores Java tiene un gran uso siendo un referente. Spring Security es un framework maduro que viene de otro proyecto llamado Acegi. Como la mayora del framework de Spring, Spring Security ofrece una gran facilidad de configuracin y parametrizacin, gracias al uso de mamespace y la inyeccin de dependencia. Permite la integracin con los sistemas legacy de autenticacin ms importantes del mercado: BBDD, LDAP, CAS (para single sign-on), gestin de certificados, etc. Spring Security tiene un gran respaldo de la comunidad con lo que hay una gran cantidad de documentacin y ejemplos.
Authenticacion, este objeto guardar los datos asociados a AutenticationManager y sus roles. As este objeto es en el que sea apoya el AuthenticationManager para implementar el servicio de autenticacin. SecurityContextHolder, encargado de guardar los datos del principal y sus roles dentro del contexto de seguridad. Como hemos dicho, la informacin del principal la alberga Authentication, as que SecurityContextHolder guarda el objeto Authentication en el contexto de seguridad. As la informacin que reside en el objeto Authentication pueda ser consultada en todo momento por cualquier componente dentro de la aplicacin. Objeto UserDetailsService, Spring suele delegar responsabilidades en otros objetos, en este caso el objeto AuthenticationManger delega en el objeto UserDetails las labores de obtencin de la informacin del usuario sobre el repositorio de seguridad. As, si los usuarios estn en una base de datos, el objeto UserDetails realizar las consultas necesarias para obtener la informacin del AuthenticationManager.
AuthenticationManager
Este interfaz slo define un mtodo: Authentication authenticate (Authentication authenticaction) throws AuthenticationException; El parmetro de entrada tendr los datos bsicos de autenticacin. Con este dato se prodecer a obtener toda la informacin de credenciales asociadas con el principal y se comprobar si dichas credenciales son correctas. En el caso de que las credenciales no sean correctas el proceso de autenticacin lanzar una AuthenticationException. Si la autenticacin es correcta se obtendrn las autoridades asociadas al usuario y sern guardadas en el objeto Authentication, el cual ser devuelto por el mtodo. ste objeto ser guardado por el componente SecurityContextHolder en el contexto de seguridad. Como hemos dicho, AuthenticationManager delega responsabilidades, teniendo un conjunto de componentes ProviderManager los cuales son los encargados de implementar el proceso de autenticacin. Los objetos ProviderManer estn ms cercanos a la tecnologa usada para impementar el repositorio de seguridad de la aplicacin. As si disponemos de un LDAP, tendremos el objetoLdapAuthenticationProvider, si accedemos a una base de datos tendremos un DaoAuthenticationProvider, etc, etc.
En el siguiente diagrama se muestra la colaboracin entre componentes para implementar el proceso de autenticacin de la aplicacin, en este caso se muestra para un delegado que buscar en una base de datos gracias al DaoAuthenticationProvider.
123
Spring tiene un gran nmero de Providers as, podremos dar soporte a multitud de sistemas. As podremos adaptar nuestras aplicaciones a distintas particularidades, presentes y futuras. Una de las grandes ventajas que tiene Spring Security es la inclusin de namespace especficos que da sopote a la configuracin de los distintos componentes que forman parte de la solucin de autenticacin y autorizacin de aplicaciones. Antes, era necesario definir un conjunto de beans muy extenso y exista la posibilidad de error en la gestin de las dependencias de los distintos beans. Gracias a la incorporacin de namespace, al desarrollar se abstrae de muchas particularidades internas de la solucin, proporcionando un fichero XML de configuracin ms sencillo.
Como vemos en el ejemplo el nico parmetro de configuracin necesario es el gestor de autenticacin donde est el esquema de base de datos donde reside el repositorio de seguridad.
Este fichero permitir la configuracin de todos los elementos necesarios para realizar la autenticacin y autorizacin de una aplicacin web sobre HTTP.
Lo primero es definir es el ServletFilter DelegatingFilterProxy. Este filtro lo que hace es un proxy entre los Servlets y el contexto de Spring. As, este filtro permite que todos los componentes que forman parte de la arquitectura de Spring Security puedan ser definidos mediante los ficheros XML. Spring Security, como ya hemos dicho, se puede configurar mediante el uso de namespace, que es un fichero XML que cumple un determinado esquema, facilitando la configuracin. Dentro del fichero de configuracin incluiremos el siguiente cdigo:
Dentro de la configuracin se puede ver como se pueden securizar URL. La etiqueta contiene el patrn de URL que se quiere securizar y una lista de roles que son necesarios para poder acceder al recurso, en el caso que se indiquen varios roles, con que se tenga uno de los roles ser suficiente para disponer de permiso para dicha URL. Dentro de este fichero de configuracin se puede configurar las siguientes grandes funcionalidades: Remember-me, as el usuarios puede guardar las credenciales en el navegador y as no tener que volver a introducirlas en el siguiente proceso de autenticacin. Usa un soporte de cookies con aplicacin de algoritmos hash para guardar la informacin. Seleccin de canal de seguridad, si usamos el atributo requires-channel dentro de la etiqueta, se puede exigir que determinadas URL sean seguras dentro de la aplicacin. Spring Security hace las oportunas redirecciones que sean necesarias para el cambio de canal. Gestin de la sesin, control de timeouts de sesin, y control de la sesin concurrente. El control de los timeouts de sesin sirve para indicar cuando esta invalidada. El control de sesin concurrente es para controlar el nmero de sesiones que estn activas para un mismo usuario. Esto supone una proteccin contra el ataque de fijacin de sesin. http://en.wikipedia.org/wiki/Session_fixation Soporte para OpenId Spring Security mantiene una cadena de filtros que cada uno de ellos da cabida a una funcionalidad dentro de los procesos de autenticacin y autorizacin entre cada peticin y respuesta. Es posible modificar la cadena de filtros. Dentro de estos filtros destacan:
125
FilterSecurityInterceeptor, el cual es encargado de manejar la seguridad de los recursos HTTP manejando los elementos definidos en el namespace. ExceptionTranslationFilter, encargado de manejar excepciones lanzadas por los interceptores de seguridad y proveer la respuesta HTTP correspondiente. SecurityContextPersistenceFilter, responsable de guardar el contexto de seguridad entre peticiones. Bsicamente el contexto de seguridad es guardado a nivel de sesin.
127
Como vemos en la figura, existen numerosos interceptores en la cadena. Toda la cadena de filtros es creada y aislada del programador mediante el uso de namespace. Sin embargo, es posible modificar y extender la cadena de filtros.
En el centro del diagrama tenemos la clase abstracta AbstractSecurityInterceptor, sta representa las capacidades de autorizacin bsicas. Se pueden securizar peticiones HTTP gracias a FilterSecurityInterceptor e invocaciones a mtodos gracias a MethodSecurityInterceptor y AspectJSecurityInterceptor. Este interceptor delega en el interfaz AccesDecisionManager la decisin de la autorizacin final. Este interfaz implementa un mtodo decide que bsicamente lo que hace es recibir un objeto Authentication representando al principal que accede a la peticin, un objeto seguro (url o mtodo) y una lista de aributos con metadatos de seguridad (la lista de roles para los que se les puede dar acceso). El siguiente diseo es el definido para el componente AccesDecisionManager:
El diagrama representa el sistema de autorizacin de Spring Security. Este sistema est basado en un sistema de votos. Spring Security tiene tres implementaciones de AccessDecissionManager: AffirmativeBased, en el caso de recibir un nico voto positivo, se le da acceso al recurso. Se permite controlar el comportamiento en el caso que todos los votos son de abstencin. ConsensusBased, en este caso ser necesario que haya ms votos positivos que negativos para dar acceso al recurso protegido. Tambin se puede controlar el comportamiento si todos los votos son abstencin. UnanimousBased, en este caso ser necesario que todos los votos sean positivos para dar acceso al recurso. Se permite controlar el comportamiento en el caso que todos los votos son de abstencin. Los objetos AccessDecissionManager hacen uso de AccessDecissionVoter, actualmente Spring proporciona dos clases de este objeto: RoleVoter, comprueba cada rol que protege el recurso comprobndolo con el principal que realiza la peticin. Si se tiene uno de los roles devolver un AffirmativeBased AccessDecissionManger, as con uno bastar para dar acceso. AuthenticatedVoter, Este componente permite diferenciar entre acceso annimo, completamente autenticado o autenticado mediante remember-me. As podr acceder a
129
pginas que necesitan tener un comportamiento diferente para usuarios autenticados de los que no. Todos los componentes del mdulo de autorizacin de Spring Security permiten ser extendidos para proveer mecanismos de autorizacin especficos o ms profundos.
En Spring podemos observar cuatro maneras de securizar las ejecuciones de mtodos dentro de Spring Security, Configuracin de seguridad a nivel global usando namespace, a nivel de bean, usando el componente MethodSecurityInterceptor y basada en anotaciones.
131
133
Captulo 7:
135
Contenido
Qu vamos a hacer? ................................................................................................................ 137 Por dnde empiezo? ............................................................................................................... 137 Maven........................................................................................................................................ 137 Web.xml .................................................................................................................................... 138 Ficheros de definicin de Beans ................................................................................................ 140 Campus-servlet.xml ................................................................................................................... 140 Controllers.xml .......................................................................................................................... 143 Campus-hibernate.xml .............................................................................................................. 143 Campus-security.xml ................................................................................................................. 147 Objetos de acceso a datos (DAO) y el modelo a persistir ......................................................... 150 Spring MVC ................................................................................................................................ 154 Controllers ............................................................................................................................. 154 Ajax con Spring MVC ............................................................................................................. 155 Vista ........................................................................................................................................... 156 Resolutores............................................................................................................................ 156 Tags JSP ................................................................................................................................. 157 Form tag ............................................................................................................................ 157 Spring tag........................................................................................................................... 158 Security tag........................................................................................................................ 158 Binding con WebDataBinder ..................................................................................................... 159 Uso y creacin de CustomEditor ........................................................................................... 159 Creando CustomEditors con PropertyEditorSupport ............................................................ 160 Uso de WebBindingInitializer: La clase CampusBindingInitializer ........................................ 162 Uso de la anotacin @InitBinder .......................................................................................... 163
137
Qu vamos a hacer?
A lo largo de este libro, hemos hecho referencias acadmicas como estudiantes, profesores o la universidad de Alcal. De modo que es bastante oportuno hacer una aplicacin de campus virtual que implemente cada una de las funcionalidades explicadas. As, esta aplicacin tendr que dar uso de las funcionalidades de Spring en materia de conexin de beans (gracias al contenedor), aspectos, persistencia de datos, web y seguridad.
Maven
Vamos a utilizar maven, una herramienta de software para la gestin y construccin de proyectos Java. Es similar en funcionalidad a Apache Ant, pero tiene un modelo de configuracin de construccin ms simple, basado en un formato XML. Maven utiliza un Project Object Model (POM) para describir el proyecto de software a construir, sus dependencias de otros mdulos y componentes externos, y el orden de construccin de los elementos. Viene con objetivos predefinidos para realizar ciertas tareas claramente definidas, como la compilacin del cdigo y su empaquetado. En el fichero pom.xml definiremos, entre otras cosas las dependencias de Spring, el siguiente cdigo es un extracto del fichero /pom.xml:
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${org.springframework-version}</version> <exclusions> <!-- Exclude Commons Logging in favor of SLF4j --> <exclusion> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${org.springframework-version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>${org.springframework-version}</version> </dependency>
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${org.springframework-version}</version> </dependency> <dependency> <groupId>org.springframework.webflow</groupId> <artifactId>spring-webflow</artifactId> <version>${org.springwebflow-version}</version> </dependency> <dependency> <groupId>org.springframework.webflow</groupId> <artifactId>spring-js</artifactId> <version>${org.springwebflow-version}</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-web</artifactId> <version>3.0.5.RELEASE</version> <exclusions> <!-- Exclude Commons Logging in favor of SLF4j --> <exclusion> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> </exclusion> </exclusions> <type>jar</type> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-taglibs</artifactId> <version>${org.springsecurity-version}</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-config</artifactId> <version>${org.springsecurity-version}</version> <exclusions> <!-- Exclude Commons Logging in favor of SLF4j --> <exclusion> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> </exclusion> </exclusions> </dependency>
Con este cdigo tendremos todas las libreras de Spring que utilizaremos ms adelante.
Web.xml
El fichero web.xml configura nuestra aplicacin, y es el punto en el que Spring arrancar. Para ello tendremos que incluir tanto el DispatcherServlet del framework MVC, el Listener de contexto, el filtro de la seguridad as como la configuracin del contexto. El siguiente cdigo muestra cmo configurar el contexto:
139
<context-param> <param-name>contextConfigLocation</param-name> <param-value> /WEB-INF/spring/root-context.xml </param-value> </context-param> <listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener>
Este Servlet ser el punto de partida para la carga de todos los componentes de Spring. Buscar en la raz de la aplicacin un fichero llamado campus-servlet.xml que contendr la definicin del contenedor de Spring. Para poder implementar la seguridad tendremos que incluir los siguientes filtros:
<filter> <filter-name>springSecurityFilterChain</filter-name> <filterclass>org.springframework.web.filter.DelegatingFilterProxy</filterclass> </filter> <filter-mapping> <filter-name>springSecurityFilterChain</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
Uno de los problemas ms habituales que existe en las aplicaciones web es la codificacin, los servidores de aplicaciones han de ser configurados explcitamente para que transmitan y reciban en una determinada codificacin. De este modo, Spring hace posible que cada aplicacin tenga su configuracin o incluso que se tengan varias configuraciones para una misma aplicacin. El siguiente filtro servir para forzar la codificacin, en este caso UTF-8.
<filter> <filter-name>CharacterEncodingFilter</filter-name> <filter-class> org.springframework.web.filter.CharacterEncodingFilter </filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value>
Con esta definicin creamos las herramientas necesarias para poder empezar nuestra aplicacin. El siguiente paso ser crear y configurar nuestro fichero campus-servlet.xml.
Campus-servlet.xml
Vamos a explicar cules son y para qu se utilizan los componentes definidos en este fichero, que como ya comentamos contendr los componentes relacionados con la parte web. El primer elemento que nos encontramos es el siguiente:
<mvc:resources mapping="/resources/**" location="/resources/" />
Con este tag lo que estamos diciendo es donde est la ruta que contendr ficheros estticos y cul ser su localizacin web. En este caso estamos diciendo que estar en la carpeta llamada resources que est en la raz de la aplicacin y que tendr la misma localizacin web. El siguiente elemento que nos encontramos proporcionar capacidades de anotaciones:
<mvc:annotation-driven/>
Los siguientes elemento son indispensables ya que los utilizaremos para resolver las vistas, son los resolutores de vistas. Vamos a definir dos, ResourceBundleViewResolver e InternalResourceViewResolver. Utilizaremos dos porque vamos a mezclar dos maneras de
141
resolver las vistas y las ordenaremos. La que va en primer orden utiliza un fichero properties que define dnde estn y qu clase se hace cargo de resolver cada uno de los nombres de las vistas, lo vamos a hacer as porque es una manera elegante de poder utilizar plantillas de Apache Tiles. El segundo es ms sencillo, simplemente buscar el nombre de la vista aadiendo el prefijo /WEB-INF/jsp/ y el sufijo .jsp. Como vemos definimos dos propiedades, basename que ser el fichero .properties donde se encontrar la correspondencia entre vista y clase resolutora y order, que indica el orden.
<bean id="viewResolver" class="org.springframework.web.servlet.view.ResourceBundleViewResolver" <property name="basename" value="views"/> <property name="order" value="0"/> </bean>
>
El siguiente resolutor, como ya hemos comentado har una correspondencia entre cada nombre de vista y la clase resolutora encargada. En este caso el orden es 1, posterior al anterior resolutor definido.
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" p:prefix="/WEB-INF/jsp/" p:suffix=".jsp" p:order="1"/>
Gracias a la clase TilesCOnfigurer de Spring podremos integrar tiles como si de otro bean se tratase, beneficindonos de la inyeccin de dependencia. El siguiente cdigo muestra como hacerlo:
Como vemos hemos de asignarle la propiedad del fichero .xml donde estarn las plantillas definidas. El siguiente tag sirve para importar los beans de otro fichero como si estuviesen declarados en este. Como ya dijimos anteriormente, hemos dividiremos los componentes web y los controladores en dos ficheros, es precisamente este fichero Controllers.xml el que contendr los controladores.
<import resource="spring/controllers.xml" />
En muchas aplicaciones estamos acostumbrados a ver cdigos de excepciones ocurridas en la ejecucin de nuestra peticin, y por defecto, los servidores de aplicaciones muestran esa excepcin al usuario, que bien podra tener poco que ver con el equipo de desarrollo y poco le importan las trazas expuestas. De esta forma, la clase SimpleMappingExceptionResolver lo que hace es redireccionar a distintas vistas cuando ocurra determinada excepcin y as mostrar una vista ms amigable al usuario.
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionR esolver"> <property name="exceptionMappings"> <props> <prop key= "org.springframework.web.servlet.PageNotFound"> pageNotFound </prop> <prop key= "org.springframework.dao.DataAccessException"> dataAccessFailure </prop> <prop key= "org.springframework.transaction.TransactionException"> dataAccessFailure </prop> </props> </property> </bean>
La internacionalizacin es un aspecto crucial en toda aplicacin, y ms para entornos web. Gracias a la clase ReloadableResourceBundlemessageSource podemos configurar ficheros de propiedades que contengan nuestros mensajes internacionalizados. El siguiente cdigo muestra cmo definir este bean:
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMes sageSource"> <property name="defaultEncoding" value="UTF-8" /> <property name="cacheSeconds" value="10" /> <property name="fallbackToSystemLocale" value="false" /> <property name="basenames"> <list> <value>/WEB-INF/properties/messages</value> </list> </property> </bean>
Como vemos existen diversos parmetros como defaultEncoding, para la codificacin, cacheSeconds, que determina cada cuanto se recarga el fichero etc. Otros beans necesarios en la internacionalizacin son el LocaleChangeInterceptor y el SessionLocaleResolver definidos con el siguiente cdigo:
<mvc:interceptors> <bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor" p:paramName="locale" /> </mvc:interceptors> <bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver" />
143
Una de las funcionalidades de Spring MVC indispensables es el enlace de datos, gracias a InitBinder podremos enlazar parmetros de peticin con objetos completos. La clase AnnotationMethodHandlerAdapter se sirve de una clase que habremos de implementar ( en nuestro caso CampusDindingInitializer) en la que podremos definir nuestros enlaces personalizados, como por ejemplo recibir un identificador y devolver un objeto persistido.
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethod HandlerAdapter"> <property name="webBindingInitializer"> <bean class="com.campus.web.binding.campusBindingInitializer"/> </property> </bean> </beans>
Controllers.xml
Como ya hemos comentado, este fichero estar contenido en el campus-servlet y en l definiremos los controladores. En nuestro caso habr dos tipos de definicin, la que otorga el tag <mvc:view-controller> que simplemente redirecciona una deterinada ruta a un fichero jsp o <context:component-scan> que define un paquete base en el que se escanearn los controladores, en nuestro caso com.campus.web.controllers El siguiente cdigo es una muestra de cmo estos estn definidos:
<mvc:view-controller path="/login" /> <context:component-scan base-package="com.campus.web.controllers" />
Campus-hibernate.xml
En este fichero definiremos todos los beans que tengan relacin con la persistencia. Como bien indica el nombre del fichero el motor de persistencia ser Hibernate, sin embargo y gracias a la modularizacin de Spring, mltiples motores de persistencia podrn ser configurados o podremos tener varios ficheros, uno para JPA, iBatis, etc e intercambiarlos segn nuestras necesidades. Esto es posible gracias a que los dems beans que esperen los DAO definidos aqu recibirn la implementacin definida, pero ellos operarn con la interfaz, desconociendo cmo estos DAO operan. El primer bean definido en este fichero ser un property placeholder, cuyo fichero ser jdbc.properties. El contenido de este fichero sern los distintos parmetros de configuracin para la fuente de datos as como para el sessionFactory de hibernate.
El siguiente bean definido es el dataSource. En esta aplicacin de ejemplo hemos creado un dataSource bsico, si dispusisemos de un dataSource en nuestro servidor podramos acceder a l tambin definiendo su ruta. Como no es nuestro caso definiremos un dataSource de tipo BasicDataSource de apache commons.
<context:property-placeholder location="classpath:jdbc.properties"/>
El siguiente bean a definir ser el data source, en nuestro caso utilizaremos BasicDataSource de Apache.
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" p:driverClassName="${jdbc.driverClassName}" p:url="${jdbc.url}" p:username="${jdbc.username}" p:password="${jdbc.password}"/>
Una vez definida el data source podremos definir un sessionFactoryBean de Hibernate, en nuestro caso hemos definido el AnnotationsSessionFactoryBean. Muy cmodo porque simplemente le indicaremos el paquete del modelo donde escanar los objetos a persistir y el se encargar de leerlos.
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationS essionFactoryBean" p:dataSource-ref="dataSource" p:packagesToScan="com.campus.model"> <property name="hibernateProperties"> <props> <prop key="hibernate.dialect">${hibernate.dialect}</prop> <prop key="hibernate.show_sql">${hibernate.show_sql}</prop> <prop key= "hibernate.generate_statistics">${hibernate.generate_statistics} </prop>
145
<prop key="hibernate.hbm2ddl.auto">update</prop> </props> </property> <property name="eventListeners"> <map> <entry key="merge"> <bean class="org.springframework.orm.hibernate3.support.IdTransferringMergeE ventListener"/> </entry> </map> </property> </bean>
El siguiente bean es un manager de transaccin, en concreto el HibernateTransactionManager. Este manager nos permitir hacer operaciones transaccionales asegurando la atomicidad.
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager" p:sessionFactory-ref="sessionFactory"/>
Como vimos en el captulo de persistencia, Spring otorga varias soluciones para facilitar la creacin de DAO, una de ellas son las plantillas, el siguiente cdigo configura una plantilla.
<bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate" p:sessionFactory-ref="sessionFactory"/>
Definiremos nuestros DAO de hibernate con anotaciones en el paquete com.campus.orm.dao y la manera que tenemos de decirle a Spring que busque en ese paquete para encontrar beans es la siguiente:
<context:component-scan base-package="com.campus.orm.dao.hibernate"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/> </context:component-scan>
Hemos visto como se definen los distintos beans pertenecientes a la parte web as como a la persistencia. La siguiente parte a declarar ser la seguridad. En este fichero definiremos mediante aspectos la configuracin del transactionManager, el bean necesario para las transacciones.
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager" p:sessionFactory-ref="sessionFactory"/> <aop:config> <aop:pointcut id="all-service-methods" expression="execution(* com.campus.orm.dao.*Dao.*(..))"/> <aop:advisor advice-ref="local-tx-advice" pointcut-ref="allservice-methods"/> </aop:config> <tx:advice id="local-tx-advice" transactionmanager="transactionManager"> <tx:attributes> <tx:method name="get*" propagation="REQUIRED" isolation="READ_COMMITTED" read-only="true"/> <tx:method name="find*" propagation="REQUIRED" isolation="READ_COMMITTED" read-only="true"/> <tx:method name="save*" propagation="REQUIRED"/> <tx:method name="remove*" propagation="REQUIRED"/> <tx:method name="add*" propagation="REQUIRED"/> <tx:method name="update*" propagation="REQUIRED"/> <tx:method name="modify*" propagation="REQUIRED"/> <tx:method name="remove*" propagation="REQUIRED"/> <tx:method name="*"/> </tx:attributes> </tx:advice>
As de simple, con esta configuracin diremos a Spring que cada vez que se ejecute un mtodo de una clase Dao del paquete com.campus.orm.dao y que responda a cada uno de los mapeos hechos con AOP realizar determinadas operaciones. Por ejemplo, con un mtodo sabe se har transaccin y propagacin. De nada servir esta configuracin si no tenemos objetos DAO. Ellos sern nuestra conexin entre base de datos y aplicacin. Ms tarde veremos cmo se definen estos DAO.
147
Campus-security.xml
La seguridad es una parte imprescindible en toda aplicacin, especialmente las web. A menudo vemos una infinidad de filtros tediosos y de difcil compresin cuya funcin es impedir que usuarios malintencionados alteren el funcionamiento de la aplicacin o que obtengan informacin no permitida. Spring security, como ya comentamos en el captulo dedicado a ello, facilita todo esto otorgando una gran potencia. El tag http permite que bajo patrones de peticin se precise tener o no un rol especfico. En la siguiente definicin vemos que por ejemplo para los recursos no se comprueba ningn permiso, al igual que para la parte pblica o el logeo. Sin embargo para todas las rutas que comiencen por admin slo un usuario con rol ADMIN tendr acceso a ellas, as como teacher si tiene el rol TEACHER etc. Tambin podemos definir cul ser nuestro formulario de logeo as como capacidades como la posibilidad de introducir una cookie para recordarte en una mquina, o que haya un mximo de sesiones abiertas para un usuario as como se puede configurar cal ser la ruta para salir de la aplicacin y su posterior redireccin.
<http auto-config="true" use-expressions="true"> <intercept-url pattern="/resources/**" filters="none"/> <intercept-url pattern="/public/**" filters="none" /> <intercept-url pattern="/login*" access="permitAll" /> <intercept-url pattern="/admin/**" access="hasRole('ADMIN')" /> <intercept-url pattern="/teacher/**" access="hasRole('TEACHER')" /> <intercept-url pattern="/student/**" access="hasRole('STUDENT')" /> <intercept-url pattern="/**" access="hasRole('ADMIN') or hasRole('TEACHER') or hasRole('STUDENT')" /> <form-login login-page="/login" login-processing-url="/loginProcess" default-target-url="/home" authentication-failure-url="/login?login_error=1" /> <logout invalidate-session="true" logout-url="/logout" logout-success-url="/login?loggedout=true" /> <remember-me /> </http>
En este fichero tambin definimos el authenticationManager y le asignamos un proveedor, en nuestro caso el DAO abstractUserDao el cul implementa UserDetailService. Tambin definiremos un codificador de contrasea que utilizar el algoritmo de cifrado sha.
<authentication-manager> <authentication-provider user-service-ref="abstractUserDao"> <password-encoder hash="sha"/> </authentication-provider> </authentication-manager>
Diremos a Spring que l cifrado a utilizar ser el algoritmo de autenticacin sha gracias al tag <password-encoder>.
El objeto AbastractUser tendr que implementar la clase UserDetails, el cdigo siguiente es la implementacin de los mtodos:
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, targetEntity = Authority.class, mappedBy = "user") public Collection<GrantedAuthority> getAuthorities() { return authorities; }
149
@Override @Transient public boolean isAccountNonLocked() { return true; } @Override @Transient public boolean isCredentialsNonExpired() { return true; }
De igual modo hemos de crear una ltima clase que implemente GrantedAuthority, esta ser Authority. Esta clase ser la encargada de guardar cada uno de los roles, el siguiente cdigo pertenece a Authority.java:
@Override @Transient public String getAuthority() { return this.value.name(); }
Slo se ha de implementar este mtodo, el cul devolver el nombre del rol en cuestin. La siguiente clase, AutorityType, muestra cuales son los distintos roles:
package com.campus.model.security; public enum AuthorityType { ADMIN, TEACHER, STUDENT }
En nuestra aplicacin slo tendremos tres tipos de roles, el rol de estudiante, el de profesor y el de administrador. El administrador ser el encargado de crear usuarios, ya sean estudiantes o profesores, crear carreras y asignaturas. Al crear una asignatura el administrador asignar sta a un profesor para que la imparta. El profesor ser el encargado de crear lecciones para cada una de sus asignaturas as como preguntas relacionadas con esas lecciones. Tendr listados de sus alumnos as como podr ver estadsticas de las respuestas. Tambin tendr la posibilidad de responder dudas surgidas sobre temas. El estudiante ser el encargado de apuntarse a asignaturas, ver y estudiar las lecciones, apuntar dudas sobre estas y responder preguntas.
151
this.id = id; } @Override @Transient public boolean isNew() { return this.getId() == null; } }
Con la anotacin @MappedSuperclass diremos que es una clase padre que no ser mapeada. Como vemos tan solo contiene un identificador como atributo. Creda esta clase estamos preparados para ver nuestra clase BaseDao, el siguiente cdigo pertenece a ella:
public interface BaseDao<T extends BaseEntity> { public Long save(T entity); public T get(Long id); public T load(Long id); public List<T> getAll(); public void update(T entity); public void delete(T entity); public void delete(Long id); public void clear(); public T getFetch(Long id); }
public abstract class BaseDaoHibernate<T extends BaseEntityCampus> extends HibernateDaoSupport implements BaseDao<T> { protected Class<T> entityClass; public BaseDaoHibernate() { super(); } public BaseDaoHibernate(Class<T> entityClass) { super(); this.entityClass = entityClass; }
@Override public void clear() { getHibernateTemplate().clear(); } @Override public void delete(T entity) { getHibernateTemplate().delete(entity); getHibernateTemplate().flush(); } @SuppressWarnings("unchecked") @Override public void delete(final Long id) { getHibernateTemplate().execute(new HibernateCallback() { public Object doInHibernate(Session session) throws HibernateException, SQLException { T entity = (T) session.load(entityClass, id); session.delete(entity); return null; } }); } @Override public T get(Long id) { return (T) getHibernateTemplate().get(entityClass, id); } @Override public T load(Long id) { return (T) getHibernateTemplate().load(entityClass, id); } @Override public List<T> getAll() { return getHibernateTemplate().loadAll(entityClass); } @Override public Long save(T entity) { getHibernateTemplate().saveOrUpdate(entity); return entity.getId(); } @Override public void update(T entity) { getHibernateTemplate().update(entity); } public void flush() { getHibernateTemplate().flush(); } }
Como vemos existe un mtodo constructor que tiene como parmetro una clase de tipo entidad base. Esta ser la clase de la cual heredarn el resto de los objetos DAO como podemos comprobar en el siguiente cdigo con StudentDaoHibernate:
153
extends
BaseDaoHibernate<Student>
La etiqueta @Repository dir a Spring que esta clase ser un bean cuyo nombre ser studentDao, ms tarde a la hora de utilizar este dao ser tan sencillo como utilizar la anotacin @Autowired, el siguiente cdigo muestra a un cmo esta clase ser inyectada:
@Autowired private StudentDao studentDao;
La anotacin @Autowired dir a Spring que el tipo de este objeto es un bean definido en el contenedor y que es sujeto de ser inyectado.
Spring MVC
Como ya dijimos, esta aplicacin ser web y har uso de la herramienta que Spring otorga para contruir aplicaciones web, Spring MVC.
Controllers
Tras ver cmo damos soporte a los componentes de Spring MVC con el fichero CampusServlet.xml es tiempo de ver el tipo ms utilizado en Spring, el Controller. En anteriores versiones de Spring, los controladores tenan que ser definidos en el fichero xml y tenan que heredar de alguna de las clases de soporte de Spring. Ahora esto se ha acabado gracias a la anotacin @Controller, con ella cualquier POJO podr ser un controlador sin necesidad de heredar clases especficas de Spring. El siguiente cdigo de la aplicacin muestra un ejemplo sencillo de controlador:
@Controller @RequestMapping("/subjectProfile") public class SubjectProfileController { @Autowired private SubjectDao subjectDao; @InitBinder public void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) throws Exception { binder.registerCustomEditor(Subject.class, new SubjectCustomEditor(this.subjectDao)); }
@RequestMapping("/{id}") public ModelAndView subjectProfile(@PathVariable("id") Subject subject){ ModelAndView mav = new ModelAndView("subjectProfile"); mav.addObject("subjectProfile",subject); return mav; } }
En este ejemplo hay varios conceptos a destacar, el primero sera ver qu sencillo es decirle a Spring que la clase es un controlador, simplemente con la anotacin @Controller. Spring buscar en los paquetes de escaneo definidos en el fichero xml. La anotacin @RequestMapping hace referencia a qu ruta mapear este controlador, es opcional el que est encima de la definicin de la clase, si es as ser la raz para el resto de los mtodos. Cada mtodo que tenga la anotacin @RequestMapping() ser mapeado por Spring como un mtodo del controlador. Los mtodos de un controlador sern sujetos de ser utilizados dependiendo de la URL de la peticin y del mtodo de la peticin (es decir, POST, GET,..etc).
155
Es decir, puede haber mtodos con la misma URL pero con distintos RequestMethod. En el ejemplo existe otra anotacin a destacar, se trata de @InitBinder este mtodo ser del que se sirva para el enlace de los valores de los parmetros de la peticin con objetos Java. En el ejemplo se define un objeto de tipo CustomEditor que esperar un identificador (un id de una entidad) ir a la base de datos y recuperar la entidad en concreto para nuestro mtodo. Tras la ejecucin del mtodo vemos que devuelve un objeto ModelAndView, este objeto es bsico para comprender el funcionamiento de Spring MVC. ModelAndView definir qu vista (por ejemplo un fichero jsp) ser la que se le enviar en la respuesta y que modelo es necesario para mostrar dicha vista. En nuestro caso la vista ser subjectProfile y contendr el objeto subjectProfile de tipo subject como modelo. Tambin podremos devolver un objeto String, de este modo simplemente Spring buscar la vista y la devolver. Si en los parmetros del mtodo hemos pedido el objeto Model Spring lo inyetar y si hacemos alguna modificacin esta estar presente en la vista designada por la cadena. El siguiente cdigo es un ejemplo de lo descrito:
@RequestMapping(method = RequestMethod.GET) public String setupForm(Model model) { model.addAttribute("lessonDataPicker", new LessonDataPicker()); return "teacher/createLesson"; }
En este mtodo se devuelve el nombre lgico de la vista pero la modificacin del objeto Model estar presente en la vista.
Vemos como el mtodo devuelve una cadena que ser un objeto JSON.
Vista
Para explicar la vista en Spring hemos de tener dos conceptos claros, los resolutores de vista y los tags de Jsp de spring.
Resolutores
Los resolutores de vista ser el nexo entre una definicin lgica y un fsica de la vista en concreto, podremos utilizar distintas tecnologas como Velocity o FreeMarker, pero en nuestra aplicacin de ejemplo utilizaremos la ms utilizada por al comunidad, los JSP. Para la resolucin de las vistas de lgico a fsico utilizaremos dos clases, definidas en campusservlet.xml:
<bean id="viewResolver" class="org.springframework.web.servlet.view.ResourceBundleViewRe solver" > <property name="basename" value="views"/> <property name="order" value="0"/> </bean>
E InternalResourceViewResolver:
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolv er" p:prefix="/WEB-INF/jsp/" p:suffix=".jsp" p:order="1"/>
He elegido dos debido a que voy a combinar el uso del framework para la vista de Apache Tiles con ResourceBundleViewResolver y para el resto con InternalResourceViewResolver. Para poder mapear con ResourceBundleViewResolver necesitaremos mapear el bean tilesConfigurer, tener un fichero de propiedades donde definiremos las vistas y el fichero de plantillas de tiles. La clase tilesConfigurer se define en campus-servlet.xml de la siguiente manera:
<bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles2.TilesConfigurer" p:definitions="/WEB-INF/tiles/templates.xml"/>
157
EL siguiente cdigo es un extracto de views.properties y muestra cmo mapearemos cada una de las vistas lgicas a usar por Spring con cada una de las plantillas de Tiles:
home.(class)=org.springframework.web.servlet.view.tiles2.TilesView home.url=home
El nombre (en este caso home) ser el nombre lgico de la vista, el atributo .(class)= hace referencia a la clase a utilizar por Spring y el atributo url ser la plantilla de Tiles, (tambin home). As en el fichero templates.xml podremos ver las diferentes vistas mapeadas con plantillas de tiles, como muestra el ejemplo:
<definition name="home" extends=".teacher"> <put-attribute name="url" value="home"/> <put-attribute name="content" value="/WEB-INF/jsp/home.jsp" /> </definition>
De este modo podremos utilizar otras tecnologas y combinarlas con nuestro framework. Este resolutor se ejecutar en primera posicin, es decir a mismos nombres lgicos este ser el encargado de resolver la vista. El siguiente resolutor es el InternalResourceViewResolver, el comportamiento de este es muy sencillo, a el nombre lgico de la vista se le aadirn un prefijo y un sufijo, as si se pregunta por login se devolver la vista /WEB-INF/jsp/login.jsp. Este resolutor se ejecutar tras descartarse la vista en el anterior resolutor.
Tags JSP
Spring MVC y Spring security tiene una serie de tags que nos sern muy tiles a la hora de construir nuestras aplicaciones. En esta aplicacin de ejemplo utilizaremos principalmente las derivadas de <form: *>, las de <spring:*> as como las de <security:*>. Form tag
Los tag <form:*> sirven principalmente para el mapeo y enlace de elementos de un formulario y estarn presentes en cada uno de los formularios de la aplicacin. Las principales utilidades son las siguientes: <Form:form> definir el formulario en cuestin y lo enlazar con un controlador as como con el objeto comando u objeto del modelo a enlazar. <form:input> este junto a otros como <form:select> , <form:checkboxes> o <form:hidden> sern cada uno de los elementos del formulario los cuales Spring enlazar con la ruta definida en path. <form:errors> este objeto imprimir los errores encontrados tras la validacin del formulario en la parte servidora.
Spring tag Con <spring:*> tendremos mltiples funcionalidades como el enlace de objetos , la creacin de URL absolutas, evaluacin de expresiones, trasformaciones de variables a cadenas etc. Security tag Con los tags que proveee <security:> podremos realizar comprobaciones de roles para el principal, es decir, para el usuario que haya realizado la peticin. Este tag lo utilizaremos mucho para mostrar u ocultar informacin dependiendo del tipo de usuario que seas. Por ejemplo las opciones del men no sern iguales para un profesor que para un alumno al igual que el alumno no podr ver resultados de las preguntas, un profesor no va a responderlas. Veamos un ejemplo con el siguiente cdigo perteneciente a menu.jsp.
<div id="tabsQuestionHome"> <security:authorize ifAnyGranted="STUDENT"> <p><a href='<spring:url value="/student/answerQuestion" htmlEscape="true" />'> <fmt:message key="ANSWER_QUESTIONS"/></a></p> </security:authorize> <security:authorize ifAnyGranted="TEACHER"> <p><a href='<spring:url value="/teacher/createQuestion" htmlEscape="true" />'> <fmt:message key="CREATE_QUESTION"/></a></p> <p> <a href='<spring:url value="/teacher/statistics/select" htmlEscape="true" />'> <fmt:message key="STATISTICS"/></a></p> <p><a href='<spring:url value="/teacher/faqs/faqsSelect" htmlEscape="true" />'> <fmt:message key="ANSWERS"/></a></p> </security:authorize> </div>
Este ejemplo es el cdigo de parte del men, para ser ms concreto de las preguntas. Como hemos dicho un alumno slo debera poder ver la opcin de responder a preguntas y un profesor slo debera poder crear preguntas o ver las estadsticas de ellas as como las respuestas de las preguntas. Evidentemente de nada sirve que ocultemos su enlace si luego es accesible si lo tecleamos en la barra de direcciones, para impedir esto el fichero campus-security.xml muestra cuales son los roles permitidos a segn que rutas.
159
Como vemos existen propiedades de diversos tipos, pero llama la atencin cmo Spring podr enlazar desde un formulario web un objeto complejo como pueda ser Subject. Para tipos comunes como Date nos podemos servir de CustomEditors del framework de Spring, que intentarn hacer un casting de los objetos y nos devolver o el objeto esperado o errores al forzar el casting (visibles posteriormente con <form:errors). La respuesta a cmo hace Spring para traerse objetos complejos es tan simple como pasarle el identificador de base de datos al que hace referencia y si existe un customEditor definido para Subject este tendr que buscar en la BBDD y devolver el objeto. Evidentemente el enlace puede ser tan complejo como el desarrollador quiera y no tiene por qu servirse de DAO.
En el podemos ver que se crea un objeto de tipo CustomEditor para la clase Date. As cuando Spring espere un objeto de tipo Date en un datapicker o como cualquier tipo de parmetro de la peticin Spring buscar y utilizar esta clase para su enlace. Es posible enlazar nuestras clases, as con la ayuda y herencia de PropertyEditorSupport se podr crear clases para ello.
Como vemos el constructor precisa de la implementacin del objeto de tipo abstracto BaseDao el cual har uso para obtener la entidad esperada, as simplemente pasndole el identificador de base de datos enlazar la entidad completa. Luego la creacin de un CustomEditor para un objeto del modelo ser tan sencillo como el siguiente cdigo:
161
Como vemos simplemente se hace una llamada al constructor con el Dao correspondiente. Una vez definidos nuestros CustomerEditors podremos utilizarlos de dos maneras, con objetos BindingInitializer o con los mtodos anotados con @InitBinder de nuestros controladores.
public CampusBindingInitializer() { } @Override public void initBinder(WebDataBinder binder, WebRequest request) { SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy"); dateFormat.setLenient(false); binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false)); binder.registerCustomEditor(String.class, new StringTrimmerEditor(false)); binder.registerCustomEditor(Long.class, new CustomNumberEditor(Long.class, true)); } }
Asi cuando se esperen determinados objetos Spring los enlazar sirvindose de estos CustomEditor.
163
Como vemos en el cdigo, la manera de definirlo es igual que dentro del mtodo initBinder de los WebInitializer.
165
Apndice A: Hibernate
Configuracin y puesta en marcha del framework
En este apndice veremos las caractersticas bsicas del marco de trabajo, veremos cmo crear objetos persistentes y definir el mapeo ORM. Javier Sevilla Snchez
167
Contenido
Introduccin .............................................................................................................................. 168 Creacin del esquema ............................................................................................................... 168 Con un esquema existente .................................................................................................... 169 Sin que exista un esquema.................................................................................................... 169 Con un esquema Parcial ........................................................................................................ 169 Mapeo de Objetos..................................................................................................................... 169 Atributo Id ............................................................................................................................. 170 Propiedades........................................................................................................................... 170 Formas de mapear objetos dentro de objetos ......................................................................... 171 Relaciones one-to-one .......................................................................................................... 171 Relaciones many-to-one ....................................................................................................... 171 Relaciones one-to-many ....................................................................................................... 172 Relacin many-to-many ........................................................................................................ 173 Configuracin de Hibernate: Fichero .cfg.xml........................................................................... 173 Inicializar el objeto Session ....................................................................................................... 174 Operaciones tpicas con el objeto Session ................................................................................ 175 Insercin ................................................................................................................................ 175 Actualizacin ......................................................................................................................... 175 Consulta................................................................................................................................. 176 Borrado.................................................................................................................................. 176
Introduccin
Hibernate es una herramienta de Mapeo objeto-relacional que facilita el mapeo de atributos entre una base de datos relacional tradicional y el modelo de objetos de nuestra aplicacin, mediante archivos declarativos (XML) que permiten establecer estas relaciones. Fue desarrollada inicialmente por la iniciativa de un grupo de desarrolladores dispersos por el mundo. Ms tarde JBoss financiara el proyecto. Con la informacin que otorgamos a Hibernate relativa a las tablas, relaciones, etc. le permite a la aplicacin manipular los datos de la base operando sobre objetos, con todas las caractersticas de la POO. Hibernate convertir los datos entre los tipos utilizados por Java y los definidos por SQL. Hibernate genera las sentencias SQL y libera al desarrollador del manejo manual de los datos que resultan de la ejecucin de dichas sentencias, manteniendo la portabilidad entre todos los motores de bases de datos con un ligero incremento en el tiempo de ejecucin. ste es un framework de software libre bajo licencia GNU-LGPL. Hibernate est diseado para ser flexible en cuanto al esquema de tablas utilizado, para poder adaptarse a su uso sobre una base de datos ya existente. Tambin tiene la funcionalidad de crear la base de datos a partir de la informacin disponible. Hibernate otorga un lenguaje de consulta de datos llamado HQL (Hibernate Query Language) y una API para construir las consultas programticamente criteria. Hibernate para Java puede ser utilizado en aplicaciones Java independientes o en aplicaciones Java EE, mediante el componente Hibernate Annotations que implementa el estndar JPA, que es parte de esta plataforma. Con lo que las principales caractersticas son: Framework Open Source. Independencia del motor de BBDD. Carga perezosa de objetos inicindolos bajo demanda. Capacidad de generar el modelo de datos a partir de modelo de objetos. Compatible con la especificacin JPA. Lenguaje propio unificado para las consultas (Hibernate Query Language) Api para realizar consultas Criteria. Utiliza la Programacin Orientada a Aspectos (AOP), interceptando los objetos y creando proxys. No incluye nuevos atributos a los POJOs simplemente genera interceptores.
169
Hibernate se sirve de un gran nmero de interceptores que crearn objetos de tipo Proxy. El tpico problema que surge al utilizar un nico modelo de dominio persistente con Hibernate es consultar los datos de nuestros objetos fuera de la sesin de Hibernate. Cuando esto ocurre nos dar una excepcin de sesin cerrada. Se recomienda utilizar el dominio de persistencia slo con los objetos que sigan el patrn DAO y generar un nuevo modelo de dominio para el resto de la aplicacin. Utilizando Hibernate Tools podremos personalizar la creacin de POJOs a partir del esquema de la base de datos.
Mapeo de Objetos
En Hibernate podremos mapear los objetos con ficheros xml o con anotaciones JPA. El nombre que reciben los ficheros xml en Hibernate es HBM. En los ficheros de mapeo se seala la relacin de la clase con la tabla del modelo entidad-relacin, el identificador del objeto que corresponder con la clave primaria, el mapeo de atributos del objeto con los campos de la tabla y el mapeo de los objetos relacionados existentes dentro del propio objeto grafo de objetos.
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class dynamic-insert="false" dynamic-update="false" mutable="true" name="es.uah.hibernateaddendum.model.Student" optimistic-lock="version" polymorphism="implicit" select-before-update="false" table="STUDENTS"> <id name="id" type="integer"> <column name="STUDENT_ID"/> </id> <property name="name"> <column name="STUDENT_NAME"/> </property> <set lazy="true" name="exams" table="STUDENTS_HAS_EXAMS"> <key> <column name="STUDENT_ID"/> </key> <many-to-many class="es.uah.hibernateaddendum.model.Exam" column="EXAM_ID"/> </set> <set lazy="true" name="subjects" table="STUDENTS_HAS_SUBJECTS"> <key> <column name="STUDENT_ID"/> </key> <many-to-many class="es.uah.hibernateaddendum.model.Subject" column="SUBJECT_ID"/> </set> </class> </hibernate-mapping>
En este fichero observamos diversos tags, en ellos se definen cual ser la correspondencia entre el objeto y las base de datos relacional. En el tag class vemos como se define la clase que ser es.uah.hibernateaddendum.model.Student, definimos cul ser la clave primaria su identificador as como la columna correspondiente.
Atributo Id
Para indicar el mapeo entre el/los atributos del objeto y la clave primaria de la tabla se utiliza el elemento id. En el anterior ejemplo el atributo id se le especifica la columna de la base de datos la cual est definida como primary key. Hibernate tiene varias implementaciones para la generacin de identificadores como increment, identity, native, assigned etc. Estos se definirn dentro del cuerpo de id con el tag genarator.
Propiedades
Para mapear los atributos utilizaremos el elemento property. Como vemos en la definicin del xml, la columna STUDENT_NAME tiene correspondencia con el atributo name del objeto.
<property name="name"> <column name="STUDENT_NAME"/> </property>
171
Como vemos los nombres del atributo de la clase y la columna de la tabla no han de coincidir. An que en el ejemplo no lo especificamos, se pueden aadir restricciones como por ejemplo no permitir valores nulos o el tamao.
En el diagrama vemos como un estudiante tiene n exmenes, que a su vez son realizados por m estudiantes. Un examen se compone de una nica asignatura, pero una asignatura puede tener varios exmenes. Cada asignatura tiene n temas que pertenecen exclusivamente a cada asignatura.
Relaciones one-to-one
Las relaciones one-to-one son aquellas que se producen cuando dos objetos se relacionan uno a uno. Cada tabla dispone de la clave primaria de la otra haciendo una referencia mutua. Este tipo de relacin no es habitual, debido a que la mayora de la informacin relacionada de esta forma estara en una sola tabla. En el ejemplo de la figura 1 vemos que no existe esta relacin. Esta relacin s se podra dar por ejemplo entre marido y esposa en culturas mongamas, ya que una persona slo puede estar casado con otra.
Relaciones many-to-one
Esta relacin se da cuando la tabla de la parte del muchos tiene referencias a la clave primaria de la tabla del uno. En esta relacin podremos incluir la instancia de otro objeto persistente que puede estar dado de alta en varios objetos persistentes. En nuestro ejemplo vemos como varios temas pertenecen a una nica asignatura o que existen varios exmenes para cada asignatura. En el modelo de clases vemos como cada examen tiene una asignatura el fichero de definicin hbm sera:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class dynamic-insert="false" dynamic-update="false" mutable="true" name="es.uah.hibernateaddendum.model.Exam" optimistic-lock="version" polymorphism="implicit" select-before-update="false" table="EXAMS"> <id name="id" type="integer"> <column name="EXAM_ID"/> </id> <property name="date" type="date"> <column name="EXAM_DATE"/> </property> <many-to-one class="es.uah.hibernateaddendum.model.Subject" fetch="join" name="subject"> <column name="SUBJECT_ID" not-null="true"/> </many-to-one> </class> </hibernate-mapping>
La relacin muchos a uno del examen indica que para una misma asignatura existirn varios exmenes.
Relaciones one-to-many
Esta relacin se da cuando el objeto que mapeamos contiene una coleccin, mapa, etc que hace que se hagan mltiples referencias. En el esquema relacional esto se convierte en una tabla es referenciada mltiples veces por otra, la parte del uno es referenciada por la parte de muchos. En nuestro ejemplo una asignatura est compuesta de varios temas, pero un tema es slo puede pertenecer a una asignatura, es decir la asignatura es la parte del uno y los temas sern la parte del muchos. El siguiente fichero mapea la clase asignatura y vemos como los objetos de la clase tema forman parte de un objeto que implementa el interfaz Set.
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="es.uah.hibernateaddendum.model.Subject" table="SUBJECTS" > <id name="id" type="integer" > <column name="SUBJECT_ID"/> </id> <property name="name" type="string"> <column name="SUBJECT_NAME"/> </property> <set inverse="true" lazy="true" name="content"> <key> <column name="SUBJECT_ID"/> </key> <one-to-many class="es.uah.hibernateaddendum.model.SubjectUnit"/> </set> </class> </hibernate-mapping>
Como vemos dentro del tag set hemos dado el valor true al atributo lazy. Este atributo ya lo habamos visto tambin en el hbm del estudiante. Hemos de tener cuidado con este tipo de
173
relacin ya que esta propiedad determinar cmo se cargan los objetos dentro de la sesin de Hibernate. Es decir cmo Hibernate instanciar los objetos. Si este atributo lo ponemos a false Hibernate cargar todos los objetos cuando se instancie el objeto lo que puede ser terrible si existen miles o millones de referencias. Un ejemplo podra ser que necesitsemos un estudiante para saber su nombre y tuvisemos lazy=false en el atributo de los exmenes realizados, esto hara que Hibernate instanciase todos los objetos Examen sin que fuesen necesarios.
Relacin many-to-many
Estas relaciones hacen que cada ocurrencia, en cualquiera de las dos entidades de la relacin, puede estar asociada con muchas (n) de la otra y viceversa. Es decir en caso un alumno realiza muchos exmenes y un examen es realizado por muchos alumnos. A la hora de pasar eso al modelo de objetos se traduce a una coleccin de exmenes en el objeto alumno y una coleccin de alumnos en la clase examen.
<set lazy="true" name="exams" table="STUDENTS_HAS_EXAMS"> <key> <column name="STUDENT_ID"/> </key> <many-to-many class="es.uah.hibernateaddendum.model.Exam" column="EXAM_ID"/> </set> <set lazy="true" name="subjects" table="STUDENTS_HAS_SUBJECTS"> <key> <column name="STUDENT_ID"/> </key> <many-to-many class="es.uah.hibernateaddendum.model.Subject" column="SUBJECT_ID"/> </set>
Una vez visto como mapear los objetos con Hibernate es preciso explicar cmo configurar Hibernate con todo aquello que necesite para hacerlo funcionar.
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration3.0.dtd"> <hibernate-configuration> <session-factory> <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property> <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property> <property name="hibernate.connection.url">jdbc:mysql://127.0.0.1:3306/HIBERNATE_ADDENDUM 2</property> <property name="hibernate.connection.username">ADMIN</property> <property name="hibernate.connection.password">ADMIN</property> <!--descomentar la siguiente linea si no existen las tablas--> <!--<property name="hibernate.hbm2ddl.auto">create</property>--> <mapping resource="es/uah/hibernateaddendum/model/Exam.hbm.xml"/> <mapping resource="es/uah/hibernateaddendum/model/Student.hbm.xml"/> <mapping resource="es/uah/hibernateaddendum/model/Subject.hbm.xml"/> <mapping resource="es/uah/hibernateaddendum/model/SubjectUnit.hbm.xml"/> </session-factory> </hibernate-configuration>
La propiedad hibernate.hbm2ddl.auto es la que usaremos para crear el esquema definido en los hbm, hacer un update o incluso crear y borrar las tablas lo cul ser til para cuando hagamos pruebas.
private static final SessionFactory sessionFactory; static { try { sessionFactory = new Configuration().configure( "hibernateAddendum.cfg.xml").buildSessionFactory(); } catch (Throwable ex) { System.err.println("Initial sessionFactory creation failed." + ex); throw new ExceptionInInitializerError(ex); } }
Tras obtener la sesin existe una serie de pasos para poder realizar toda operacin persistente. Tras la obtencin de la sesin crearemos la transaccin, operaremos en la base de datos y finalizaremos la transaccin y despus la sesin Hibernate. Todo ello lo haremos a travs del
175
objeto sesin. Como se habr percatado el lector, Spring nos facilita todo proceso reiterativo, es decir, todo proceso en el cul existan una serie de pasos fijos.
Insercin
Como ya hemos contado, existen un nmero de pasos que hemos de dar con las operaciones con el objeto Session. As primero recuperaremos el objeto sesin de la factora, empezaremos la transaccin, ejecutaremos el mtodo save() al cul le pasaremos el objeto que queremos persistir y despus forzaremos la insercin con el mtodo flush(), haremos un commit en la base de datos y cerraremos la sesin. El cdigo siguiente muestra los pasos descritos.
public void insert(Exam exam) throws ExamsException { try { Session session = getSessionFactory().openSession(); Transaction transaction = session.beginTransaction(); session.save(exam); session.flush(); transaction.commit(); session.close(); } catch (HibernateException hibernateException) { throw new ExamsException(INSERT, exam, hibernateException); } }
Actualizacin
Cuando hayamos modificado objetos existentes previamente el mtodo update() ser el encargado de realizar un upadate en la base datos. Es muy similar al mtodo save comentado, siguiendo los mismos pasos. Un cdigo de ejemplo sera el siguiente.
public void update(Exam exam) throws ExamsException { try { Session session = getSessionFactory().openSession(); Transaction transaction = session.beginTransaction(); session.update(exam); session.flush(); transaction.commit(); session.close(); } catch (HibernateException hibernateException) { throw new ExamsException(INSERT, exam, hibernateException); } }
A pesar de que la manera ms clara de actualizar un objeto es esta, existe tambin la posibilidad de recuperar un objeto con el mtodo get de la sesin, hacer los cambios necesarios y despus cerrar la sesin. Esto har el mismo efecto que el mtodo update.
Consulta
Disponemos de varias formas de obtener objetos de la base de datos. Podremos utilizar el mtodo get de la sesin pasndole la clase y el identificador o podemos hacer consultas ya sea en lenguaje HQL o mediante el API de criteria. El siguiente cdigo hace uso del mtodo del objeto sesin get.
public Exam getExam(int id) throws ExamsException { Exam exam = null; try { Session session = getSessionFactory().openSession(); exam = (Exam) session.get(Exam.class, id); session.close(); } catch (HibernateException he) { throw new ExamsException(GET_STUDENT, exam, he); } if (exam == null) { throw new ExamNotFoundException(id); } return exam; }
Para poder hacer consultas ms complejas Hibernate dispone de un lenguaje unificado de consulta llamado HQL. Gracias a este lenguaje podemos crear consultas que ms tarde Hibernate interpretar pasndolas al lenguaje de la base de datos utilizada gracias al dialecto seleccionado. El objeto Query es el encargado de la personalizacin de la consulta teniendo diversos mtodos que nos ayudaran a perfilar la consulta. Con HQL podremos generalizar las consultas a la base de datos haciendo que no tengamos que aprender cada lenguaje SQL de cada base de datos, pero, por el contrario, tendremos que aprender HQL. Criteria es un API que nos permite realizar consultas dentro del ORM utilizando objetos, as no necesitaremos saber HQL. Un ejemplo de cmo utilizamos criteria es el siguiente:
public Collection<Exam> getAllExams() { Session session = getSessionFactory().openSession(); Criteria criteria = session.createCriteria(Exam.class); Collection<Exam> exams = criteria.list(); session.close(); return exams; }
Borrado
Para eliminar de una entrada de la tabla utilizaremos el mtodo delete. Para eliminar primero deberemos recuperar el objeto y despus borrarlo. El siguiente cdigo muestra cmo hacerlo:
public void delete(int id) throws ExamsException { try { Session session = getSessionFactory().openSession(); Transaction transaction = session.beginTransaction();
177
Exam exam = (Exam) session.get(Exam.class, id); transaction.commit(); session.close(); } catch (HibernateException exception) { throw new ExamsException(DELETE); } }
179
CONCLUSIONES
181
Contenido
Experiencia personal ................................................................................................................. 183 Bibliografa ................................................................................................................................ 184
183
Experiencia personal
Como tantos otros Informticos, empec a trabajar antes de terminar mi carrera, Ingeniera Tcnica en Informtica de Gestin. Mi primer trabajo lo consegu como programador Java y fui poco a poco dndome cuenta de la importancia que tiene hacer aplicaciones mantenibles, desacopladas, con mdulos reutilizables y entendibles. Tras estos aos he utilizado diversos componentes, marcos de trabajo y herramientas para el desarrollo de aplicaciones pero ninguno tan extenso y tan til como Spring.
Cuando empec con este trabajo me gust la idea de poder involucrarme ms con una herramienta que ya haba utilizado y que en ese momento vea prctica. Ms tarde, segn me fui documentando, ca en la cuenta de lo extenso, verstil y potente que es Spring Framework y entend cmo era uno de los requisitos ms valorados dentro del mundo Java en las ofertas de trabajo. Comprob que para entender Spring y cmo este interacta tena que conocer otras herramientas ya que la magia de Spring consiste en ello. De este modo este estudio no solo me sirvi para conocer el framework de Spring, sino para conocer ms sobre el mundo Java. Segn fui acumulando conocimientos en Spring en particular y en Java en general pude cambiar de puesto de trabajo y comprobar que Spring es una herramienta muy valorada. Posteriormente decid crear una empresa junto a otro compaero en la cual desarrollaramos una aplicacin web. Todo ese desarrollo ha sido creado haciendo uso de Spring.
Bibliografa
Spring. Craig Walls. Anaya multimedia 2009. Java 6. Felipe Lima Daz. Anaya Multimedia. http://www.springsource.org/ Spring Web Flow 2 Web Development. Sven Lppken, Markus Stuble 2009. Spring Security 3. Peter Mularien, Apress 2011. Spring Recipes: A Problem-Solution Approach Gary Mark, Ken Sipe, Josh Long Apress. http://refcardz.dzone.com/
185