Spring Framework-Javier Sevilla

185
Spring Framework Javier Sevilla Sánchez

Transcript of Spring Framework-Javier Sevilla

Page 1: Spring Framework-Javier Sevilla

Spring Framework

Javier Sevilla Sánchez

Page 2: Spring Framework-Javier Sevilla
Page 3: Spring Framework-Javier Sevilla

Quiero dar las gracias a Sergio, Julián, 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 empujándome o tendiéndome la mano para alzarme me hace aprender.

Page 4: Spring Framework-Javier Sevilla
Page 5: Spring Framework-Javier Sevilla

Capítulo 1:

Primeros pasos con Spring

[Escribir el subtítulo del documento] [Escriba aquí una descripción breve del documento. Una descripción breve es un resumen corto del contenido del documento. Escriba aquí una descripción breve del documento. Una descripción breve es un resumen corto del contenido del documento.]

Javier Sevilla Sánchez

Page 6: Spring Framework-Javier Sevilla
Page 7: Spring Framework-Javier Sevilla

7

Contenido Introducción al framework de Spring ............................................................................................ 8

Inyección de dependencia e inversión de control ........................................................................ 8

Módulos .................................................................................................................................... 8

El contenedor ........................................................................................................................ 9

Integración y acceso de Datos ............................................................................................. 10

Web ..................................................................................................................................... 10

AOP ...................................................................................................................................... 10

Test ...................................................................................................................................... 10

Primeros pasos con Spring .......................................................................................................... 11

Mi primer Hola mundo con Spring .......................................................................................... 11

Inyección de Dependencia ID (Dependency injection) ............................................................... 13

Inyección de dependencia en la práctica ................................................................................ 13

Programación orientada a aspectos............................................................................................ 16

¿Qué ventajas tenemos con AOP? .......................................................................................... 17

Resumen ...................................................................................................................................... 17

Page 8: Spring Framework-Javier Sevilla

Introducción al framework de Spring

Spring es un framework de código abierto de desarrollo de aplicaciones para la plataforma

Java. La primera versión fue escrita por Rod Jonhson. Es una plataforma Java que otorga una

infraestructura de apoyo global al desarrollo de aplicaciones Java. De este modo, Spring se

encarga de la infraestructura para que nosotros nos centremos en la aplicación.

Unos ejemplos pueden ser:

• Inyección de dependencia e inversión de control.

• Integración del acceso a datos.

• Facilitar el desarrollo de aplicaciones web separando claramente las partes del

modelo, vista y controlador.

• Poder ejecutar métodos transaccionales en una base de datos sin necesidad de lidiar

con API de transacción, métodos remotos sin tener que lidiar con API de

procedimientos remotos, métodos de gestión sin JMX, control de mensajes sin JMS...

Inyección de dependencia e inversión de control

El término “Aplicación Java” es un término tan amplio que podría ir desde un applet hasta

aplicaciones empresariales en servidores de nivel n.

Java proporciona una gran cantidad de herramientas para desarrollar aplicaciones, pero carece

de medios para organizar los elementos. Normalmente es el arquitecto Java el que se encarga

de esta tarea pudiendo utilizar patrones.

La inversión de control de Spring lo que hace es preocuparse de proporcionar una manera

formal de creación de componentes dispares de una manera homogénea y de una manera

funcional. Codifica componentes que se integran en las aplicaciones.

Diversas instituciones y empresas eligen Spring como una manera de diseñar aplicaciones

robustas fáciles de mantener.

Módulos

El framework de Spring consiste en elementos organizados en veinte módulos. Estos módulos

se agrupan en el Contenedor (core container), Acceso a datos e integración, modelo vista

controlador (módulo web MVC), aspectos (AOP), instrumentación y test.

El siguiente diagrama muestra cómo se divide:

Page 9: Spring Framework-Javier Sevilla

9

El contenedor

El contenedor consiste en un núcleo, objetos bean, un contexto y un lenguaje de expresiones.

El núcleo y los beans son la parte fundamental de Spring, incluyendo la inversión de control y

la inyección de dependencia. Este contenedor es una versión más compleja del patrón Factory.

Elimina la necesidad de programar singletons y permite desacoplar la configuración y

especificación de dependencias de la lógica de programación.

El contexto se construye sobre la sólida base del núcleo. Así permite determinadas

configuraciones. Así la internacionalización, propagación de eventos, lectura de recursos o la

creación de contextos (como el web) formarán parte de este módulo.

Los Lenguajes de expresión permiten una potente herramienta de consulta y manipulación de

un objeto en tiempo de ejecución. Es una extensión del “Unified EL”, especificado en la

especificación JSP 2.1. El lenguaje permite asignar y obtener valores de las propiedades,

asignar propiedades, invocar métodos, acceder al contexto de matrices, listas, índices,

operadores aritméticos y lógicos, variables, y obtención de objetos por nombre del contendor

de Spring.

Page 10: Spring Framework-Javier Sevilla

Integración y acceso de Datos

La capa de Integración y acceso a datos consiste en la integración de los módulos JDBC, ORM,

OXM, JMS y de transacción.

El módulo JDBC otorga una capa de abstracción que elimina la necesidad de crear código

tedioso y trasforma las excepciones generadas por el proveedor de base de datos.

El módulo ORM otorga una integración con los APIS más populares de ORM como puedan ser

JPA, JDO, Hibernate o iBatis.

El módulo OXM otorga una capa de abstracción para el mapeo Objeto/XML en distintas

implementaciones como JAXB, Castor, XMLBeans, JiBX o XStream.

El módulo JMS contiene características para la producción y consumo de mensajes.

El módulo de Transacción permite transacciones programáticas y declarativas para las clases

que implementan interfaces especiales y para todos los POJO.

Web

La capa web consiste en los módulos Web, Web-Servlet, Web-Struts y Web-Portlet.

El módulo Web permite integración básica de características como la subida multiparte de un

fichero, la inicialización de la inversión de control del contenedor usando listeners Servlet y un

contexto de lógica web.

El módulo Servlet contiene la implementación modelo vista controlador. El framework Spring

MVC permite una separación entre el modelo, el código y los formularios web y se integra con

todas las otras características de Spring.

El módulo Web-Struts permite la integración de clases integrando Struts, actualmente este

soporte esta obsoleto en Spring 3.0.

El módulo Web-Portlet permite las características web en sistemas empotrados.

AOP

El módulo AOP de Spring permite una implementación de programación orientada a aspectos

permitiendo definir métodos e interceptores, puntos de corte, etc. Para desacoplar el código.

Permite la integración con AspectJ

Él módulo de instrumentación otorga instrumentación de clases así como un cargador de claes

a ser usadas en determinadas aplicaciones de servidor.

Test

Page 11: Spring Framework-Javier Sevilla

11

El módulo 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 código de manera aislada.

Primeros pasos con Spring

La Inyección de Dependencia es un patrón de diseño orientado a objetos, en el que se inyectan

objetos a una clase en lugar de ser la propia clase quien cree objetos mediante constructores.

Como para muestra un botón, haremos un ejemplo “Hola Mundo” en el que se plasmarán los

conceptos básicos.

Empezaremos creando una clase interfaz “Saludo”, a pesar de que no es necesario para el

ejemplo ni para el uso de Spring, es una buena práctica ya que potencia el uso de la inyección

de dependencia, hace más fácil las pruebas y aumenta la abstracción entre otras ventajas.

Mi primer Hola mundo con Spring

Interfaz Saludo

package es.uah.tfc.javiersevilla.holamundo; public interface Saludo { String getSaludo(); void setSaludo(String saludo); void saluda(); }

Vemos cómo esta interfaz hace que cualquier clase que la implemente cumpla con la

característica de los beans de tener métodos getters y setters. El siguiente paso es crear una

clase que implemente esa interfaz, la llamaremos SaludoImp. Crearemos un constructor vacío

al igual que un constructor con argumento para poder ilustrar las dos maneras que tiene

Spring de inyectar dependencias a un Bean.

Implementación 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() {

Page 12: Spring Framework-Javier Sevilla

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 ningún 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 configuración xml.

Fichero de configuración 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!! (método)" /> </ 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 creación de Beans, vemos como se crean dos uno

haciendo uso de la inyección por parámetro y otro haciendo uso de la inyección por

constructor.

Ya están listos todos los componentes, sólo falta ver la puesta en marcha. Para ello crearemos

una clase con un método main.

package es.uah.tfc.javiersevilla.holamundo; import org.springframework.context.ApplicationConte xt; import org.springframework.context.support.ClassPat hXmlApplicationContext; public class HolaMundo { public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("holamundo.xml"); Saludo saludoConstructor = (Saludo) ctx.getBean("saludoConstructor");

Page 13: Spring Framework-Javier Sevilla

13

saludoConstructor.saluda(); Saludo saludoMetodo = (Saludo) ctx.getBean("salud oMetodo"); saludoMetodo.saluda(); } }

Fijémonos en el código, 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 identificación.

Con este ejemplo queda esbozado el concepto de inyección de dependencia, pasemos a

explicar este término de un modo más amplio.

Inyección de Dependencia ID (Dependency injection)

El anterior ejemplo es algo muy sencillo, las aplicaciones no son como el HolaMundo sino que

suelen estar compuestas por diversas clases que operan de forma conjunta para un fin.

Tradicionalmente eran los propios objetos los responsables de obtener las referencias a otros

objetos con los que colaboraba haciendo un código muy acoplado y muy poco “testeable”. La

inyección de dependencia consiste en que estas dependencias las otorgue una entidad externa

inyectándolas en los objetos. Así la responsabilidad se delega en la entidad externa, haciendo

clases más simples.

Si un objeto sólo conoce sus dependencias mediante interfaces bien definidas, podremos

cambiar la implementación del objeto del que depende sin que se sepa la diferencia es decir

tendremos acoplamiento débil.

Inyección de dependencia en la práctica

Imaginemos que nos piden una aplicación de gestión para la universidad, en ella habría

alumnos, profesores, titulaciones, cursos, asignaturas, temarios, apuntes… etc. Pero de forma

sencilla y muy resumida diremos que un estudiante estudia una carrera y al finalizar obtiene

un título. Bajo esta premisa diríamos que el estudiante es el responsable de matricularse en la

carrera, estudiar y así obtener el título.

Para recrear este escenario usando la inyección de dependencia empezaríamos definiendo

claramente las interfaces que entren en juego como puedan ser Alumno, Carrera y Diploma.

Habrá relación entre ellas, pero debemos de crear un código en el cual las clases que

implementen estos interfaces conozcan lo mínimo posible de las clases de las cuales

Page 14: Spring Framework-Javier Sevilla

dependen, es decir, los objetos sólo han de conocer el interfaz y su implementación ha de ser

inyectada en las clases dependientes.

Código del Interface Carrera:

package es.uah.uahdi.model;

public interface Carrer { public void setName(String carrerName); public void setUniversityName(String universityName ); public Certificate graduate();

}

Código del Interface Alumno:

package es.uah.uahdi.model; public interface Student { public void setCarrer(Carrer carrer); public String getName(); public void setName(String name); public Certificate study(); }

Código del Interface Diploma:

package es.uah.uahdi.model;

public interface Certificate { public String getCarrerName(); public void setCarrerName(String carrerName); public String getUniversityName(); public void setUniversityName(String university Name); }

Código de la implementación de Carrera:

package es.uah.uahdi.model; public class CarrerImp implements Carrer { private String carrerName; private String universityName; public void setName(String carrerName) { this.carrerName = carrerName; } public void setUniversityName(String universityNam e) { this.universityName = universityName; } public Certificate graduate() { return new UniversityCertificate(carrerName, univ ersityName); } }

Page 15: Spring Framework-Javier Sevilla

15

Código de la implementación de Alumno:

package es.uah.uahdi.model; public class StudentImp implements Student { private String name; private Carrer carrer; public StudentImp() { } public Carrer getCarrer() { return carrer; } public void setCarrer(Carrer carrer) { this.carrer = carrer; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Certificate study() { return this.carrer.graduate(); } }

Código de la implementación de Diploma:

package es.uah.uahdi.model; public class UniversityCertificate implements Certi ficate{ private String carrerName; private String universityName; public UniversityCertificate(String carrerName, Str ing universityName) { this.carrerName = carrerName; this.universityName = universityName; } public String getCarrerName() { return this.carrerName; } public void setCarrerName(String carrerName) { this.carrerName = carrerName; } public String getUniversityName() { return universityName; } public void setUniversityName(String universityName ) { this.universityName = universityName; } }

Page 16: Spring Framework-Javier Sevilla

Fichero de configuración de Spring:

<?xml version ="1.0" encoding ="UTF-8" ?> <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" xmlns:jee ="http://www.springframework.org/schema/jee" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/s pring-beans-2.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/sp ring-aop-2.0.xsd" > < bean id ="itig" class ="es.uah.uahdi.model.CarrerImp" > < property name="universityName" value ="Universidad de Alcalá" /> < property name="name" value ="Ingeniería Técnica en Informática de Gestión" /> </ bean > < bean id ="juanito" class ="es.uah.uahdi.model.StudentImp" > < property name="name" value ="Juan Aurelio Gonzalez" /> < property name="carrer" ref ="itig" /> </ bean > </ beans >

En el fichero de configuración 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 método 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 código más

mantenible y reutilizable ya que cada implementación no conoce las implementaciones de las

demás, tan sólo conoce su interfaz. Esto hace un código menos acoplado y probable de

manera unitaria.

Programación orientada a aspectos

La programación orientada a aspectos hace que las funcionalidades de los componentes de las

aplicaciones sean más reutilizables. La programación orientada a aspectos (AOP) es una

técnica de programación que promueve que los sistemas estén bien divididos por

incumbencias. Así cada componente es responsable de una parte (gestión de transacciones,

gestión de registros, gestión de seguridad, gestión de de trazas… etc.) . Sin embargo,

típicamente los componentes acaban llevando funcionalidades que están fuera de su función y

que hacen que aumente la complejidad del código. Los principales problemas son que el

código que implementa las incumbencias del sistema se repite por cada componente que lo

necesite haciendo que si hay un cambio se tendrá que realizar en cada módulo que lo

Page 17: Spring Framework-Javier Sevilla

17

implemente, en vez de en un sitio sólo y que los componentes tienen código que no es de su

funcionalidad principal.

¿Qué ventajas tenemos con AOP?

Como hemos explicado, sin AOP cada componente conoce las funcionalidades de los

demás, introduciendo complejidad adicional a su funcionalidad principal, como resultado, los

objetos empresariales están más implicados con los sistemas de servicios.

AOP hace posible poner en módulos estos servicios y después aplicarlos de manera declarativa

a los componentes que deberían afectar aumentando así la cohesión y haciendo que los POJO

se mantengan simples. Así los aspectos “envuelven” a los demás componentes haciéndolos

más sencillos evitando ensuciarles con lógica de transacciones, seguridad o trazas.

Siguiendo con la línea de la aplicación de la universidad, hagamos un ejemplo que clarifique

este concepto.

Resumen

En este capítulo hemos presentado Spring, viendo que es un framework que facilita la creación

de aplicaciones haciéndolas más comprensibles, desacopladas y fáciles de mantener. Hemos

presentado sus principales componentes haciendo una breve visión de lo que Spring puede

hacer por nosotros.

Hemos hecho una primera visión de los módulos en los que se divide Spring, Estos módulos se

agrupan en el Contenedor (core container), Acceso a datos e integración, modelo vista

controlador (módulo web MVC), aspectos (AOP), instrumentación y test.

Más tarde, a modo de prueba, hemos creado nuestra primera aplicación con Spring, viendo de

una manera más práctica su funcionamiento. Hemos hecho una breve introducción a la

inyección de dependencia así como a la programación orientada a aspectos.

Page 18: Spring Framework-Javier Sevilla
Page 19: Spring Framework-Javier Sevilla
Page 20: Spring Framework-Javier Sevilla
Page 21: Spring Framework-Javier Sevilla

21

Page 22: Spring Framework-Javier Sevilla

[Escriba el título del documento] [Escriba el subtítulo del documento] [Escriba aquí una descripción breve del documento. Normalmente, una descripción breve es un resumen corto del contenido del documento. Escriba aquí una descripción breve del documento. Normalmente, una descripción breve es un resumen corto del contenido del documento.] Javier Sevilla Sánchez

[Seleccione la fecha]

Page 23: Spring Framework-Javier Sevilla

23

Contenido

Conexión de Beans ...................................................................................................................... 24

Introducción ............................................................................................................................ 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 creación y conexión de beans .............................................................................. 27

Conectar tipos múltiples ......................................................................................................... 31

Tipos de conexión de beans ........................................................................................................ 34

Auto-conexión byName .......................................................................................................... 34

Auto-conexión byType ............................................................................................................ 35

Auto-conexión constructor ..................................................................................................... 35

Auto-conexión autodetect ...................................................................................................... 35

La auto-conexión default ........................................................................................................ 36

La Auto-conexión no ............................................................................................................... 36

Configuración de la creación de los beans .................................................................................. 37

Delimitación de un bean ......................................................................................................... 37

Creación de beans con clases singleton .................................................................................. 38

Uso de init-method y de destroy-method (inicializar y destruir) ............................................ 38

Page 24: Spring Framework-Javier Sevilla

Conexión de Beans

Introducción

En el capítulo anterior hemos dado los primeros pasos con Spring, hemos visto por primera vez

la inyección de dependencia (DI) y la programación orientada a aspectos (AOP) creando unos

sencillos proyectos a modo de ejemplo.

A lo largo de este capítulo veremos más en profundidad el contenedor de Spring y sus distintas

implementaciones. Las principales categorías de las implementaciones son la factoría de beans

y el contexto de aplicación. En este capítulo veremos cómo conectar beans dentro del

contenedor y qué información daremos al fichero XML para crear una aplicación con una DI

potente.

Bean dentro de un contenedor

A diferencia de Java tradicional en el que el ciclo de vida del objeto es desde su instanciación

con new hasta su etiquetado por el recolector, en Spring el ciclo de vida es más complejo.

Aunque dependa de si el bean ha sido creado por una factoría o un contexto, el bean pasa por

una serie de pasos entre los que se encuentran la instanciación, la asignación de propiedades y

nombre, nombre de la fábrica, procesamiento, iniciación…etc. Así hasta que se ejecuta el

método destroy() sobre él.

Contenedor de beans

Como ya dijimos, en el enfoque tradicional las asociaciones llevan a código complejo y no

reutilizable ni “testeable”. En Spring el contenedor es el responsable de la interconexión de

beans.

El contenedor será el responsable de la inyección de dependencia y del ciclo de vida de los

beans, creándolos, interconectándolos y destruyéndolos.

Spring tiene diversos contenedores, pero responden a dos grandes grupos según la

implementación del interfaz BeanFactory o AplicationContext.

Page 25: Spring Framework-Javier Sevilla

25

BeanFactory

La implementación más 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 más básico, pero es más que la instanciación y entrega de beans. Cuando una

fábrica entrega un bean lo entrega configurado, consciente de sus dependencias y listo para

usar.

El siguiente ejemplo crea un XmlBeanFactory como implementación de BeanFactory con un

FileSystemResource como implementación de Resource y obtiene el bean llamado

beanEjemplo.

BeanFactory factoria = new XmlBeanFactory(new FileSystemResource(“/home/user/spring/beans.xml”)); BeanExample beanEjemlo = (BeanExample) factory.getB ean(“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 más

limitados como pueda ser un móvil, o sistemas empotrados.

Otra diferencia es cómo abren los beans. Mientras la factoría 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 más comunes son ClassPathXmlApplicationContext,

FileSystemXmlApplicationContext y XmlWebApplicationContext. Ésta última la trataremos más

adelante en el capítulo destinado a Spring MVC.

La diferencia entre FileSystemXmlApplicationContext y ClassPathXmlApplicationContext es que

el primero buscará en el sistema de archivos y el segundo según el path de la clase incluyendo

archivos jar.

Ejemplo de ello sería:

ApplicationContext ctx = new ClassPathXmlApplicatio nContext(“miFichero.xml”);

Page 26: Spring Framework-Javier Sevilla

Pasos de la vida de un Bean

Como ya hemos visto, un bean en Spring tiene una vida más compleja que en Java puro. Sus

pasos son:

1. Instanciar: Spring instancia el bean.

2. Informar de las propiedades: Spring hace la inyección a las propiedades.

3. Asignación del nombre: Spring pasa el nombre del bean si este implementa

BeanNameAware.

4. Establecer nombre de la fábrica y el contexto: Si el bean implementa

BeanNameAware le pasará el nombre de la fábrica y en el caso de implementar

ApplicationContextAware y de estar contenido en un contexto se le pasaría el

nombre del contexto.

5. Iniciación del Bean: Si el bean implementa InitializingBean, se llamaría a su

método afterPropertiesSet(). Existen dos métodos que se llamarían si hubiese

algún BeanPostProccessor, uno antes de la inicialización y otro después.

6. Estado de listo para su uso: Tras esto, el bean ya estaría listo para usarse.

7. Destrucción del bean: Si el bean implementa DisposableBean se llamará al método

destroy().

Page 27: Spring Framework-Javier Sevilla

27

Ejemplo de creación y conexión de beans

Hasta ahora hemos visto distintos proyectos con Spring que creaban y usaban beans, pero no

habíamos entrado en detalle en la explicación de estas conexiones.

Existe un eterno debate de cuál es la mejor opción para la inyección de dependencias si por

constructor o por el contrario por setter. Como para casi todos los debates la respuesta es

“depende”.

Inyección por constructor

Por un lado la inyección por constructor crea una fuerte dependencia con el instanciador ya

que este ha de tener todas las dependencias antes, así garantiza que un bean está

completamente configurado antes de usarse. Si se inyecta por constructor es posible que no

sean necesarios métodos setter, con lo que la clase será más sencilla e inalterables sus

dependencias.

Inyección por setter

Sin embargo, la inyección por constructor tiene también sus inconvenientes. Si el bean tiene

muchas dependencias el constructor será complejo, si existen varios parámetros del mismo

tipo pues puede confundir su instanciación y acaba siendo más complejo cuando la clase

hereda. La configuración por setter puede realizar construcciones más complejas.

Spring permite ambas configuraciones, con lo que no restringe a una forma la inyección.

Ejemplo de Conexión

Continuando con la filosofía de anteriores ejemplos, veamos cómo se conectan beans

tomando como ejemplo de nuevo un escenario universitario. Al inicio del curso, los

responsables de hostelería han visto que este año habrá más alumnos, con lo que han decidido

contratar a más gente. Para el puesto en cuestión se valoraran distintas cualidades como saber

cocinar, limpiar, el trato con la gente… etc.

El código del interfaz trabajador sería el siguiente:

public interface Worker { void work() throws WorkingException; }

La siguiente clase define los candidatos camareros:

package es.uah.uahconnection.cafe.model; public class Waiter implements Worker { private int coffeesServedDaily = 1; public Waiter() { } public Waiter(int coffeesServedDaily) {

Page 28: Spring Framework-Javier Sevilla

this.coffeesServedDaily = coffeesServedDail y; } public void work() throws WorkingException { System.out.println("el camarero está sirvie ndo " + coffeesServedDaily + " cafés"); } }

Veamos cuál sería la implementación más básica de camarero:

<bean id="joselito" class="es.uah.uahconnection.caf e.model.Waiter"/>

Esta es la definición más básica de un bean, tan sólo 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 parámetros. Tal y como vemos no es muy complicado que se te

considere como camarero, con servir un café al día vale. Pero no creo que Joselito consiga el

trabajo con tan poco esfuerzo.

Pepe viene con más entusiasmo, él asegura que es capaz de servir al menos 30 cafés al día.

Para ello hacemos uso del constructor con parámetro, 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 parámetros que el constructor precise siempre

que coincidan en número, tipo y posición así obtenemos la inyección mediante constructor.

Pepe es capaz de servir 30 cafés, pero hay competidores más preparados. Alberto es capaz de

poner el mismo número de cafés mientras limpia el local. Veamos como es el código del

camarero-limpiador.

public class WaiterDustman extends Waiter { private Local local; public WaiterDustman(int coffeesServedDaily, Re staurant restaurant) { super(coffeesServedDaily); this.local = restaurant; } @Override public void work() throws WorkingException{ super.work(); local.clean(); } }

Siendo el interfaz local y su implementación restaurante la siguiente:

interface Local { void clean(); }

Page 29: Spring Framework-Javier Sevilla

29

public class Restaurant implements Local { public void clean() { System.out.println("El ha sido limpiado"); } }

La definición 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>

Lo que Spring internamente ejecutaría sería algo similar a esto:

Local restaurant = new Restaurant(); Worker alberto = new WaiterDustman(coffeesS ervedDaily, restaurant);

Como hemos visto en anteriores ejemplos y hemos comentado al principio de este punto,

Spring permite configurar dependencias con los métodos 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(); } }

La interfaz receta sería:

public interface Recipe { void develop(); public String getName(); }

Y la clase paella valenciana sería la siguiente:

public class PaellaValenciana implements Recipe {

Page 30: Spring Framework-Javier Sevilla

private static final String NAME = "Paella vale nciana"; public void develop() { fryTheMeat(); sauteVegetables(); pourWater(); addRice(); } public String getName() { return NAME; } public void fryTheMeat(){ System.out.println("La carne se está friend o"); } public void sauteVegetables(){ System.out.println("las verduras se están r ehogando"); } public void pourWater(){ System.out.println("se vierte agua"); } public void addRice(){ System.out.println("se añade el arroz"); } }

Ernesto es un buen cocinero cuya especialidad es la paella valenciana receta de Arguiñano. La

definición de Ernesto así como de su paella valenciana sería la siguiente.

<bean id="paellaValencianaArguiñano" class="es.uah.uahconnection.cafe.model.PaellaVa lenciana"/> <bean id="ernesto" class="es.uah.uahconnection. cafe.model.Kitchener"> <property name="recipe" ref="paellaValencia naArguiñano"/> <property name="diners" value="6"/> </bean>

Como vemos en el código podemos asignar valores mediante el tag <property>. Esto sólo será

así si en la clase tenemos un método 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 numérico, cadena o

booleano. Para definir a Ernesto se ha usado una inyección de bean y otra de valor simple, se

ha pasado la receta de paella que aprendió de Arguiñano y el número de comensales para el

que es capaz de cocinar.

Inyectar beans internos

Los beans internos, al igual que pasaría 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 también 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">

Page 31: Spring Framework-Javier Sevilla

31

<bean class="es.uah.uahconnection.cafe. model.PaellaValenciana"/> </property> </bean>

Si la clase cocinero pudiera configurarse por constructor también se podría 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>

Conectar tipos múltiples

Hemos visto como en Spring se inyectan dependencias mediante los operadores value y ref.

Sin embargo se pueden inyectar tipos múltiples como List (cuando sea una lista), Set (una lista

sin duplicados), Map (clave y valor de cualquier tipo) o Properties (clave y valor de tipo

cadena).

Saber cocinar un plato está bien, pero lo útil sería poder acumular un gran número de recetas

para poder ser un buen Chef. La clase Chef refleja esto.

public class Chef implements Worker { private Collection<Recipe> recipes;//también un Array o una lista private int diners;//comensales public Chef() { } public void setRecipe(Recipe recipe) { } public void setDiners(int diners) { this.diners = diners; } public void work() throws WorkingException { for (Recipe recipe : recipes) { System.out.println("Se dispone a cocina r " + recipe.getName()); recipe.develop(); } System.out.println("Todo ello para " + dine rs + " comensales"); } }

Raimundo es un buen Chef, ha estudiado varias recetas. Veamos su definición.

<bean id="raimundo" class="es.uah.uahconnection .cafe.model.Chef"> <property name="recipes"> <list> <ref bean="paellaValencianaArguiñan o"/> <ref bean="tortillaDePatatas"/> <ref bean="pizzaItaliana"/> </list>

Page 32: Spring Framework-Javier Sevilla

</property> <property name="diners" value="50"/> </bean>

La clase de la propiedad recipes podría ser también una lista o incluso un array al igual que a la

hora de definirlo podríamos haberlo hecho con la etiqueta <set> en vez de <list>, esto

aseguraría que no hubiese repetidos.

El trato con la gente es algo muy valorado, ya que todo cliente estará más contento si alguien

le atiende de una manera amable. Borja Mari es un poco pedante, pero cae bien. Mientras

sirve cafés suele amenizar con frases amables. Borja Mari pertenece a la clase camarero

amable cuyo código 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 c onversación a sus clientes: "); for (Iterator it = kindSpeach.keySet().iter ator(); it.hasNext();) { String speachType = (String) it.next(); System.out.println(" - para " + speac hType + " dice \"" + kindSpeach.getProperty(speachTy pe) + "\""); } } }

Para poder definirlo en el contenedor de Spring incluiremos el siguiente código en el fichero

xml.

<bean id="borjaMari" class="es.uah.uahconnectio n.cafe.model.KindWaiter"> <property name="kindSpeach"> <props> <prop key="buenosDias">Buenos días, que bonita mañana hace hoy</prop> <prop key="buenasTardes">Buenas y m aravillosas tardes</prop> <prop key="servir">Aquí está su caf é</prop> <prop key="cuenta">Le traigo su cue nta enseguida</prop> <prop key="despedirse">Muchas graci as y vuelva otra vez</prop> </props> </property> </bean>

Hasta ahora todos nuestros aspirantes al puesto vacante para la cafetería de la universidad

han sido muy buenos, pero también se valoran dotes de liderazgo. El señor Antunez es un

buen coordinador y no sólo trabaja sino que sabe organizar a los demás. Para la definición de

la clase coordinador utilizaremos un mapa en el cual queden reflejados los nombres de los

trabajadores para que el señor Antunez pueda referirse por su nombre (la clave de la clase

Map) y el propio trabajador.

Page 33: Spring Framework-Javier Sevilla

33

La clase Jefe refleja lo comentado:

public class Boos implements Worker { private Map<String, Worker> employees; public void work() throws WorkingException { for (String name : employees.keySet()) { System.out.println("Organiza el trabajo para " + name); Worker worker = employees.get(name); worker.work(); } } public void setEmployees(Map<String, Worker> em ployees) { this.employees = employees; } }

Su definición en el fichero xml sería la siguiente

<bean id="antunez" class="es.uah.uahconnection. cafe.model.Boos"> <property name="employees"> <map> <entry key="Alberto" value-ref="alb erto"/> <entry key="Alfredo" value-ref="alf redo"/> <entry key="Ernesto" value-ref="ern esto"/> <entry key="Joselito" value-ref="jo selito"/> <entry key="Pepe" value-ref="pepe"/ > <entry key="Raimundo" value-ref="ra imundo"/> <entry key="BorjaMari" value-ref="b orjaMari"/> </map> </property> </bean>

Page 34: Spring Framework-Javier Sevilla

Tipos de conexión de beans

Existen dos vías para conectar dependencias de beans, la manual y la automática. La manual es

la que hemos ido viendo en los ejemplos que hemos hecho hasta ahora ya sea vía constructor

o vía setter.

La principal razón por la que existe la auto conexión es la reducción de código xml haciéndolo

más simple y más vistoso. Para ello tenemos cuatro tipos distintos de auto conexión, por

nombre, por tipo, por constructor y otra última que combina el constructor y el tipo.

Si tenemos definido un bean cuyo id en el contenedor tenga el mismo nombre que una

propiedad del bean que estamos definiendo podemos usar la auto conexión por nombre

(byName). La auto conexión por tipo nos será útil cuando haya un único bean del mismo tipo

que la propiedad a conectar, si hubiese más se lanzaría una excepción y si no hubiese la

propiedad tendría null.

También se le puede indicar al bean que intente auto conectarse por constructor intentando

que Spring haga corresponder los beans del contenedor con los constructores. Si hubiese

ambigüedades se lanzaría una excepción.

Por último existe la auto conexión con auto detección, esto sería una mezcla de la auto

conexión por tipo y por constructor.

Hay que comentar que también se puede conectar tipos null, gracias a la etiqueta <null/>, esto

es útil aquí en la auto conexión, ya que es posible que no queramos que determinadas

propiedades se conecten.

La autoconexión y la conexión especificada pueden perfectamente convivir. También hay que

decir que hay otro debate si es útil la auto-conexión o no.

Auto-conexión byName

Si el nombre que le damos a un bean coincide con el nombre de la propiedad, éste se auto-

conectará con la propiedad si lleva el campo autowire=”byName”. Como ejemplo podríamos

cambiar la definición de estudiante y carrera del ejemplo de la Inyección de dependencia:

<bean id="itig" class="es.uah.uahdi.model.CarrerImp"> <property name="universityName" value="Univ ersidad de Alcalá de Henares"/> <property name="name" value="Ingeniería Téc nica en Informática de Gestión"/> </bean> <bean id="juanito" class="es.uah.uahdi.model.StudentImp"> <property name="name" value="Juan Aurelio G onzalez"/>

Page 35: Spring Framework-Javier Sevilla

35

<property name="carrer" ref="itig"/> </bean>

Por el siguiente código:

<bean id="carrer" class="es.uah.uahdi.model.CarrerImp"> <property name="universityName" value="Univ ersidad de Alcalá de Henares"/> <property name="name" value="Ingeniería Téc nica en Informática de Gestión"/> </bean> <bean id="juanito" class="es.uah.uahdi.model.StudentImp" autowire="byName"> <property name="name" value="Juan Aurelio G onzalez"/> </bean>

Cuando asignamos la auto-conexión por nombre estamos diciéndole 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-conectarán. El ejemplo sólo podríamos auto-conectar la

misma carrera, si quisiésemos inyectar más beans de distinto tipo en otros lo tendríamos que

hacer sin auto-conexión.

Auto-conexión byType

La auto-conexión 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 excepción de tipo UnsatisfiedDepedencyException. Si en el ejemplo anterior

sólo podíamos auto-conectar aquellas propiedades cuyo nombre fuese el mismo que el del

bean y si queríamos conectar otras de distinto nombre tenía que ser con el método normal,

con la auto-conexión por tipo no podremos conectar ningún otro bean del mismo tipo. Es

decir, la propiedad que tenga autowire=”byType” obliga a que no haya más beans de la misma

clase en el contenedor.

<bean id=”exampleBean” class=”example.ExampleClass” autowire=”byType”/>

Auto-conexión constructor

La auto-conexión por constructor tiene las mismas limitaciones que la auto-conexión por tipo

pero sólo para las propiedades del constructor, Spring no intentará adivinar que bean auto-

conectar si hay más de uno del mismo tipo.

<bean id=”exampleBean” class=”example.ExampleClass” autowire=”constructor”/>

Auto-conexión autodetect

Page 36: Spring Framework-Javier Sevilla

La conexión por auto-detección hace que Spring primero intente conectar mediante

constructor y luego mediante tipo.

<bean id=”exampleBean” class=”example.ExampleClass” autowire=”autodetect”/>

La auto-conexión default

Si en el tag de beans configuramos el campo default-autowire los beans que definamos en su

cuerpo se autoconectarán de esa forma. También se puede incluir explícitamente en el tag del

bean el valor default.

<beans default-autowire=”byType”> … </beans>

Si se especifica en el propio bean algo distinto se hará de esa manera.

La Auto-conexión no

Si se especifica en la propiedad del bean autowire=”no” no se hará autoconexión, incluso si en

el tag beans indiquemos lo contrario en el campo default-autowire, no se hará.

Page 37: Spring Framework-Javier Sevilla

37

Configuración de la creación de los beans

A Spring se le pueden dar distintas directrices para alterar la simple creación del bean como

hasta ahora hemos hecho en los ejemplos. Las opciones serían las siguientes:

• Inicializar un bean una vez ya creado y ejecutar código antes de su destrucción.

• Crear beans desde métodos estáticos de fábrica en vez de constructores públicos.

• Controlar el número de instancias de un bean. Una instancia por sesión, por petición,

por cada uso o por aplicación.

Delimitación de un bean

Si no se especifica nada, Spring instancia cada bean de forma única, por cada petición 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 límite bajo en el que se crean los

beans sin tener que codificar las reglas de limitación en la clase del bean.

Al igual que pasaba con bean, el término Singleton en Spring no es sinónimo 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 diseño de manera ajena a la

propia clase.

Típo de límite

Singleton Limita a una única instancia por contenedor.

Es la opción predeterminada.

Prototype Permite que un bean sea instanciado

cualquier número de veces (se instanciará

cada vez que se use).

Request En Spring MVC es usado para que el bean se

instanciado una vez por petición HTTP

(request).

Session Al igual que request es usado en entorno web

y la instancia del bean durará lo mismo que

dure la sesión en el servidor.

Global-session Limita el bean a una sesión global en el

servidor en entornos web.

Page 38: Spring Framework-Javier Sevilla

Dependiendo de las necesidades del sistema de información, unas veces será útil dejar la

configuración por defecto, es decir, la instanciación singleton y otras veces será útil tener

varias instancias con prototype. Con respecto al servidor web, más tarde veremos todas las

utilidades que podemos encontrar para la construcción de distintos beans.

Creación de beans con clases singleton

Esto es útil si se están usando clases de terceras partes cuyos métodos públicos sean estáticos.

Es decir, para la inclusión de clases tipo singleton dentro del contenedor de Spring. En Spring,

como ya hemos dicho, por defecto se crea una instancia de cada bean, pero no tiene por qué

ser de cada clase. Continuando con el anterior ejemplo, cambiemos la definición de la cafetería

de la universidad, ya que siempre va a ser la misma.

package es.uah.uahconnection.cafe.model; public class Restaurant implements Local { public void clean() { System.out.println("El restaurante ha sido limpiado"); } private static class RestaurantSingletonHolder { static Restaurant instance = new Restaurant (); } public static Restaurant getInstance() { return RestaurantSingletonHolder.instance; } }

En el anterior ejemplo hemos definido una clase estática interna cuya función es devolver una

instancia de la clase restaurante, cumpliendo el patrón de diseño tradicional de singleton. Para

instanciar correctamente este bean en Spring le deberemos especificar en su definición el

campo factory-method el método de instanciación. El código xml sería el siguiente.

<bean id="cafeteriaPolitecnica" class="es.uah.uahconnection.cafe.model.Restaurant" factory-method="getInstance"/>

Uso de init-method y de destroy-method (inicializar y destruir)

Es posible que queramos ejecutar cierto código de inicialización o de destrucción al instanciar

un bean, ya sea para ponerlo en un estado en particular o para eliminar o limpiar ciertas

vicisitudes. Para ello (y para todo aquello que se nos pueda ocurrir), tenemos la posibilidad de

configurar dos campos en la declaración. Éstos son init-method y destroy-method. El primero

ejecutará el código del método al inicio y el segundo al final. Un ejemplo del código xml sería el

siguiente:

Page 39: Spring Framework-Javier Sevilla

39

<bean id="exampleBean" class="exampleClass" init-method="initExample" destroy-method="cleanUpExample"/>

También si tenemos varios beans a los cuales tenemos que inicializar y todos ellos tienen el

mismo nombre de método (usualmente init y clean) podríamos definirlo por defecto en el tag

de beans como muestra el código de ejemplo.

<beans xmlns="http://www.springframework.org/schema /beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-inst ance" xsi:schemaLocation="http://www.springframework.o rg/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 cafetería, 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 código 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 + " com ensales"); } 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"); } }

Ahora la definición de cocinero sería tan simple como esta:

<bean id="alfredo" class="es.uah.uahconnection. cafe.model.Kitchener"

Page 40: Spring Framework-Javier Sevilla

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 sólo el cocinero manipula alimentos creemos que todos los chef deberían 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

implementarán el método init y también el método destroy.

Spring tiene una forma más útil de poder definir un método por defecto de inicio así como otro

de destrucción como ya hemos visto. El código perteneciente a esto sería:

<beans xmlns="http://www.springframework.org/schema /beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-inst ance" xsi:schemaLocation="http://www.springframework.o rg/schema/beans http://www.springframework.org/schema/beans/ spring-beans-2.0.xsd" default-init-method="init" default-destroy-method="clean">

Aún sigue teniendo Spring una forma más de hacer todo esto. Si la clase que definimos

implementa el interfaz InitializingBean y el DisposableBean. Utilizará los métodos

afterPropertiesSet y destroy, como ya comentamos al inicio de éste capítulo.

Vemos como sería el código del cocinero si implementase esto:

public class Kitchener implements Worker, Initializ ingBean, 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 + " com ensales"); } 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 Excepti on { init(); }

Page 41: Spring Framework-Javier Sevilla

41

public void destroy() throws Exception { clean(); } }

Como seguramente se haya percatado, esta opción liga el desarrollo de las clases de nuestro

sistema de información al framework de Spring, haciéndolas menos reutilizables. Es por ello

que aún que la opción esté y haga que nuestro xml de configuración sea más limpio esta

opción se descarte en muchas ocasiones.

Page 42: Spring Framework-Javier Sevilla
Page 43: Spring Framework-Javier Sevilla

43

Page 44: Spring Framework-Javier Sevilla

Capítulo 3:

Programación

orientada a aspectos

(AOP) Estudio teórico y práctico de AOP

En el primer capítulo hicimos una breve introducción a la programación orientada a aspectos. En éste entraremos en detalle en cómo Spring gestiona este paradigma y daremos ejemplos prácticos.

Javier Sevilla Sánchez

Page 45: Spring Framework-Javier Sevilla

45

Contenido Introducción ................................................................................................................................ 46

Conceptos básicos ................................................................................................................... 46

Advice .................................................................................................................................. 46

Join point ............................................................................................................................. 46

Pointcut ............................................................................................................................... 46

Aspect .................................................................................................................................. 47

Introduction ........................................................................................................................ 47

Target .................................................................................................................................. 47

Proxy .................................................................................................................................... 47

Weaving ............................................................................................................................... 47

AOP en Spring .............................................................................................................................. 47

Ejemplo de aplicación en Spring ................................................................................................. 48

Creación de una clase Advice (Notificación) ........................................................................... 49

MethodInterceptor (Around Advice o notificación alrededor) ............................................... 50

Definir Pointcuts y Advice ....................................................................................................... 50

Declaraciones con expresiones regulares (Regular Expressions Pointcuts) ........................ 51

Definición de puntos de corte con AspectJ ......................................................................... 51

Configuración con ProxyFactoryBean ..................................................................................... 52

Abstracción de ProxyFactoryBean .......................................................................................... 53

Autoproxying ............................................................................................................................... 54

Autoproxy básico de beans. .................................................................................................... 54

@AspectJ ................................................................................................................................. 55

Creación de aspectos POJO ..................................................................................................... 56

Page 46: Spring Framework-Javier Sevilla

Introducción

La Programación Orientada a Aspectos (POA) es un paradigma de programación relativamente

reciente cuya intención es permitir una adecuada modularización de las aplicaciones y

posibilitar una mejor separación de conceptos. Spring puede encapsular los diferentes

conceptos que componen una aplicación en entidades bien definidas, eliminando las

dependencias entre cada uno de los módulos.

Así tenemos un código menos acoplado, sin código dependiente y disperso y con POJOS

sencillos, adaptables y reusables. Spring se ha erigido como un líder dentro de la programación

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 aplicación se

suelen llamar aspectos transversales (cross-cutting concerns) y suelen estar separados de la

lógica empresarial. La AOP intenta separar estos aspectos de la lógica de la aplicación.

En el capítulo segundo vimos cómo utilizar la DI para desacoplar los objetos dependientes.

AOP desacopla los objetos transversales.

Veremos a lo largo del capítulo funcionalidades AOP de Spring, así como cómo AspectJ se

acopla perfectamente.

Conceptos básicos

Antes de meternos en harina, es recomendable entender cada uno de los términos que se

manejaran más adelante. La programación Orientada a Aspectos ha creado su propia jerga y

las traducciones al castellano comúnmente no convergen.

Advice

Notificación o consejo es la implementación del aspecto, es decir, contiene el código que

implementa la nueva funcionalidad. Se insertan en la aplicación en los Puntos de Cruce.

Join point

Un punto de unión es un punto en la ejecución de la aplicación en el que se puede insertar un

aspecto, puntos de ejecución dentro del sistema donde un aspecto puede ser tejido, como una

llamada a un método, el lanzamiento de una excepción o la modificación de un campo (Éste

último no contemplado por Spring). El código del aspecto será insertado en el flujo de

ejecución de la aplicación para añadir su funcionalidad.

Pointcut

Los Puntos de Corte definen los Consejos que se aplicarán a cada Punto de Cruce. Se especifica

mediante Expresiones Regulares o mediante patrones de nombres (de clases, métodos o

campos), e incluso dinámicamente en tiempo de ejecución según el valor de ciertos

parámetros.

Page 47: Spring Framework-Javier Sevilla

47

Aspect

Es la fusión de notificación 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 más común 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 podrían ser transacciones, seguridad o verificación de credenciales.

Introduction

La Introducción permite añadir métodos o atributos a clases ya existentes. Un ejemplo en el

que resultaría útil es la creación de un Consejo de Auditoría que mantenga la fecha de la

última modificación de un objeto, mediante una variable y un método

setUltimaModificacion(fecha), que podrían ser introducidos en todas las clases (o sólo 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 añadir un comportamiento personalizado.

Proxy

El proxy o resultante, es un objeto creado después de aplicar una notificación 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 aplicación 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 compilación: 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 código de

esa clase destinataria antes de que la clase se introduzca en la aplicación.

• Tiempo de ejecución: Es la manera que tiene Spring de hacerlo. Los aspectos se

entretejen en algún momento durante la ejecución del programa. Normalmente, un

con tenedor AOP generará dinámicamente 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

modificación de campo, mientras sólo a invocación de métodos. 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.

Page 48: Spring Framework-Javier Sevilla

En Spring se da soporte de cuatro maneras algunas de ellas no incluidas en Spring 1:

1. AOP clásico, basado en proxy.

2. Notación AspectJ (sólo en Spring 2.x)

3. Aspectos POJO puros (sólo en Spring 2.x)

4. Aspectos AspectJ inyectados.

Como hemos comentado, Spring sólo da soporte a la interceptación de métodos, pero

teniendo en cuenta la filosofía de los bean de Java Enterprise Edition, esto lo convertiría en

una buena práctica.

El código del aspecto así como la configuración serán muy familiares a los desarrolladores, ya

que todo se escribe en Java. Sin embargo, AspectJ tiene su propia sintaxis y es una extensión.

En Spring hay cinco tipos de notificación. Cada una de ellas definida por un interfaz.

Tipos Interfaz

Para antes del método org.springframework.aop.MethodBeforeAdvice

Después del método org.springframework.aop.AfterReturningAdvice

Alrededor del método org.aopalliance.intercept.MethodInterceptor

Introducción org.springframework.aop.IntroductionInterceptor

Después del lanzamiento de

una introducción

org.springframework.aop.ThrowsAdvice

Todas las notificaciones a excepción de alrededor pertenecen al framework de Spring.

Ejemplo de aplicación en Spring En el primer capítulo dimos una ligera introducción a cómo Spring gestionaba aspectos con el

ejemplo del conserje que abre la puerta cuando el profesor va a dar clase. Ahora, con los

conceptos más aclarados es momento de volver al ejemplo, pero antes enseñaremos cuál sería

la implementación de la clase profesor si no lo hubiésemos programado con AOP.

public class TeacherImp implements Teacher { private String name; private Subject subject; private Conserje conserje; public TeacherImp(String name) { this.name = name; } public String getName() { return name; }

Page 49: Spring Framework-Javier Sevilla

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 implementación el profesor debería estar detrás del conserje para que le abriese la

puerta, perdiendo así su tiempo. Al igual que en ejemplo del primer capítulo, no queremos que

eso ocurra.

Creación de una clase Advice (Notificación)

Como hemos dicho, una notificación define lo que hace un aspecto y cuándo lo hace. Para la

notificación que hemos realizado haremos que implemente MethodBeforeAdvice,

AfterReturningAdvice y ThrowsAdvice, es decir, las interfaces para la ejecución de antes del

método, después del método y al producirse una excepción.

package es.uah.uahaop.model; import java.lang.reflect.Method; import org.springframework.aop.AfterReturningAdvice ; import org.springframework.aop.MethodBeforeAdvice; import org.springframework.aop.ThrowsAdvice; public class WatchmanAdvice implements MethodBefore Advice, 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, Throw able throwable){ watchman.teacherDidNotCome((Teacher target) ;//si el profesor no está }

Page 50: Spring Framework-Javier Sevilla

}

Como vemos en el código, la notificación tiene un conserje como dependencia así que en la

definición le inyectaremos una implementación de conserje.

<bean id="watchmanAdvice" class="es.uah.uahaop.aop.WatchmanAdvice"> <property name="watchman" ref="julian"/> </bean>

Echando un vistazo al código vemos cómo son los métodos que se tienen que implementar. Al

implementar MethodBeforeAdvice se requiere el método before(). Los tres parámetros que

tiene corresponden a java.lang.reflect.Method que es el método ejecutado para aplicar la

notificación. El segundo, la matriz, corresponde a los parámetros del método que se han

pasado. El último es el objeto del cual se ha ejecutado el método.

El método afterReturning es requisito de la interfaz ReturningAdvice y se ejecuta después de la

ejecución del método. Su primer parámetro es el objeto devuelto tras la ejecución del método

siendo el resto igual que los de before().

Por último hemos implementado la interfaz ThrowsAdvice la cual no es requisito implementar

ningún método, se toma como un mero marcador para Spring, diciéndole que esta notificación

es capaz de interceptar una excepción. Este método puede implementar distintos métodos

teniendo parámetros opcionales. Comparte cada uno de los parámetros de before() e incluye

otro más 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 excepción y será entonces cuando nuestra notificación (consejo o Advice) entrará en

escena haciendo que nuestro querido conserje cierre el aula.

MethodInterceptor (Around Advice o notificación alrededor)

La notificación alrededor es una notificación antes, después y si ocurre un lanzamiento. Su

funcionamiento es sencillo, en vez de ejecutar cada método, uno antes, otro después y otro si

hay una excepción, pues se ejecuta un método que se le pasará el método a invocar como

parámetro. La interfaz es MethodInterceptor y requiere que se implemente el método

invoke(). En esta clase podemos hacer un post-procesamiento del retorno del método, así

podemos verificar el objeto, modificarlo… etc. cosa que no podemos hacer con

AfterReturningAdvice.

Definir Pointcuts y Advice

Page 51: Spring Framework-Javier Sevilla

51

Ya que tenemos los Advice lo ideal sería poder darles uso, para ello crearemos puntos de

corte. Los puntos de corte seleccionan uno o más puntos de unión donde debe aplicarse una

notificación 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é método se va a notificar.

Declaraciones con expresiones regulares (Regular Expressions Pointcuts)

En Spring se disponen de dos clases que soportan expresiones regulares para la definición 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 definición sería así:

<bean id="watchmanPointcut" class="org.springframework.aop.support.JdkR egexpMethodPointcut"> <property name="pattern" value=".*teach"/> </bean>

La propiedad pattern es la expresión regular con la que diremos qué métodos queremos que

sean un punto de corte. En nuestro caso serán todos aquellos que se llamen “teach” ya sean

de cualquier clase. En el bean watchmanAdvisor estamos definiendo que punto de corte

tendrá la notificación.

<bean id="watchmanAdvisor" class="org.springframework.aop.support.Defa ultPointcutAdvisor"> <property name="advice" ref="watchmanAdvice "/> <property name="pointcut" ref="watchmanPoin tcut"/> </bean>

Pero esto seguramente acabe siendo muy repetitivo y acabaremos teniendo un bean patrón

para cada notificación. No hay que preocuparse, porque Spring tiene una clase que soluciona

tanto código xml. La clase org.springframework.aop.support.RegexpMethodPointcutAdvisor

nos lo soluciona.

<bean id="watchmanAdvisor2" class="org.springframework.aop.support.Rege xpMethodPointcutAdvisor"> <property name="advice" ref="watchmanAdvice "/> <property name="pattern" value=".*teach"/> </bean>

Definición de puntos de corte con AspectJ

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.

Page 52: Spring Framework-Javier Sevilla

Si se quiere reducir el código xml, al igual que pasaba con RegexpMethodPointcutAdvisor

existe una clase en la que podremos definir notificación y punto de corte, ésta es

AspectJExpressionPointcutAdvisor.

Configuración con ProxyFactoryBean

El ejemplo lo haremos con un nuevo profesor, el profesor Aurelio Martínez. La definición de

Aurelio será igual que la que hicimos en el capítulo primero de José Sarmiento.

<bean id="aurelioMartinezTarget" class="es.uah.uahaop.model.TeacherImp"> <constructor-arg value="Aurelio Martínez"/> </bean>

Como hemos dicho la definición 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

implementación de profesor que tenemos, sino el proxy que hemos creado para la ocasión. El

siguiente código ilustra lo comentado.

<bean id="aurelioMartinez" class="org.springframework.aop.framework.ProxyF actoryBean"> <property name="target" ref="aurelioMartine zTarget"/> <property name="interceptorNames" value="wa tchmanAdvisor"/> <property name="proxyInterfaces" value="es. uah.uahaop.model.Model" /> </bean>

En esta definición 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.

Page 53: Spring Framework-Javier Sevilla

53

Figura 1: Lo que realmente está pasando es que la clase que instancie el bean aurelioMartinez cuando ejecute su método teach() Spring interceptará esa llamada gracias al proxy y se le entregará a la notificación antes de la

ejecución del método.

Como vemos en el código quién 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 definición en el xml, con lo que ¿hay algo que nos permita definir todos aquellos beans

de tipo target que respondan al mismo patrón? La respuesta es sí, utilizando beans abstractos.

Abstracción de ProxyFactoryBean

Como hemos dicho, cuando en una aplicación se tienen varios destinatarios de la misma clase,

éstos tienen una configuración similar en el xml. Para eliminar código xml existe la posibilidad

de hacerlo con ProxyFactoryBean como un bean abstracto. La definición de éste será la

siguiente:

<bean id="watcherProxyBase" abstract="true" class="org.springframework.aop.framework.ProxyF actoryBean"> <property name="interceptorNames" value="wa tchmanAdvisor"/> <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 más 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"/>

Page 54: Spring Framework-Javier Sevilla

</bean> <bean id="julianRebollo" parent="watcherProxyBa se"> <property name="target" ref="julianRebolloT arget"/> </bean> <bean id="adolfoRenato" parent="watcherProxyBas e"> <property name="target" ref="adolfoRenatoTa rget"/> </bean>

Renombrar a los beans que en principio serían los que la aplicación instanciará añadiéndoles 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

capítulo primero).

Autoproxying

Spring da soporte a la conversión en proxy de bean. El Autoproxying nos ofrece una

implementación AOP más completa dejando que la definición de punto de corte de un aspecto

decida qué beans serán proxy.

Se pueden hacer de dos maneras. La primera con el autoproxying básico de beans basado en

beans notificadores declarados en el contexto Spring y la otra con anotación AspectJ.

Autoproxy básico de beans.

El siguiente código ilustra cómo hacerlo:

<bean id="exampleAdvisor" class="org.springframework.aop.aspectj.AspectJE xpressionPointcutAdvisor"> <property name="advice" ref="exampleAdvice" /> <property name="expression" value="execution(* *.methodExampleInvocatio n(..))"/> </bean>

La propiedad advice dice que notificación aplicar y la propiedad expression dice dónde

aplicarla. Spring tiene una implementación de BeanPostProccessor que crea automáticamente

beans proxy con notificadores coincidentes llamada DefaultAdvisorProxyCreator, cuya

declaración es la siguiente.

<bean class="org.springframework.aop.framework.auto proxy. DefaultAdvisorAutoProxyCreator"/>

Este bean no tiene id, eso es debido a que nunca nos referiremos a él. Después de esto ya no

necesitamos declarar ProxyFactoryBean ni poner otro nombre al target.

Esto funcionará en entornos java inferiores a 5. Pero si nuestra aplicación está en Java 5 puede

utilizar anotación de AspectJ.

Page 55: Spring Framework-Javier Sevilla

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 org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import 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 " + teache r.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 " + teach er.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.getN ame() + " de " +

Page 56: Spring Framework-Javier Sevilla

subject.getName()); classRoom.setOpen(false); } }

La anotación @Aspect indica que es un aspecto, no un simple POJO. @Pointcut y el método

teaching añadido marcan el punto de corte. Los métodos openClassRoom y closeClassRoom

han sido anotados para que se ejecuten antes y después del método teach y el método

teacherDidNotCome se le ha hecho una anotación para que se ejecute si hay una excepción.

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 automáticamente creará proxy cuyos métodos 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 capítulo incluiremos la siguiente definición 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.springframewo rk.org/schema/beans http://www.springframework.org/schema/b eans/spring-beans-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">

También AnnotationAwareAspectJAutoProxyCreator hace lo mismo que DefaultAdvisor-

AutoProxyCreator.

Existe también la posibilidad de crear una notificación alrededor, ésta sería con la anotación

@Around(“metodoAEjecutar()”) y definiríamos todo de la misma manera que con la anterior

anotación.

Creación de aspectos POJO

A pesar de todas las formas que tiene Spring para declarar aspectos no es del todo intuitivo ni

rápido. A partir de la segunda versión de Spring se ofrecen nuevos elementos en el namespace

aop que simplifican la manera de declarar aspectos. Con los elementos de configuración aop

Page 57: Spring Framework-Javier Sevilla

57

podemos convertir cualquier clase en un aspecto. Por fin hemos llegado al punto en el que

dejamos el primer capítulo, así que escribiremos de nuevo la definición 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="closeClass Room" pointcut-ref="classRoomDoorToTeach" arg-names="bean" /> </aop:aspect> </aop:config>

Elemento de configuración AOP Utilidad

<aop:advisor> Configura un notificador

<aop:before> Configura una notificación antes de la ejecución.

<aop:after> Configura una notificación posterior (haya habido éxito

en la ejecución o no)

<aop:around> Configura una notificación alrededor

<aop:after-throwing> Configura una notificación de excepción

<aop:after-returning> Configura una notificación después del retorno

<aop:pointcut> Punto de corte definido

<aop:aspect> Definición de un aspecto, etiqueta contenida en

<aop:config>

<aop:config> Elemento padre de las etiquetas aop

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 método a pesar de que comparta la

anotación @Aspect. Si necesitamos algo más potente deberemos crear aspectos con AspectJ.

Spring permite inyectar beans creados con AspectJ y hacer uso de ellos.

Page 58: Spring Framework-Javier Sevilla
Page 59: Spring Framework-Javier Sevilla

59

Page 60: Spring Framework-Javier Sevilla

Capítulo 4:

Persistencia Peticiones a bases de datos, persistencia y

patrones DAO Hoy en día, la construcción de cualquier aplicación empresarial es inviable sin una base de datos. La información con la que el sistema informático 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 Sánchez

Page 61: Spring Framework-Javier Sevilla

61

Contenido Spring y el acceso a datos ........................................................................................................... 62

Patrón DAO.............................................................................................................................. 62

Excepciones en Spring ............................................................................................................. 63

Plantillas ...................................................................................................................................... 64

Uso de clases de soporte DAO ................................................................................................ 65

Configuración de una fuente de datos ........................................................................................ 66

Configuración JDBC ................................................................................................................. 66

Uso de plantillas JDBC ................................................................................................................. 67

JdbcTemplate .......................................................................................................................... 67

Utilizar parámetros nombrados .............................................................................................. 70

Simplificación de JDBC ............................................................................................................ 70

Clases de Spring para trabajar con JDBC ..................................................................................... 72

Plataformas de persistencia en Spring ........................................................................................ 73

Integración 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

Page 62: Spring Framework-Javier Sevilla

Spring y el acceso a datos

Gracias a los anteriores capítulos tenemos una idea clara de qué es lo que el contenedor de

Spring es capaz de hacer y de cuál es la filosofía de Spring con respecto a los beans, la DI y la

AOP. En este capítulo construiremos una capa de persistencia y explicaremos las múltiples

opciones que Spring nos otorga para ello.

Spring es compatible todos los marcos de persistencia que existen para Java como pueda ser

Hibernate 2, Hibernate 3, Java Persistence API (JPA), iBATIS o cualquier otro. La función

principal será facilitar aún más el trabajo. Spring hace muy buenas migas con JDBC facilitando

la configuración y capturando y dando forma a las excepciones que se produzcan.

Como hemos dicho, en anteriores capítulos hemos visto que Spring tiene como objetivo, entre

otros, inculcar las buenas artes que dentro de la programación orientada a objetos supone la

codificación en interfaces. Como podrá adivinar el modo que tiene Spring para el acceso a

datos cumple también con ello gracias al patrón DAO.

Patrón DAO

El problema que viene a resolver este patrón 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 patrón surgió de la necesidad de gestionar una diversidad de

fuentes de datos, aunque su uso se extiende al problema de encapsular no sólo la fuente de

datos, sino además 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 cómo se realiza el acceso a los datos o de

cuál 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 estándar 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

método estándar para acceder a tablas y vistas. La idea de este patrón es ocultar la fuente de

datos y la complejidad del uso de JDBC a la capa de presentación o de negocio.

Un DAO define la relación entre la lógica de presentación y empresa por una parte y por otra

los datos. El DAO tiene un interfaz común, 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.

Deberían exponer esta funcionalidad mediante una interfaz por la que el resto de la aplicación

accediera a ellos.

Page 63: Spring Framework-Javier Sevilla

63

Figura 2 Los objetos de servicio delegan el acceso a datos a los DAO. La interfaz DAO mantiene el acoplamiento débil.

Los objetos de servicio acceden mediante interfaces DAO. Así los objetos de servicio son

comprobables y no están acoplados a una única implementación.

Excepciones en Spring

Al escribir código JDBC estamos obligados a capturar excepciones SQLException. Ésta ha

podido producirse por varias razones, que deberíamos estar averiguando. Otro de los

problemas es que la mayoría de las excepciones responden a una situación 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 excepción.

JDBC de Spring proporciona una jerarquía más amplia de excepciones con las que podemos

resolver más problemas ya que son más descriptivas.

Excepciones de JDBC Excepciones de DB Spring

BatchUpdateException CannotAcquierLockException DataTruncation CannotSerializeTtransactionException SQLException CleanupFailureDataAccessException SQLWarning ConcurrencyFailureDataAccessException DataAccessException DataAccesResourceFailureException DataIntegrityViolationException DataRetrievalFailureException DeadlockLoserDataAccessException

Page 64: Spring Framework-Javier Sevilla

EmpyResultDataAccessException EmptyResultDataAccessException IncorrectUpdateSemantisDataException InvalidDataAccessResourceUsageException OptimisticLockingFailureException PermissionDeniedDataAccessException PessunustucLockingFailureException TypeMismatchDataAcessException UncategorizedDataAccessException

Todas las excepciones tienen como padre a DataAccessException la cual es una excepción sin

comprobación, con lo que no se tiene por qué atrapar nada si no se desea. Sí, Spring prefiere

dejar libre al programador la elección de si quiere capturarla o no, ya que en otras ocasiones

éste se ve obligado a capturar un sinfín de bloques try-catch que a menudo se dejan vacíos.

Plantillas

Un método de plantilla define el esqueleto de un proceso que es fijo en una operación. 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 específicas 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

Page 65: Spring Framework-Javier Sevilla

65

persistencia que vayamos a usar. Usar una plantilla simplifica mucho el código de acceso a

datos y se configura como un bean del contexto de Spring.

Plantilla

CciTemplate Conexiones JCA CCI

JdbcTemplate Conexiones JDBC

NamedParameterJdbcTemplate JDBC con soporte para parámetros nombrados

SimpleJdbcTemplate JDBC simplificadas

HibernateTemplate Hibernate 2

HibernateTemplate (…orm.hibernate3.*) Hibernate 3

SqlMapClientTemplate iBATIS SqlMap

JdoTemplate JDO

JpaTemplate JPA Java Persistence Api

TopLinkTemplate TopLink de Oracle

También se pueden usar las clases DAO de Spring para simplificar más la aplicación. Hay clases

básicas DAO de Spring que pueden gestionar la plantilla por nosotros.

Uso de clases de soporte DAO

Cada plantilla también ofrece métodos prácticos que simplifican el acceso a datos sin

necesidad de crear una implementación de retrollamada explícita. Spring proporciona clases

de soporte DAO destinadas a ser subclases de las clases DAO que hagamos en nuestro

proyecto.

En la siguiente figura se muestra la relación.

Figura 4 La relación entre un DAO de aplicación y el soporte DAO de Spring y las clases plantilla

Page 66: Spring Framework-Javier Sevilla

Spring ofrece varias clases de soporte DAO. Cada plantilla tendrá su soporte DAO de Spring. En

la siguiente tabla se enumeran.

SOPORTE DAO TIPO

CciDaoSupport JCA CCI

JdbcDaoSupport JDBC

NamedParameterJdbcDaoSupport JDBC con soporte para parámetros nombrados

HibernateDaoSupport Hibernate 2

HibernateDaoSupport (orm.hibernate3.support)

Hibernate 3

SqllMapClientDaoSupport iBatis

JdoDaoSupport JDO

JpaDaoSupport JPA

TopLinkDaoSupport TopLink

A pesar de la cantidad de frameworks de persistencia con los que Spring puede trabajar

empezaremos con lo más sencillo, una conexión JDBC. Antes veamos cómo se configura una

fuente de datos.

Configuración de una fuente de datos

Configuración JDBC

Es la fuente de datos más simple. Spring tiene dos clases de fuentes de datos de este tipo.

DriverManagerDataSource que devuelve una nueva conexión cada vez y

SingleConnectionDataSource que devuelve la misma conexión.

Veamos cómo se configura un DriverManagerDataSource en el fichero xml:

<bean id="dataSourceJDBC" class="org.springframework.jdbc.datasource.Driv erManagerDataSource"> <property name="driverClassName" value="com .mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost/jdbcspringexample"/> <property name="username" value="ADMIN"/> <property name="password" value="ADMIN"/> </bean>

Como vemos hemos de configurar las propiedades url que será la url de acceso a la base de

datos, el usuario y su contraseña así como qué driver usará, en nuestro caso el driver de

MySql.

Page 67: Spring Framework-Javier Sevilla

67

Uso de plantillas JDBC

Todo aquel que haya trabajado con JDBC sabrá que, a pesar del potencial que tiene este, para

hacer cualquier operación sencilla se tendrá que escribir una innumerable clase con bloques

try-catch obligatorios en los que poco se puede hacer.

Es por ello que Spring trata todo este tipo de código estándar repetitivo. Spring limpia el

código JDBC asumiendo la gestión de recursos y excepciones. Como ya dijimos, Spring abstrae

el código estándar a través de clases plantilla. Hay tres JdbcTemplate,

NamedParameterJdbcTemplate y SimpleJdbcTemplate.

JdbcTemplate

Es la implementación más sencilla, permite el acceso a datos mediante JDBC y consultas

sencillas de parámetros indexados. Tan sólo necesita un DataSource para funcionar así que el

código xml será muy sencillo. Utilizaremos el DataSource definido anteriormente:

<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource JDBC"/> </bean>

La propiedad dataSource puede ser cualquier implementación de javax.sql.DataSource.

Interfaz persona y su implementación

Para poder llevar a cabo el ejemplo crearemos una interfaz sencilla de una persona que

tenga métodos 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); }

Una implementación de la clase persona podría ser la siguiente:

package es.uah.jdbcspringexample.model; public class PersonImp implements Person {

Page 68: Spring Framework-Javier Sevilla

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); }

Empezaremos con la implementación JDBC para nuestro DAO, el código es el siguiente:

public class JdbcPersonDao implements PersonDao { private JdbcTemplate jdbcTemplate; private static final String PERSON_INSERT = "insert into person (id, name) " + "val ues (?,?)"; private static final String PERSON_SELECT = "select id, name from person "; private static final String PERSON_SELECT_ID = PERSON_SELECT + " where id=?"; public JdbcPersonDao(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } public void setJdbcTemplate(JdbcTemplate jdbcTe mplate) { this.jdbcTemplate = jdbcTemplate; } public JdbcTemplate getJdbcTemplate() { return jdbcTemplate; } public Person getPerson(int personID) { List matches = jdbcTemplate.query(PERSON_SE LECT_ID, new Object[]{Long.valueOf(personID) }, new RowMapper() {

Page 69: Spring Framework-Javier Sevilla

69

public Object mapRow(ResultSet rs, int rowNum) throws SQLException, Da taAccessException { Person person = new PersonI mp(); person.setId(rs.getInt(1)); person.setName(rs.getString (2)); return person; } }); return matches.size() > 0 ? (Person) matc hes.get(0) : null; } public void savePerson(Person person) { jdbcTemplate.update(PERSON_INSERT, new Obje ct[]{ new Integer(person.getId()), person.get Name()}); } public List<Person> getAllPerson() { List matches = jdbcTemplate.query(PERSON_SE LECT, new RowMapper() { public Object mapRow(ResultSet rs, int rowNum) throws SQLException, Da taAccessException { Person person = new PersonI mp(); 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

definición de Spring con el bean que hemos definido anteriormente. La definición sería así de

sencilla:

<bean id="jdbcPersonDao" class="es.uah.jdbcspringexample.dao.JdbcPersonDao" > <constructor-arg ref="jdbcTemplate"/> </bean>

Analizando el método 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 obtención de datos, en los métodos getPerson y getAllPerson son también sencillas, es clave

el uso del objeto RowMapper y el uso de la plantilla del método query. Vemos que el método

query toma por parámetro 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 método abstracto rowMap. Éste

método se ejecutará una vez por fila devuelta en la base de datos.

Page 70: Spring Framework-Javier Sevilla

Ésta implementación está muy bien, pero aún hay pegas. Para el método savePerson se

utilizan parámetros que han de llevar un orden con lo que si algún día hay algún cambio

tendremos que tener en cuenta este orden.

Utilizar parámetros nombrados

Podemos usar parámetros nombrados, así podremos dar a cada parámetro en SQL un nombre

explícito. De la siguiente forma:

private static final String PERSON_INSERT = "ins ert into person (id, name)" + "values (:id,:name)";

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 definición en el fichero xml es la

siguiente.

<bean id="namedJdbcTemplate" class="org.springframework.jdbc.core.namedparam.Nam edParameterJdbcTemplate"> <constructor-arg ref="dataSourceJDBC"/> </bean> <bean id="namedParameterJdbcPersonDao" class="es.uah.jdbcspringexample.dao.NamedPa rameterJdbcPersonDao"> <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.

Simplificación de JDBC

A partir de la versión 5 de java es posible pasar listas de parámetros de longitud variable a un

método. Así la clase SimpleJdbcPersonDao la codificaremos de la siguiente manera:

public class SimpleJdbcPersonDao implements PersonD ao { private SimpleJdbcTemplate simpleJdbcTemplate; private static final String PERSON_INSERT = "insert into person (id, name) " + "v alues (?,?)"; 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 s impleJdbcTemplate) { this.simpleJdbcTemplate = simpleJdbcTemplat e;

Page 71: Spring Framework-Javier Sevilla

71

} public void setJdbcTemplate(SimpleJdbcTemplate simpleJdbcTemplate) { this.simpleJdbcTemplate = simpleJdbcTemplat e; } public SimpleJdbcTemplate getSimpleJdbcTemplate () { return simpleJdbcTemplate; } public Person getPerson(int personID) { List matches = simpleJdbcTemplate.query(PER SON_SELECT_ID, new ParameterizedRowMapper() { public Person mapRow(ResultSet rs, int rowNum) throws SQLException { Person person = new PersonI mp(); person.setId(rs.getInt(1)); person.setName(rs.getString (2)); return person; } }, personID); return matches.size() > 0 ? (Person) matche s.get(0) : null; } public void savePerson(Person person) { simpleJdbcTemplate.update(PERSON_INSERT, pe rson.getId(), person.getName()); } public List<Person> getAllPerson() { List matches = simpleJdbcTemplate.query(PER SON_SELECT, new ParameterizedRowMapper() { public Object mapRow(ResultSet rs, int rowNum) throws SQLException, Da taAccessException { Person person = new PersonI mp(); 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 versión 5.

En el método getPerson vemos que los parámetros del método query del objeto

SimpleJdbcTemplate relativos a la formulación de la query (es decir qué parámetros se

utilizaran a la hora de ser sustituidos por cada ‘?’) se podrán como los últimos parámetros no

teniendo límite y pudiendo ser primitivos (no como en el ejemplo de JdbcPersonDao en el cual

teníamos que crear objetos Integer), es decir, poder hacer autobox. Como vemos también se

usa un nuevo objeto abstracto, el ParametrizedRowMapper, del cual tendremos que

implementar el método mapRow().

La definición del bean en el fichero xml es la siguiente:

Page 72: Spring Framework-Javier Sevilla

<bean id="simpleJdbcTemplate" class="org.springframework.jdbc.core.simple.Simple JdbcTemplate"> <constructor-arg ref="dataSourceJDBC"/> </bean> <bean id="simpleJdbcPersonDao" class="es.uah.jdbcspringexample.dao.SimpleJdbcPers onDao"> <constructor-arg ref="simpleJdbcTemplate"/> </bean>

Clases de Spring para trabajar con JDBC

Como hemos podido ver, el código que hemos de generar es muy repetitivo. Cada clase DAO

ha de contener una plantilla que a su vez tenga una fuente de datos. Como era de esperar,

Spring proporciona una manera alternativa para poder disponer de todo esto otorgando clases

de las cuales podamos heredar para crear nuestros DAO.

El siguiente código muestra cómo nuestro DAO extiende de la clase JdbcDaoSupport.

public class JdbcPersonDao extends JdbcDaoSupport i mplements PersonDao{ private static final String PERSON_INSERT = "insert into person (id, name) " + "val ues (?,?)"; 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(PERS ON_SELECT_ID, new Object[]{Long.valueOf(personID) }, new RowMapper() { public Object mapRow(ResultSet rs, int rowNum) throws SQLException, Da taAccessException { Person person = new PersonI mp(); person.setId(rs.getInt(1)); person.setName(rs.getString (2)); return person; } }); return matches.size() > 0 ? (Person) matc hes.get(0) : null; } public void savePerson(Person person) { getJdbcTemplate().update(PERSON_INSERT, new Object[]{ new Integer(person.getId()), person.get Name()}); } public List<Person> getAllPerson() { List matches = getJdbcTemplate().query(PERS ON_SELECT, new RowMapper() { public Object mapRow(ResultSet rs, int rowNum) throws SQLException, Da taAccessException { Person person = new PersonI mp(); person.setId(rs.getInt(1));

Page 73: Spring Framework-Javier Sevilla

73

person.setName(rs.getString (2)); return person; } }); return matches; } }

Con esto hemos eliminado la tarea de crear métodos 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á automáticamente.

Al igual que los anteriores ejemplos (Parámetros nombrados y simplificación de JDBC) también

existen clases abstractas de soporte en Spring con estas plantillas. Estas son

NamedParameterJdbcDaoSupport y SimpleJdbcDaoSupport.

Plataformas de persistencia en Spring

En el mercado existen diversas plataformas para dar solución a un sistema de persistencia

basado en mapeo de modelos de objeto relacionales (ORM). Spring es compatible con varios

marcos de trabajo entre ellos Hibernate, iBatis, Toplink o JPA. Spring aporta compatibilidad

integrada para transacciones declarativas de Spring, manejo de excepciones, plantillas, soporte

DAO y gestión de recursos.

Integración de Hibernate en Spring

Hibernate es el framework que más ha tenido acogida dentro de la comunidad siendo este de

código abierto y desarrollado en un principio por desarrolladores dispersos alrededor del

mundo. No sólo otorga ORM sino que también ofrece solución al duplicado de datos, la carga

perezosa, eager fetching y duplicado de datos.

Un aspecto importante que hemos de tener en cuenta antes de embarcarnos en el desarrollo

es elegir la versión de Hibernate, al margen de las nuevas funcionalidades, ya que el nombre

de los paquetes varia de la versión 2 a la 3 y esto hace que tengamos que diferenciarlo en

Spring. Así para la versión 2 de Hibernate utilizaremos las clases contenidas en

org.springframework.orm.hibernate y para la versión 3 utilizaremos las clases del paquete

org.springframework.orm.hibernate3. Como el lector habrá pensado esta decisión se tomó

para mantener la retrocompatibilidad.

Page 74: Spring Framework-Javier Sevilla

Instanciar SessionFactory de Hibernate

Existen varias opciones para instanciar el objeto SessionFactory de Hibernate en Spring,

utilizando archivos de mapeo clásicos XML o con anotaciones.

LocalSessionFactoryBean

El objeto LocalSessionFactoryBean de Spring es un bean factory de Spring que produce una

instancia local de SessionFactory de Hibernate que toma sus parámetros de mapeo de los

archivos XML.

La definición de este bean sería la siguiente:

<bean id="localSessionFactory" class="org.springframework.orm.hibernate3.L ocalSessionFactoryBean"> <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">${hibe rnate.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 configuración de Hibernate.

AnnotationSessionFactoryBean

Si lo que deseamos es utilizar objetos cuyas clases implementen las anotaciones JPA así como

específicas 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.a nnotation. AnnotationSessionFactoryBean"> <property name="dataSource" ref="dataSource "/> <property name="annotatedClasses" > <list> <value>es.uah.hibernateaddendum.mod el.Exam</value> <value>es.uah.hibernateaddendum.mod el.Student</value> <value>es.uah.hibernateaddendum.mod el.Subject</value> <value>es.uah.hibernateaddendum.mod el.SubjectUnit</value> </list>

Page 75: Spring Framework-Javier Sevilla

75

</property> <property name="hibernateProperties"> <props> <prop key="hibernte.dialect">${hibe rnate.dialect}</prop> </props> </property> </bean>

Como vemos la definición 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 factoría de sesión de Hibernate de cualquiera de las

maneras vistas.

El siguiente XML muestra cómo se configura HibernateTemplate en Spring:

<bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.Hib ernateTemplate"> <property name="sessionFactory" ref="localS essionFactory" /> </bean>

Y el siguiente sería para utilizar el annotationSessionFactoryBean definido anteriormente.

<bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.Hib ernateTemplate"> <property name="sessionFactory" ref="annota tionSessionFactoryBean" /> </bean>

Ambas definiciones son idénticas, cabe señalar que en atributo sessionFactory podremos

pasarle cualquier clase que implemente FactoryBean.

Una vez hecho todo esto estamos en situación de crear un DAO que haga uso de la plantilla y

así poder persistir y recuperar objetos.

El siguiente código pertenece al DAO HibernateExamsDaoImp y sirve de ejemplo de cómo se

hace uso de la plantilla:

public class HibernateExamsDaoImp { private static final String INSERT = "insert"; private static final String DELETE = "delete"; private static final String GET_EXAM = "getExam "; private static final String EXAM = Exam.class.g etName(); private 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; }

Page 76: Spring Framework-Javier Sevilla

public void setHibernateTemplate(HibernateTempl ate 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 Exam sException { for (Iterator<Exam> it = exams.iterator(); it.hasNext();) { this.insert(it.next()); } } public Exam getExam(int id) throws ExamsExcepti on { 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_A LL); } public void delete(int id) throws ExamsExceptio n { getHibernateTemplate().delete(this.getExam( id)); } }

Así las operaciones monótonas necesarias para operar con Hibernate serán controladas por la

plantilla.

La definición dentro del contenedor de Spring es la siguiente:

<bean id="hibernateExamsDaoImp" class="es.uah.hibernatespringintegration.dao. HibernateExamsDaoImp"> <property name="hibernateTemplate" ref="hib ernateTemplate" /> </bean>

HibernateDaoSupport

Al igual que hemos visto con JdbcDaoSupport, Spring también facilita la construcción de DAO

en Hibernate. La clase HibernateDaoSupport otorga una plantilla HibernateTemplate con lo

que sólo le tendremos que inyectar un objeto SessionFactory.

El siguiente código pertenece a la clase HibernateStudentsDaoImp que como vemos hereda de

HibernateDaoSupport:

public class HibernateStudentsDaoImp extends Hibern ateDaoSupport { private static final String STUDENT = Student.c lass.getName();

Page 77: Spring Framework-Javier Sevilla

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) throw s StudentsException { for (Iterator<Student> it = students.iterat or(); it.hasNext();) { this.insert(it.next()); } } public Student getStudent(int id) throws Studen tsException { 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_A LL); } public void delete(int id) throws StudentsExcep tion { getHibernateTemplate().delete(this.getStude nt(id)); } }

Con las clases de soporte Spring facilita al máximo la codificación de nuevas clases, reduciendo

el número de líneas de código de estas y reduciéndolas a la mínima expresión.

Page 78: Spring Framework-Javier Sevilla

JPA (Java Persistence API)

Java Persistence API, más conocida por sus siglas JPA, es la API de persistencia desarrollada

para la plataforma Java EE o la Java Persistence API, a veces referida como JPA, es un

framework del lenguaje de programación Java que maneja datos relacionales en aplicaciones

usando la Plataforma Java en sus ediciones Standard (Java SE) y Enterprise (Java EE).

El objetivo que persigue el diseño de esta API es no perder las ventajas de la orientación a

objetos al interactuar con una base de datos (siguiendo el patrón de mapeo objeto-relacional),

como sí pasaba con EJB2, y permitir usar objetos regulares (conocidos como POJOs).

La parte de la especificación de EJB 3 que sustituye los beans de entidad se conoce como JPA.

Configurar EntityManagerFactory

Para usar JPA se ha de usar una implementación de EntityManagerFactory para obtener una

instancia de un EntityManager. La especificación JPA define dos tipos de gestores de entidad,

gestionados por la aplicación o por el contenedor. Los gestionados por la aplicación se crean

cuando la aplicación directamente pide un gestor de entidad de una fábrica de gestor de

entidad. Cuando ésta se usa es la aplicación 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 aplicación no interactúa en absoluto con la fábrica 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 inyección. Ambos gestores implementan

EntityManager.

Para los desarrolladores, al utilizar la interfaz, no tendremos que saber en muchos casos ni

siquiera cuál estamos utilizando. Spring gestionará esto por nosotros.

Para ello Spring tiene dos beans, LocalEntityManagerFactoryBean y

LocalContainerEntityManagerFactoryBean, siendo la primera gestionada por la aplicación 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 hallarán la mayor parte de la configuración. En este fichero

definimos tantas unidades de persistencia como queramos, enumerando las clases

persistentes.

Page 79: Spring Framework-Javier Sevilla

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/persiste nce_1_0.xsd" version ="1.0" > <persistence-unit name="CbosCommonsPU" transaction-type ="RESOURCE_LOCAL"> <provider >org.apache.openjpa.persistence.PersistenceProviderI mpl </ 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 >

Más tarde definiremos el bean en el fichero de Spring y le deberemos pasar la unidad de

persistencia como parámetro.

<bean id ="entityManagerFactory" class ="org.springframework.orm.jpa.LocalEntityManagerFact oryBean" > <property name="persistenceUnitName" value ="youdiversity" />

<property name="jpaVendorAdapter" > <bean

class ="org.springframework.orm.jpa.vendor.TopLinkJpaVendo rAdapter" > <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 información

proporcionada por el contendor. Esta es la configuración típica cuando usamos JBoss o

WebLogic, es decir, servidores de aplicación JEE.

El siguiente código muestra cómo configurar un bean

LocalContainerEntityManagerFactoryBean.

<bean id ="entityManagerFactory" class ="org.springframework.orm.jpa.LocalContainerEntityMa nagerFactoryBean" > <property name="dataSource" ref ="youdiversityDataSource" /> <property name="jpaVendorAdapter" > <bean class ="org.springframework.orm.jpa.vendor.TopLinkJpaVendo rAdapter" > <property name="showSql" value ="true" /> <property name="generateDdl" value ="true" /> <property name="database" value ="HSQL" /> </ bean >

Page 80: Spring Framework-Javier Sevilla

</ 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 implementación de JPA a utilizar.

Plantillas JPA (JpaTemplate)

Al igual que con otras soluciones de persistencia, Spring otorga una plantilla llamada

JpaTemplate. Esta plantilla contendrá un EntityManagers de JPA. En el siguiente XML vemos

cómo se configura una plantilla.

<bean id ="jpaTemplate" class ="org.springframework.orm.jpa.JpaTemplate" > <property name="entityManagerFactory" ref ="entityManagerFactory" />

</ bean >

La propiedad entityManagerFactory de JpaTemplate debe conectarse con una implementación

de la interfaz javax.persistence.EntityManagerFactory de JPA.

JpaTemplate al igual que otras plantillas tiene muchos de los métodos 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 cómo 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; } ... }

Para configurar este DAO simplemente conectamos JpaTemplate a la propiedad jpaTemplate:

<bean id ="userDao" class ="youdiversity.model.dao.jpa.UserJpaDao" > <property name="jpaTemplate" ref ="jpaTemplate" /> </ bean >

Una manera de usar la plantilla sería así.

public void saveUser(User user){ getJpaTemplate().persist(user); } public List<User> getAllUsers(){ return getJpaTemplate().find( "select u from User u;" ); }

Page 81: Spring Framework-Javier Sevilla

81

Extender de JpaDaoSupport para construir un DAO

La clase JpaDaoSupport es una clase abstracta que tiene una plantilla JpaTemplate, así que en

vez de conectar a cada DAO su plantilla, lo que haremos será extender de JpaDaoSupport y

conectar el bean entityManagerFactory.

Así nuestro DAO extenderá de JpaDaoSupport así:

public class BaseDaoJpa<T extends BaseEntity> extends JpaDaoSupport { …

}

La definición será tan sencilla como:

<bean id ="baseDao" class ="youdiversity.model.orm.dao.jpa.BaseDaoJpa" > <property name="entityManagerFactory" ref ="entityManagerFactory" /> </ bean >

Page 82: Spring Framework-Javier Sevilla
Page 83: Spring Framework-Javier Sevilla

83

Page 84: Spring Framework-Javier Sevilla

84

Capítulo 5:

Modelo Vista

Controlador Peticiones web, controladores, formularios

y vistas. Gracias a Spring MVC podremos crear aplicaciones robustas basadas en el patrón Modelo Vista Controlador (MVC) aportando herramientas de fácil comprensión y utilización y encapsulando el manejo y manipulación de los Servlets.

Javier Sevilla Sánchez

Page 85: Spring Framework-Javier Sevilla

85

Contenido ¿Qué es Spring MVC? .................................................................................................................. 87

Características de Spring MVC .................................................................................................... 87

DispatcherServlet ........................................................................................................................ 88

Configuración de DispatcherServlet y ContextLoaderListener ............................................... 89

Usando Spring MVC sin anotaciones........................................................................................... 91

Creando nuestro primer Controlador ..................................................................................... 92

El objeto ModelAndView ........................................................................................................ 93

Configuración del controlador ................................................................................................ 93

View Resolvers ........................................................................................................................ 93

Crear un JSP ............................................................................................................................. 94

Pasos de la petición ..................................................................................................................... 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 parámetros de la petición a parámetros de métodos 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 sesión ........................................................... 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

Page 86: Spring Framework-Javier Sevilla

El prefijo “redirect:” .......................................................................................................... 108

El prefijo “forward:” .......................................................................................................... 108

ContentNegotiatingViewResolver ......................................................................................... 108

Configuración 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

Page 87: Spring Framework-Javier Sevilla

87

¿Qué es Spring MVC?

La arquitectura MVC (Modelo vista controlador) se basa en la separación de los datos y

modelo de la aplicación (Modelo), la interfaz de usuario (comúnmente un navegador que

recibe código HTML) y la interacción entre ambos, el controlador.

En una aplicación MVC, la gestión de estado, la validación y el flujo de trabajo son temas

fundamentales y principal foco de atención. 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 configuración local, de temas así como para la subida de ficheros. A lo largo de

Spring ha habido cambios desde la definición 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 también objetos command (anteriores a la versión 3) pero

actualmente se pueden utilizar cualquier objeto como un command. Este objeto será útil para

la extracción de información de formularios. Así con Spring no tendremos que duplicar los

objetos teniendo, por ejemplo, un POJO con cadenas que trasformaremos en el objeto más

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, específicamente el MVC, está diseñado para facilitar tanto la

construcción de controladores, como las vistas que están asociadas así como la interacción con

los objetos del modelo. Todo esto de la manera más flexible y con la posibilidad de integrar

otros marcos de trabajo conocidos como Struts o JSF.

Características de Spring MVC

• Una clara separación de roles.

• Una potente y sencilla configuración entre el marco de trabajo y las clases de la

aplicación como las JavaBeans.

• Adaptabilidad, no intrusión y flexibilidad.

• Reusabilidad del código empresarial, sin necesidad de duplicado, pudiendo usar código

empresarial existente como comandos o formularios reflejándolos como una clase del

marco de trabajo en particular.

Page 88: Spring Framework-Javier Sevilla

• Validaciones y enlaces personalizados, enlazando valores reales como fechas y

números evitando la conversión 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 tecnología de vista.

• Se pueden configurar tanto temas como configuración local de diversas maneras

compatibles con JSP o Velocity.

• Spring también tiene una potente librería de tag que ofrece tanto el enlace de datos,

temas o formularios.

• Los beans tienen un ámbito de aplicación de petición o sesión, esto no es específico 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 características 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, métodos, (principalmente POST y GET)

así como los parámetros, objetos JSON etc. El servidor analizará la petición y deberá decidir

qué controlador será quién 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 añadir una capa más, de manera que todas las

peticiones que lleguen (y que queramos que se haga cargo el framework de Spring) llegarán a

el Servlet DispatcherServlet. Éste será el encargado, tras analizar la petición, 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 petición.

Es decir, cuando una petición sale del navegador tiene la URL solicitada y también puede tener

datos adicionales como datos de un formulario, esto llegará al DispatcherServlet (siempre que

cumpla el patrón que para él hayamos configurado). Este Servlet frontal lo que hará será

consultar al HandlerMapping si la petición está contenida y de ser así que Controlador está

asociado. Si existe coincidencia se le devolverá un controlador y un método de este el cuál

ejecutará y tras su ejecución devolverá un objeto ModelAndView el cual encapsula una vista y

los datos generados tras la ejecución 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 código HTML tras la ejecución e interpretación del JSP.

Page 89: Spring Framework-Javier Sevilla

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 más

que eso, ya que está integrado al contenedor de Spring y así puede disfrutar de todas las

ventajas de la inyección de dependencia y de la inversión de control así como cualquier

característica de Spring.

El flujo de trabajo del procesado de una petición está en el siguiente diagrama, la petición 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 ejecución al controlador frontal

que se encargará de buscar una vista, pasarle el modelo para después dar una respuesta al

cliente, el navegador.

Esto ha sido una breve introducción y resumen de lo que Spring MVC hace más comúnmente,

aunque cada una de las piezas comentadas realmente puede tener un comportamiento más

complejo, esta introducción nos da una primera impresión de los fundamentos de éste marco

de trabajo, más tarde daremos una explicación más detallada, pero de momento lo primero

que debemos hacer es configurar nuestro DispatcherServlet en el contenedor de Spring.

Configuración de DispatcherServlet y ContextLoaderListener

Como hemos dicho, DispatcherServlet será el Servlet que funciona como conector principal

con los controladores de Spring. Para que esto ocurra, hemos de configurar la clase como otro

Servlet más en el fichero web.xml y asignarle un patrón de URL. Si la petición coincide con el

patrón definido será redireccionado a este controlador.

Page 90: Spring Framework-Javier Sevilla

<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 patrón hemos utilizado éste (también el más utilizado en la comunidad Spring

junto a *.htm) como podríamos haber elegido cualquier otro. Bien es cierto que es una buena

práctica darle extensión ya que es muy posible que quisiéramos dejar absolutamente todo en

manos del Servlet de Spring.

DispatcherServlet ahora abrirá el contexto de aplicación web que hayamos definido (o el por

defecto, nombre-servlet.xml). Como ya comentamos en capítulos anteriores y como

seguiremos viendo más adelante, es una práctica muy buena dividir los ficheros de

configuración del contexto de igual modo que las capas lógicas más significativas.

Así tendríamos cuatro ficheros dividiendo las 4 capas más importantes, la capa de seguridad, la

capa de servicio, la capa de persistencia y con esta la capa web. Si dividimos los ficheros de

configuración de contexto de forma coherente con la lógica de la aplicación ganaremos en

reusabilidad, mantenibilidad y facilitaremos la comprensión.

De hecho, cualquiera de estos cuatro iniciales podrían ser reemplazables, es decir, imaginemos

que tenemos un fichero *-data.xml en el que hay una configuración de contexto que hace uso

del motor de persistencia JPA y otro *-data.xml con Hibernate, reemplazar uno por otro sería

facilísimo y no tendría que afectar a las otras capas.

Para asegurarnos que todos los archivos de configuración se abren necesitamos configurar un

cargador de contexto en el archivo web.xml. Éste será ContextLoaderListener cuya

configuración 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.ContextLoaderListen er </ listener-class >

</ listener >

Page 91: Spring Framework-Javier Sevilla

91

Dentro de la etiqueta “context-param” definimos tanto el nombre del parámetro (será

contextConfigLocation) y una lista de valores que hacen referencia a cada uno de los archivos

de configuración de contexto de Spring. Aquí se han definido por servicio, datos y seguridad.

Aún no se han comentado la seguridad pero es conveniente que se vaya tratando este tema ya

que una buena modularización 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 estándar de JEE.

El WebApplicationContext es una extensión del ApplicationContext con algunas características

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 métodos estáticos 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 petición 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 mayoría de los beans se proporcionan parámetros 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 ejecución de una lista de pre-

procesos y post-procesos y controlan si son ejecutados si se cumple una determinada

condición.

• Resolutores de vista o ViewResolvers, los cuales resuelven los nombres de las vistas

con las vistas.

• localeResolver o resolutor de configuración local, el cual resuelve la localización 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.

Usando Spring MVC sin anotaciones

En los últimos años el desarrollo del software se ha ido mudando de las clásicas aplicaciones de

escritorio al patrón cliente servidor y de este a la aplicación web gracias al Modelo Vista

Controlador.

• Para desarrollarlas con Spring básicamente necesitaremos hacer lo siguiente:

Page 92: Spring Framework-Javier Sevilla

• Hacer una clase Controladora que interprete la petición del cliente y sus parámetros,

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 página JSP la cual será la parte de la vista, es decir una plantilla para mostrar

los datos y que, como ya sabemos, tras su interpretación se le enviará un .html al

cliente.

• Configurar el archivo que resuelva la página.

Creando nuestro primer Controlador

Como si de un interfaz se tratase, un controlador actúa como pieza que interconecta la

aplicación con el usuario. Si el lector está habituado al uso de otros marcos de trabajo Modelo

Vista Controlador como Struts verá que es bastante similar al concepto Action con la salvedad

de que al ser un bean definido en el contexto Spring podrá beneficiarse de la inyección de

dependencia así como de la Programación Orientada a Aspectos (AOP).

Por lo general un objeto Controlador delegará la responsabilidad de la acción a un objeto de

servicio designado para tal tarea (Darse de alta, crear, borrar, etc).

Hagamos un ejemplo sencillo de lo que podría ser una página de inicio para una aplicación,

Ésta página lo único que mostrará es la fecha actual.

package youdiversity.web; import java.util.Date; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.portlet.ModelAndView; import org.springframework.web.portlet.mvc.AbstractContro ller; /** * * @author javiersevilla */ public class WelcomeController extends AbstractController { public WelcomeController(){ } protected ModelAndView handleRequestInternal(HttpServletRequ est request, HttpServletResponse response) throws Exception { return new ModelAndView( "home" , "dateMessage" , new Date()); } }

Este controlador es de lo más sencillo que podemos hacer con Spring, pero sirve para

ilustrarnos cómo tras ejecutar el método se devuelve un objeto ModelAndView el cual

Page 93: Spring Framework-Javier Sevilla

93

contendrá el nombre de la vista asociada (“home”) y un objeto (u objetos) que representarán

el modelo.

El objeto ModelAndView

Una vez que tengamos los resultados se han de mandar al navegador. En el controlador

anterior tan sólo se devolvía un objeto de nombre dateMessage que era de tipo Date con la

fecha del día de hoy.

Tal y como hemos visto, al crear un objeto ModelAndView hemos pasado en el primer

parámetro el nombre de la vista asociada. El encargado de interpretar este nombre con el

fichero .jsp correspondiente será el resolutor de visualización. Así la definición 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 vacío, con el parámetro de la vista (ya sea el nombre en String o un objeto View) y

también con la vista y los objetos del modelo.

Configuración del controlador

Para dar de alta el controlador que acabamos de escribir iremos al archivo de configuración de

contexto del DispatcherServlet (como ya dijimos *-servlet.xml siendo * el nombre que le

demos al dispatcherServlet en el fichero web.xml).

En este caso no vamos a inyectar ningún otro objeto, en ejemplos posteriores veremos que es

tan sencillo como se hace con el resto de los bean.

El siguiente código muestra cómo hacerlo:

<bean class="org.youdiversity.web.controllers.WelcomeController" name="/welcome.htm"/>

Algo que puede sorprender es que no lleva propiedad “id” mientras que sí que lleva “name” y

éste es un patrón URL. Esto es tan sencillo como que el atributo “id” no puede llevar

caracteres especiales, como la barra inclinada.

Cuando llegue una petición que cumpla dicho patrón le pasará la llamada.

View Resolvers

Las Java Servlet Pages (JSP) se han impuesto como la tecnología 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.

Page 94: Spring Framework-Javier Sevilla

Para ayudar a Spring a buscar qué JSP utilizar para cada visualización se ha de incluir un nuevo

bean de tipo ViewResolver. El más sencillo es InternalResourceViewResolver cuya definición es

la siguiente.

<bean class ="org.springframework.web.servlet.view.InternalResou rceViewResolver" p:prefix ="/WEB-INF/jsp/" p:suffix =".jsp" />

Spring tiene varios ViewResolvers pero para las visualizaciones mostradas por JSP

InternalResourceViewResolver es el más sencillo. Su funcionamiento es tan simple como

añadir 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 deberíamos crear el fichero “welcome.jsp”, el siguiente código ilustra su

contenido:

<%@ page language ="java" import ="java.util.*" pageEncoding ="ISO-8859-1" %> <! DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html > <head > <title >WELCOME</ title > </ head > <body > ${dateMessage} <br > </ body > </ html >

Pasos de la petición

Ahora lo tenemos todo, el controlador, la página jsp, lo hemos registrado en el contexto de

DispatcherServlet y tenemos un ViewResolver.

Pero, ¿Qué ocurre cuando se recibe una petición?, los acontecimientos son los siguientes:

1. El servidor le pasa al DispatcherServlet la petición (para ello lo configuramos y le dimos

un patrón en el web.xml).

2. DispatcherServlet buscará qué controlador se encarga de la URL.

Page 95: Spring Framework-Javier Sevilla

95

3. Nuestro controlador devuelve un ModelAndView con un nombre lógico 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 lógico (en nuestro

caso welcome) y devolverá la ruta completa (WEB-INF/jsp/welcome.jsp).

5. DispatcherServlet reenviará la petición al jsp.

Con esto hemos completado la manera más básica 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 versión 3 nos aporta.

Usando Spring MVC con Anotaciones y Con Spring 3

Los Controladores facilitan el acceso al comportamiento de la aplicación que suelen definir a

través de un interfaz de servicio. Los controladores interpretan la entrada del usuario y

devuelven un modelo representado en una vista. Spring implementa controladores de una

manera muy abstracta que permite crear una gran variedad de controladores.

A partir de la versión 2.5, Spring introdujo una serie de anotaciones para los controladores

MVC como @Controller, @RequestMapping, @ModelAttribute etc. Estas anotaciones están

disponibles tanto para Servlet MVC o Portlet MVC. Los controladores que son implementados

de este modo no han de extender de clases específicas como AbstractController o

implementar interfaces como Controller. Tampoco tienen dependencias con las API de Servlet

o Portlet aunque se pueden configurar fácilmente el acceso a ellas.

package org.youdiversity.web.controllers; import org.springframework.stereotype.Controller;

Page 96: Spring Framework-Javier Sevilla

import org.springframework.web.bind.annotation.RequestMap ping; 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 métodos y firmas. En este ejemplo no hay parámetros y devuelve un

ModelAndView. Existen múltiples estrategias como se explicarán más adelante en este

capítulo. Con ModelAndView, @Controller y @RequestMapping tenemos los pilares básicos

para construir con Spring MVC.

Definir el controlador en el fichero *-servlet.xml.

La anotación @Controller indica que una clase en particular sirve como un controlador. Spring

como ya hemos dicho, no necesita que extiendas de ninguna implementación de Controller.

El dispatcher escanea en búsqueda de clases que tengan métodos mapeados y detecta

anotaciones @RequestMapping y @Controller.

Se pueden definir estos controladores directamente en el contexto del dispatcher, sin

embargo la anotación @Controller permite que se autodetecte. Para ello tendremos que

incluir el siguiente tag en fichero de configuración de contexto:

<context:component-scan base-package ="org.youdiversity.web.controllers" />

Mapeando peticiones con @RequestMapping

La anotación @RequestMapping se usa para mapear URL ya sea en una clase entera o en un

método en particular. Normalmente si mapeamos a nivel de clase se usarán un método u otro

dependiendo del método de petición (GET o POST) o los parámetros de la petición.

El siguiente código muestra las cabeceras de dos métodos el los cuáles uno será llamado

cuando se hagan peticiones POST y la segunda cuando sean GET:

@RequestMapping (method = RequestMethod. POST) public String processSubmit( @ModelAttribute AdminDataPicker …

@RequestMapping (method = RequestMethod. GET)

Page 97: Spring Framework-Javier Sevilla

97

public String setupForm(Model model) {

En el ejemplo el @RequestMapping es usado para cada método. Lo que hará que se

diferencien será tanto los parámetros de la petición, si hay sub-URL o si la petición es GET o

POST.

El @RequestMapping a nivel de clase no es necesario, si no lo hacemos cada uno de los

métodos 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.RequestMap ping; 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; } }

Peticiones URL, usando @RequestMapping

Para acceder a partes de la petición 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 recibiésemos una petición /user/12 el número 12 sería una variable de path

que en este caso nos servirá como en id del usuario.

Sólo si se ha compilado en modo debug no se necesitará la anotación @PathVariable para

enlazar con el valor de la variable.

También se pueden usar varias variables

@RequestMapping ( "/user/{userId}/{subjectId" ) public ModelAndView userHandler ( @PathVariable ( "userId" ) Long userId,

@PathVariable ( "subjectId" ) Long subjectId) {

Page 98: Spring Framework-Javier Sevilla

...

return mav; }

O incluso definir una a nivel global y otras a nivel particular

@Controller @RequestMapping ( "/user/{userId}" ) public class UserProfileController { private UserDao userDao ; @Autowired public UserProfileController(UserDao userDao) { this. userDao = userDao; } @RequestMapping ( "/subject/{subjectId}" ) public ModelAndView userHandler ( @PathVariable ( "userId" ) Long userId,

@PathVariable ( "subjectId" ) Long subjectId) { ...

return mav; } }

Mapeo avanzado con @RequestMapping

Además de las URL la anotación @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 explícitas y tras

descartar cada una de ellas las rutas que tengan una expresión como la citada serán las que

queden por defecto si no ha habido una coincidencia anterior.

Si se tienen un único método por defecto (sin un mapeo específico), entonces todas las

peticiones sin métodos con mapeos más específicos serán servidas a él.

Se pueden especificar que el método está esperando determinados parámetros que han de

estar o no estar, es decir, @RequestMapping (value = "/subject/{subjectd}", params =

"myParam=myValue"). Así este método sólo será mapeado si además de cumplir el

path de la URL también tenga el parámetro “myParam” y con el valor “myValue”. Para

indicar lo contrario sería indicar “!myParam”, así ese método nunca será seleccionado

si encuentra el parámetro “myParam”.

De igual forma, los mapeos pueden ser configurados teniendo en cuenta también 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; }

Page 99: Spring Framework-Javier Sevilla

99

El método anterior sólo es ejecutado cuando el contenido es text/* como por ejemplo

text/html.

Argumentos de método soportados

Los métodos 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 sesión del API de Servlet fuerza la presencia de una sesión. 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 genérico

a un parámetro de la petición así como el acceso a los atributos Request/Session sin que haya

vínculos al API nativo Servlet/Portlet.

java.util.Locale es determinado según el mayor resolutor de locale que esté disponible, de

hecho, el LocaleResolver es un Servlet.

Se puede acceder a la petición con java.io.InputStream o con java.io.Reader de igual manera

que con el API Servlet.

También se puede generar una respuesta con java.io.OutputStream así como con

java.io.Writer como usásemos el API de Servlet.

El objeto java.security.Principal contiene el usuario autenticado.

Los parámetros de acceso de la anotación @PathVariable obtienen el acceso a las variables de

la URI.

Con la anotación @RequestParam se hace referencia a los parámetros de la petición, es decir a

los parámetros específicos de la petición de la Request. Los parámetros son convertidos en el

tipo declarado en el método. Más tarde haremos referencia a como enlazar (o hacer binding)

estos valores.

La anotación @RequestHeader se utiliza para acceder a los parámetros de la cabecera de la

petición. Los valores de los parámetros serán convertidos en los tipos declarados.

La anotación @RequestBody se utiliza para anotar parámetros que se hallen en el cuerpo de la

petición. Los valores serán convertidos en los tipos declarados usando

HttpMessageConverteres. Más tarde veremos cómo hacerlo.

Page 100: Spring Framework-Javier Sevilla

Con el parámetro HttpEntity<?> podremos acceder a las cabeceras HTTP y su contenido de la

petición. El flujo de la petición será convertido en la entidad body usando

HttpMessageConverteres. También lo veremos más tarde.

Podemos usar java.util.Map, org.springframework.ui.Model o

org.springframework.ui.ModelMap para guardar los objetos de modelo.

Los objetos Command o Form los podemos enlazar con parámetros como propiedades de los

beans, campos, conversiones con algún tipo personalizado, dependiendo de los métodos de

@InitBinder y de la configuración 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

sesión que se han quedado indicados por la anotación @SessionAttributes.

Los parámetros 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 método no podremos poner un único BindingResult, sino

que tendremos que poner uno a continuación de cada uno de los objetos enlazados.

Tipos devueltos soportados

El objeto ModelAndView en el que se haya el modelo con los objetos y los resultados de la

referencia a los datos de la anotación @ModelAttribute.

El objeto Model, en este caso el nombre de la vista será determinado implícitamente a través

RequestToViewNameTranslator y el objeto Model, de igual forma a ModelAndView, contendrá

los objetos y resultados.

Un mapa (implementación 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á implícitamente determinado por los

objetos del comando y los objetos con anotaciones @ModelAttribute. De igual forma, los

objetos de comando estarán implícitos como modelo.

Una cadena que representa la vista lógica. De igual modo, tendrá implícito el modelo con los

objetos de comando y los objetos anotados con @ModelAttribute.

Nada, es decir un método con firma void. En este caso el método manejará la petición por sí

mismo, es decir, escribiendo la respuesta directamente con contenido, declarando un

Page 101: Spring Framework-Javier Sevilla

101

argumento de tipo ServletResponse o HttpResponse o si el nombre de la vista se ha de deducir

a través del objeto RequestToViewNameTranslator.

Si el método 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 método 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 añadirán los objetos command así como los anotados con

@ModelAttribute y la vista será determinada.

Enlazando parámetros de la petición a parámetros de métodos con

@RequestParam

Se usa @RequestParam para enlazar los parámetros de la petición con los parámetros de los

métodos del controlador.

El siguiente código muestra un ejemplo:

@Controller @SessionAttributes (types = AdminDataPicker. class) @RequestMapping ( "/public/createAdmin" ) public class CreateAdminController { @Autowired private AdminDao adminDao ; @RequestMapping (method = RequestMethod. GET) public String setupForm(Model model) { AdminDataPicker adminDataPicker = new AdminDataPicker(); model.addAttribute( "adminDataPicker" , adminDataPicker); this.refreshData(model); return "public/createAdmin" ; } private void refreshData(Model model) { } @RequestMapping (method = RequestMethod. POST) public String processSubmit(

@ModelAttribute AdminDataPicker adminDataPicker, BindingResult res ult, SessionStatus status, Model model) {

new AdminValidator().validate(adminDataPicker, result, this. adminDao ); if (result.hasErrors()) { this.refreshData(model); return "public/createAdmin" ; } else { Admin user = this. adminDao .saveFromDataPicker(adminDataPicker); status.setComplete(); return "redirect:/userProfile/" + user.getId(); } } }

Page 102: Spring Framework-Javier Sevilla

Los parámetros que usan esta anotación se requieren por defecto, si queremos que sean

opcionales tendremos que añadir el atributo “required” con un valor “false”.

@RequestBody

La anotación @RequestBody cuando se usa sobre un parámetro del método indica a Spring

que ese parámetro del método ha de ser enlazado con el valor de la petición HTTP. Por

ejemplo:

@RequestMapping(value = "/something", method = Requ estMethod.PUT) public void handle(@RequestBody String body, Writer writer) throws IOException { writer.write(body); }

Se puede convertir el cuerpo de la petición usando HttpMessageConverter. Este objeto

también convierte el mensaje de solicitud a un objeto de respuesta. DispatcherServlet soporta

el procesamiento de la anotación.

@ResponseBody

La anotación @ResponseBody es la análoga de la respuesta de @RequestBody. En este caso

puesto sobre un método indica a Spring que el valor de retorno del método 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, answersIdLo ngs); return questionAnswered.getCorrect() ? "Pregunta contestada correctamente" : "Pregunta no contestada correctamente" ; }

Spring convertirá la cadena en una respuesta HTTP usando el conversor HttpMessageConverter.

HttpEntity

Page 103: Spring Framework-Javier Sevilla

103

Similar a @RequestBody o @ResponseBody, el HttpEntity tiene acceso al cuerpo de la petición

o de la respuesta y permite acceder a las cabeceras de las peticiones o de las respuestas.

El siguiente método es un ejemplo:

@RequestMapping("/something") public ResponseEntity<String> handle(HttpEntity<byt e[]> requestEntity) throws UnsupportedEncodingException { String requestHeader = requestEntity.getHeaders().getFirst("MyRequestHeade r")); byte[] requestBody = requestEntity.getBody(); HttpHeaders responseHeaders = new HttpHeaders(); responseHeaders.set("MyResponseHeader", "MyValue" ); return new ResponseEntity<String>("Hello World", responseHeaders, HttpStatus.CREATED);

Proporcionando un enlace a los datos del modelo con @ModelAttribute

Se usa @ModelAttribute cuando lo pones en un parámetro del método indicando que

pertenece a un atributo del modelo o cuando lo pones sobre un método indicando que el valor

devuelto pertenece a un objeto de modelo (comúnmente para iniciarlo).

Para iniciarlo:

@ModelAttribute ( "types" ) public Collection<RoleType> populateRoleTypes() { return RoleType.values(); }

O como hemos dicho, para enlazarlo con un parámetro del método:

@RequestMapping (method = RequestMethod. POST) public String processSubmit( @ModelAttribute AdminDataPicker adminDataPicker, BindingResult result, SessionStatus status, Model model) { new AdminValidator().validate( adminDataPicker, result, this. adminDao ); if (result.hasErrors()) { this.refreshData(model); return "public/createAdmin" ; } else { Admin user = this. adminDao .saveFromDataPicker(adminDataPicker); status.setComplete(); return "redirect:/userProfile/" + user.getId(); }

}

Especificando atributos para almacenar en la sesión

Page 104: Spring Framework-Javier Sevilla

La anotación @SessionAttributes indica que atributos de sesión son usados por un manejador

en particular. Esto lista una serie de nombres de atributos del modelo que se almacenaran

como atributos de sesión.

@Controller @SessionAttributes (types = TeacherDataPicker. class) @RequestMapping ( "/admin/createTeacher" ) public class CreateTeacherController {

Uso de cookies con @CookieValue

La anotación @CookieValue permite enlazar una cookie HTTP con un método. El siguiente

código muestra cómo hacerlo:

@RequestMapping("/displayHeaderInfo.do") public void displayHeaderInfo(@CookieValue("JSESSIO NID") String cookie) { //... }

Mapeando cabeceras de peticiones con @RequestHeader

La anotación @RequestHeader permite enlazar los parámetros de los métodos con la cabecera

de la petición.

Dada la siguiente cabecera:

Host localhost:8080 Accept text/html,application/xhtml+xml,application/xml;q=0 .9 Accept-Language fr,en-gb;q=0.7,en;q=0.3 Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q= 0.7 Keep-Alive 300

Un ejemplo sería el siguiente:

@RequestMapping("/displayHeaderInfo.do") public void displayHeaderInfo(@RequestHeader("Accep t-Encoding") String encoding, @RequestHeader("Keep- Alive") long keepAlive) { //... }

Page 105: Spring Framework-Javier Sevilla

105

Personalizando el enlace a Datos

Se pueden usar personalizaciones de enlace tanto con una clase WebBindingInitializer como

con métodos exclusivos para cada controlador gracias a la anotación @InitBinder.

El siguiente método es un ejemplo

@InitBinder public void initBinder(HttpServletRequest request, ServletRequ estDataBinder binder) throws Exception { binder.registerCustomEditor( Carrer. class, new CarrerCustomEditor( this. carrerDao )); binder.registerCustomEditor( Subject. class, new SubjectCustomEditor( this. subjectDao )); SimpleDateFormat dateFormat = new SimpleDateFormat( "dd/MM/yyyy" ); dateFormat.setLenient( false);

binder.registerCustomEditor( Date. class, new CustomDateEditor(dateFormat, false)); }

Para externalizar y normalizar el enlace de datos a nivel de aplicación implementaremos una

clase de WebBindingInitializer.

<bean class ="org.springframework.web.servlet.mvc.annotation.Ann otationMethodHandlerAdapter" > <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 aplicación web. Ahora el DispatcherServlet busca

anotaciones @RequestMapping en los controladores. Hay diversas propiedades que están

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.annotati on.DefaultAnnotationHandlerMapping"> <property name="interceptors"> <bean class="example.MyInterceptor"/> </property> </bean> <beans>

Page 106: Spring Framework-Javier Sevilla

Interceptando peticiones con HandlerInterceptor

El mecanismo de mapeo incluye interceptores que son útiles cuando se desea aplicar una

cierta funcionalidad a determinadas peticiones, por ejemplo, chequeando la principal.

Los interceptores localizados en el manejador del mapeo deben implementar

HandlerInterceptor . Este interfaz define tres métodos, preHandle , postHandle y

afterCompletion. El primero es llamado antes de que se ejecute el manejador actual, el

segundo después y el último después de que la petición se haya terminado. Estos métodos

otorgan completa flexibilidad para procesar cualquier tipo de preproceso y postproceso.

El método preHandle devuelve un valor booleano, dependiendo del valor devuelto se

continuará o cancelará la ejecución del controlador. Si el valor devuelto es falso el

DispatcherServlet asumirá que el propio interceptor ha redireccionado y tomado control de la

petición.

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

tecnología de vista específica. Spring permite usar vistas JSP, Velocity o XSLT.

ViewResolver y View son los interfaces que Spring utiliza.

ViewResolver

Como hemos comentado, todos los métodos en los controladores de Spring MVC deben

resolver con el nombre de una vista lógica, de manera implícita o explícita. Las vistas de Spring

son mapeadas con una vista lógica resuelta por el resolutor de vista. Spring proporciona los

siguientes resolutores:

• AbstractCachingViewResolver

• XmlViewResolver

• ResourceBundleViewResolver

• UrlBasedViewResolver

• InternalResourceViewResolver

• VelocityViewResolver / FreeMarkerViewResolver

• ContentNegotiatingViewResolver

Con UrlBasedViewResolver las traducciones de resolución de vista se hacen desde una URL

<bean id ="viewResolver" class ="org.springframework.web.servlet.view.UrlBasedViewR esolver" > <property name="viewClass" value ="org.springframework.web.servlet.view.JstlView" /> <property name="prefix" value ="/WEB-INF/jsp/" /> <property name="suffix" value =".jsp" /> </ bean >

Page 107: Spring Framework-Javier Sevilla

107

Cuando se devuelve ejemplo como vista lógica se traducirá el fichero físico /WEB-

INF/jsp/ejemplo.jsp.

Cuando se combinan varias tecnologías de vista se pueden usar

ResourceBundleViewResolver del siguiente modo:

<bean id ="viewResolver" class ="org.springframework.web.servlet.view.ResourceBundl eViewResolver" > <property name="basename" value ="views" /> <property name="defaultParentView" value ="parentView" /> </ bean >

Encadenando ViewResolvers

Spring permite tener múltiples resolutores de vista encadenados. Se puede especificar el

orden con el atributo order, a mayor valor de este más tarde se posicionará.

Redireccionando a vistas

Como hemos dicho un controlador devuelve un nombre de vista lógica. Para tecnologías de

vista como JSP que se procesan a través de motores JSP o Servlet, esta resolución está

típicamente manejada a través de la combinación de InternalResourceViewResolver e

InternalResourceView los cuales redireccionan o incluyen a través del API de Servlet con

los métodos RequestDispatcher.forward() o RequestDispatcher.include().

En ocasiones es deseable una tarea de redirección 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 delegación de otro controlador que también ve los mismos

datos enviados con POST. Esto es potencialmente problemático si puede confundir con otros

datos esperados también enviados por POST. Otra razón por la que se redirige antes de enviar

a la vista es para eliminar la posibilidad de que el usuario haga un submit múltiple, es decir, un

envío de un formulario varias veces. En este escenario, el navegador enviará un primer POST

con datos entonces el recibirá una respuesta con una redirección a una URL distinta,

finalmente el navegador ejecutará una redirección GET a la URL contenida en la respuesta de

redirección. Así, desde la perspectiva del navegador, la página 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 múltiples ocasiones. El refresco fuerza un GET

del resultado de la página, no un re-envío de los datos por POST.

RedirectView

Una manera de forzar una redirección 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 resolución de vista. En cambio, como ya se ha dado la

redirección a la vista el DispatcherServlet simplemente deja que la vista haga su trabajo.

La tarea de RedirectView es una llamada al método de redirección

HttpServletResponse.sendRedirect(), el cual devuelve al navegador una redirección HTTP.

Todos los atributos del modelo serán parámetros de consulta HTTP, esto significa que el

Page 108: Spring Framework-Javier Sevilla

modelo debe contener solo objetos (Cadenas o valores convertibles a cadenas), los cuales han

de estar preparados para ser convertidos en parámetros 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 redirección.

Esto no es del todo óptimo, el controlador no debería preocuparse de que la respuesta sea

controlada. En general sólo se debería operar en términos 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

redirección. El resto del nombre de la vista será tratado como la redirección URL.

El efecto final es como si el controlador devolviese una redirección RedirectView, pero ahora el

mismo controlador puede operar en términos de nombre de vista lógica. Lo importante de

esto es que el mismo controlador no sabe que la redirección está pasando.

El prefijo “forward:”

También existe la posibilidad de usar un prefijo especial forward: para los nombres de vistas

que serán 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 tecnología 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 términos 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 representación de la petición del

cliente. Dos estrategias son llevadas a cabo:

Usar una URI distinta para cada recurso, normalmente usando una extensión distinta. Por

ejemplo la URI, http://ejemplo.org/usuarios.pdf y http://ejemplo.org/usuarios.xml, uno

pediría una representación PDF de los usuarios y la otra una representación XML.

Para permitir múltiples representaciones de un recurso Spring dispone del

ContentNegotiantingViewResolver el cual resuelve una vista basada en la extensión del fichero

o de la cabecera de la petición HTTP. ContentNegotiantingViewResolver no interpreta la

Page 109: Spring Framework-Javier Sevilla

109

resolución de la vista sino que delega a una lista de resolutores de vista que especificarán a

través de la propiedad ViewResolver.

La clase ContentNegotiatingViewResolver selecciona un resolutor de vista comparando el tipo

de la petición (content-type) con los distintos ViewResolvers. El primer ViewResolver que sea

compatible con el content-type devolverá la representación al cliente. Si no hubiese una

coincidencia de contenido por los distintos ViewResolvers se consultará la propiedad

DefaultViews. Esta última opción es apropiada para vistas singleton que hará una

representación adecuada de los recursos sin importar el nombre de vista lógico. Se pueden

incluir caracteres comodín, como por ejemplo text/* en el que el content-type podrá ser

text/html, text/xml etc.

Para ayudar a la resolución 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.ContentNegoti atingViewResolver" > <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.BeanNameViewR esolver" /> <bean class ="org.springframework.web.servlet.view.InternalResou rceViewResolver" > <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.MappingJ acksonJsonView"/>

</ list > </ property > </ bean > <bean id ="content"

class ="com.springsource.samples.rest.SampleContentAtomVie w" />

El InternalRessourceViewResolver maneja la traducción de la visa en páginas 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 configuración si una configuración es creada con la extensión .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 petición es

Page 110: Spring Framework-Javier Sevilla

creada con una extensión .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 petición es creada por una extensión .json la instancia del

bean MappingJacksonJsonViewserá elegido ya que es la opción por defecto. Por otro lado, las

solicitudes se pueden hacer sin extensión del archivo pero con la cabecera Accept definida con

un media-type, en tal caso se hará la misma resolución.

Configuración regional

La mayor parte de la arquitectura de Spring soporta internacionalización, incluida la parte

Spring MVC. DispatcherServlet permite resolver mensajes de manera automática usando el

locale del cliente gracias a los objetos LocaleResolver.

Cuando llega una petición el DispatcherServlet busca un resolutor local, si se encuentra uno se

intentará usar para asignar una configuración regional. Mediante el método

RequestContext.getLocale() se pueden obtener el Locale que fue resuelto por el

LocaleResolver.

A parte de la configuración automática de resolución de Locale, también 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 parámetros.

Los resolutores de configuración regional y los interceptores están definidos en el paquete

org.springframework.web.wervlet.i18n.

AcceptHeaderLocaleResolver

Este resolutor busca en el parámetro de la cabecera de la petición accept-language que fue

enviada por el cliente. Normalmente este parámetro 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

específico. Si lo hay, se usará ese locale específico. Se puede definir el nombre de la cookie, su

vida así como aplicarle un path el cual será visible para una parte específica de la aplicación (/

para toda la aplicación).

SessionLocaleResolver

Page 111: Spring Framework-Javier Sevilla

111

El SessionLocaleResolver permite recibir Locales desde la sesión que quizá fue asociada con

una petición del usuario.

LocaleChangeInterceptor

Se puede activar la opción de poder cambiar los Locale añadiendo LocaleChangeInterceptor a

uno de los manejadores de mapeo. Eso hará que detecte un parámetro en la petición y cambie

el Locale. Se llamará a setLocale del objeto LocaleResolver que está en el contexto. El siguiente

ejemplo muestra cómo las llamadas a los recursos *.view contienen un parámetro que se

llama siteLanguage que cambiará el Locale.

<bean id ="localeChangeInterceptor" class ="org.springframework.web.servlet.i18n.LocaleChangeI nterceptor" > <property name="paramName" value ="siteLanguage" /> </ bean > <bean id ="localeResolver" class ="org.springframework.web.servlet.i18n.CookieLocaleR esolver" /> <bean id ="urlMapping" class ="org.springframework.web.servlet.handler.SimpleUrlH andlerMapping" > <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 aplicación. La resolución del tema es muy similar a la resolución del Locale. Un tema es

una colección de recursos estáticos, hojas de estilo e imágenes que afectan al aspecto visual de

la aplicación.

Definiendo los temas

Para usar temas en la aplicación deberemos configurar una implementación del interfaz

ThemeSource. El contexto de aplicación web implementa ThemeSource, pero delega sus

responsabilidades a una implementación dedicada. Por defecto, esta será

ResourceBundleThemeSource que leerá los ficheros de propiedades de la raíz del classpath. Se

podrán usar implementaciones personalizadas de ThemeSource. Cuando se quiere configurar

ResourceBundleThemeSource o se quiere definir otro específico se ha de hacer en el contexto

de la aplicación. Si queremos usar ResourceBundleThemeSource habrá que definir un par de

propiedades en un fichero.

Luego utilizaremos el tag <spring:theme en los ficheros JSP que es muy similar al tag

<spring:message.

...

Page 112: Spring Framework-Javier Sevilla

<link rel="stylesheet" href="<spring:theme code='st yleSheet'/>" type="text/css"/> ... <body style="background=<spring:theme code='backgro und'/>"> ...

Por defecto ResourceBundleThemeSource usa un nombre base vacío como prefijo. Así, el

fichero de properties es cargado desde la raíz del classpath. ResourceBundleThemeSource

utiliza el mecanismo de carga del paquete estándar de Java para leer los recursos, siendo así

también internacionalizable.

Resolutores de tema

Después de definir los temas hay que elegir cuál de ellos usar. El DispatcherServlet buscará un

bean llamado themeResolver para saber que implementación de ThemeResolver se usará.

ThemeResolver funciona de manera muy similar a LocaleResolver, se detecta que tema se

usará en una petición en particular y también 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 sesión HTTP del usuario el cuál

permanecerá toda la sesión, pero no estará persistida entre sesiones.

• CookieThemeResolver, El tema se guarda en una cookie en el navegador del cliente.

Spring también tiene la clase ThemeChangeInterceptor que permite los cambios de tema en

cada una de las peticiones con un único parámetro.

Subida de ficheros

Spring permite la subida de ficheros multipart. Se puede activar esta característica con el

objeto MultipartResolver. Spring proporciona un resolutor multiparte para usar Commons

FileUpload de Apache.

El siguiente ejemplo muestra cómo usar CommonsMultipartResolver:

<bean id ="multipartResolver" class ="org.springframework.web.multipart.commons.CommonsM ultipartResolver" > <property name="maxUploadSize" value ="100000" /> </ bean >

Por supuesto también 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 petición multi-part activa el resolutor que

haya sido declarado en el contexto. El resolutor entonces envuelve la petición HTTP actual en

una MultipartHttpServletRequest que permite la subida por partes de ficheros.

Page 113: Spring Framework-Javier Sevilla

113

Subiendo un fichero desde un formulario

Después de que el resolutor complete su trabajo, la petición se procesa como si hubiese sido

cualquier otra. Primero hay que crear un formulario con un input de tipo file. Habrá que definir

el atributo enctype con el valor “multipart/form-data”, eso permitirá al navegador saber cómo

ha de enviar el fichero. El siguiente ejemplo muestra cómo hacerlo:

<html> <head> <title>Upload a file please</title> </head> <body> <h1>Please upload a file</h1> <form method="post" action="/form" enctype= "multipart/form-data"> <input type="text" name="name"/> <input type="file" name="file"/> <input type="submit"/> </form> </body> </html>

El siguiente paso será crear un controlador que maneje la petición de la subida del fichero.

Este controlador es muy similar a cualquier otro, con la salvedad que usa

MultipartHttpServletRequest o MultipartFile como parámetros de método. El siguiente código

muestra cómo hacerlo:

@Controller public class FileUpoadController { @RequestMapping(value = "/form", method = Reque stMethod.POST) public String handleFormUpload(@RequestParam("n ame") 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 cómo los atributos anotados con @RequestParam son los elementos input del

fichero.

Finalmente, se tendrán que declarar el controlador y su resolutor en el contexto de la

aplicación.

Page 114: Spring Framework-Javier Sevilla

<beans> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.Co mmonsMultipartResolver"/> <bean id="fileUploadController" class="examples.FileUploadController"/> </beans>

Manejo de Excepciones

La clase de Spring HandlerExceptionResolver se encarga de la excepción de excepciones que

puedan ocurrir mientras se hace se ejecuta la petición a un controlador.

HandlerExceptionResolver tiene un comportamiento similar al mapeo que se puede hacer en

el descriptor xml de la aplicación web. Sin embargo, proporciona mayor flexibilidad para

manejar las excepciones. Proporciona información sobre qué manejador se estaba ejecutando

mientras se lanzó la excepción. Por otra parte, una manera programática de manejar

excepciones te da más opciones de responder apropiadamente antes de que la petición sea

pasada a otra URL.

Aunque se pueda implementar la interfaz HandlerExceptionResolver, que se implementa

únicamente con el método resolverException(Exception, Handler) y devolviendo un

ModelAndView. También se puede usar el SimpleMappingExceptionResolver el cual te

permite resolver el nombre de la clase de la excepción 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 también se puede implementar de una manera más ajustada mapeos de

excepciones de diferentes controladores.

Por defecto, el DispatcherServlet registra DefaultHandlerExceptionResolver, este resolutor

maneja una serie de excepciones estándar de Spring asignándolas un específico código de

respuesta. La siguiente tabla muestra esta equivalencia:

Excepción Código de estado HTTP ConversionNotSupportedException 500 (Error interno del servidor) HttpMediaTypeNotAcceptableException 406 (No aceptable) HttpMediaTypeNotSupportedException 415 (Tipo de medio no soportado) HttpMessageNotReadableException 400 (Mala petición) HttpMessageNotWritableException 500 (Error interno de servidor) HttpRequestMethodNotSupportedException 405 (Método no soportado) MissingServletRequestParameterException 400 (Mala petición) NoSuchRequestHandlingMethodException 404 (No encontrado) TypeMismatchException 400 (Mala petición)

Page 115: Spring Framework-Javier Sevilla

115

@ExceptionHandler

Otra alternativa al interfaz HandlerExceptionResolver es la anotación @ExceptionHandler. Se

usa @ExceptionHandler como una anotación de método, para especificar qué método será

invocado cuando una excepción en particular sea lanzada mientras la ejecución de algún

método del controlador.

@Controller public class SimpleController { @ExceptionHandler(IOException.class) public String handleIOException(IOException ex, H ttpServletRequest request) { return ClassUtils.getShortName(ex.getClass()); } }

De este modo se invocará este método cuando una excepción sea lanzada.

Page 116: Spring Framework-Javier Sevilla

Convenios

La mejor manera de mantener y crear un proyecto es seguir determinadas pautas y convenios

que harán más sostenible la configuración necesaria, el mapeo de controladores, la resolución

de vistas, las instancias ModelAndView y demás. Esto hará más consistente y mantenible el

código.

Page 117: Spring Framework-Javier Sevilla

117

Page 118: Spring Framework-Javier Sevilla

Capítulo 6:

Spring Security

Cómo hacer de nuestra aplicación un lugar

seguro

Este capítulo se centrará en la seguridad y cómo Spring a través de su proyecto Spring Security se hace cargo de ella.

Javier Sevilla Sánchez

Page 119: Spring Framework-Javier Sevilla

119

Page 120: Spring Framework-Javier Sevilla

Contenido ¿Qué es Spring Security? ........................................................................................................... 121

Autenticación en Spring Security .............................................................................................. 121

AuthenticationManager ............................................................................................................ 122

Seguridad en aplicaciones Web ................................................................................................ 123

Autorización en Spring Security ................................................................................................ 127

Seguridad de la capa de servicio ............................................................................................... 129

Configuración de seguridad a nivel global (namespace)....................................................... 129

Seguridad a nivel de bean ..................................................................................................... 129

Con el objeto MethodSecurityInterceptor ............................................................................ 129

Seguridad a nivel de anotación ............................................................................................. 130

Page 121: Spring Framework-Javier Sevilla

121

¿Qué es Spring Security?

Spring Security se ha convertido en la librería de referencia dentro del mundo Java, para dar

soporte a los servicios de autenticación y autorización de una aplicación. Esto se debe

principalmente a que es un proyecto proyecto enmarcado dentro de Spring y como ya hemos

visto gracias a Spring podremos hacer código mantenible, reutilizable, ágil y robusto. El código

y diseño 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 mayoría del framework de Spring,

Spring Security ofrece una gran facilidad de configuración y parametrización, gracias al uso de

mamespace y la inyección de dependencia. Permite la integración con los sistemas legacy de

autenticación más importantes del mercado: BBDD, LDAP, CAS (para single sign-on), gestión de

certificados, etc. Spring Security tiene un gran respaldo de la comunidad con lo que hay una

gran cantidad de documentación y ejemplos.

Autenticación en Spring Security

Un proceso de autenticación consiste en identificar la entidad que realiza una acción sobre la

aplicación y garantizar que es quién dice ser. La entidad que realiza una acción sobre la

aplicación es conocida como principal, en Spring es llamada AuthenticationManager,

encargado de implementar el proceso de autenticación en las aplicaciones.

Antes de describir cómo es el diseño de AutenticationManager, vamos a presentar otros

componentes importantes y necesarios para poder comprender el proceso de autenticación.

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 autenticación.

SecurityContextHolder, encargado de guardar los datos del principal y sus roles dentro del

contexto de seguridad. Como hemos dicho, la información del principal la alberga

Authentication, así que SecurityContextHolder guarda el objeto Authentication en el contexto

de seguridad. Así la información que reside en el objeto Authentication pueda ser consultada

en todo momento por cualquier componente dentro de la aplicación.

Objeto UserDetailsService, Spring suele delegar responsabilidades en otros objetos, en este

caso el objeto AuthenticationManger delega en el objeto UserDetails las labores de obtención

de la información del usuario sobre el repositorio de seguridad. Así, si los usuarios están en

una base de datos, el objeto UserDetails realizará las consultas necesarias para obtener la

información del AuthenticationManager.

Page 122: Spring Framework-Javier Sevilla

AuthenticationManager

Este interfaz sólo define un método:

Authentication authenticate (Authentication authenticaction) throws AuthenticationException;

El parámetro de entrada tendrá los datos básicos de autenticación. Con este dato se prodecerá

a obtener toda la información 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 autenticación lanzará una AuthenticationException. Si la autenticación es correcta

se obtendrán las autoridades asociadas al usuario y serán guardadas en el objeto

Authentication, el cual será devuelto por el método. É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

autenticación. Los objetos ProviderManer están más cercanos a la tecnología usada para

impementar el repositorio de seguridad de la aplicación. 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 colaboración entre componentes para implementar el

proceso de autenticación de la aplicación, en este caso se muestra para un delegado que

buscará en una base de datos gracias al DaoAuthenticationProvider.

Page 123: Spring Framework-Javier Sevilla

123

Spring tiene un gran número 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 inclusión de namespace específicos

que da sopote a la configuración de los distintos componentes que forman parte de la solución

de autenticación y autorización de aplicaciones. Antes, era necesario definir un conjunto de

beans muy extenso y existía la posibilidad de error en la gestión de las dependencias de los

distintos beans. Gracias a la incorporación de namespace, al desarrollar se abstrae de muchas

particularidades internas de la solución, proporcionando un fichero XML de configuración más

sencillo.

Como vemos en el ejemplo el único parámetro de configuración necesario es el gestor de

autenticación donde está el esquema de base de datos donde reside el repositorio de

seguridad.

Este fichero permitirá la configuración de todos los elementos necesarios para realizar la

autenticación y autorización de una aplicación web sobre HTTP.

Seguridad en aplicaciones Web

Page 124: Spring Framework-Javier Sevilla

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 configuración.

Dentro del fichero de configuración incluiremos el siguiente código:

Dentro de la configuración se puede ver como se pueden securizar URL. La etiqueta contiene el

patrón 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 configuración 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 autenticación. Usa un soporte de cookies

con aplicación de algoritmos hash para guardar la información.

Selección 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 aplicación. Spring Security

hace las oportunas redirecciones que sean necesarias para el cambio de canal.

Gestión de la sesión, control de timeouts de sesión, y control de la sesión concurrente. El

control de los timeouts de sesión sirve para indicar cuando esta invalidada. El control de sesión

concurrente es para controlar el número de sesiones que están activas para un mismo usuario.

Esto supone una protección contra el ataque de fijación de sesión.

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 autenticación y autorización entre cada petición y

respuesta. Es posible modificar la cadena de filtros.

Dentro de estos filtros destacan:

Page 125: Spring Framework-Javier Sevilla

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. Básicamente el contexto de seguridad es guardado a nivel de sesión.

Page 126: Spring Framework-Javier Sevilla
Page 127: Spring Framework-Javier Sevilla

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.

Autorización en Spring Security

El siguiente diagrama de clases define la arquitectura del módulo de autorización en Spring

Security:

En el centro del diagrama tenemos la clase abstracta AbstractSecurityInterceptor, ésta

representa las capacidades de autorización básicas. Se pueden securizar peticiones HTTP

gracias a FilterSecurityInterceptor e invocaciones a métodos gracias a

MethodSecurityInterceptor y AspectJSecurityInterceptor.

Este interceptor delega en el interfaz AccesDecisionManager la decisión de la autorización

final. Este interfaz implementa un método decide que básicamente lo que hace es recibir un

objeto Authentication representando al principal que accede a la petición, un objeto seguro

(url o método) y una lista de aributos con metadatos de seguridad (la lista de roles para los que

se les puede dar acceso).

El siguiente diseño es el definido para el componente AccesDecisionManager:

Page 128: Spring Framework-Javier Sevilla

El diagrama representa el sistema de autorización 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 abstención.

ConsensusBased, en este caso será necesario que haya más votos positivos que negativos para

dar acceso al recurso protegido. También se puede controlar el comportamiento si todos los

votos son abstención.

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 abstención.

Los objetos AccessDecissionManager hacen uso de AccessDecissionVoter, actualmente Spring

proporciona dos clases de este objeto:

RoleVoter, comprueba cada rol que protege el recurso comprobándolo con el principal que

realiza la petición. 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 anónimo,

completamente autenticado o autenticado mediante remember-me. Así podrá acceder a

Page 129: Spring Framework-Javier Sevilla

129

páginas que necesitan tener un comportamiento diferente para usuarios autenticados de los

que no.

Todos los componentes del módulo de autorización de Spring Security permiten ser

extendidos para proveer mecanismos de autorización específicos o más profundos.

Seguridad de la capa de servicio

Spring Security permite securizar invocaciones a métodos, comúnmente llamado seguridad a

nivel de capa de servicio. Esto permitirá que tengamos mecanismos de autorización de

invocaciones de componentes de la capa de servicio aportando mayor seguridad.

Spring otorga cuatro configuraciones para la seguridad en la capa de servicio, configuración a

nivel global mediante namespace, mediante el componente MethdoSecurityInterceptor,

basada en anotaciones o a nivel de bean.

En Spring podemos observar cuatro maneras de securizar las ejecuciones de métodos dentro

de Spring Security, Configuración de seguridad a nivel global usando namespace, a nivel de

bean, usando el componente MethodSecurityInterceptor y basada en anotaciones.

Configuración de seguridad a nivel global (namespace)

Spring Security permite una configuración de seguridad a nivel global, esta opción es una

securización centralizada de todos los métodos de la capa de servicio. Adicionalmente,

presenta una expresión AspectJ de definición del pointcut, el cual permite aplicar de manera

flexible seguridad a varios objetos. En el ejemplo expuesto se están securizando todos los

métodos del paquete com.mycompany que acaben por service e independientemente del

número de parámetros que tenga el método.

Seguridad a nivel de bean

Con el objeto MethodSecurityInterceptor

Page 130: Spring Framework-Javier Sevilla

Seguridad a nivel de anotación

Page 131: Spring Framework-Javier Sevilla

131

Page 132: Spring Framework-Javier Sevilla
Page 133: Spring Framework-Javier Sevilla

133

Page 134: Spring Framework-Javier Sevilla

Capítulo 7: Desarrollando con

Spring

Javier Sevilla Sánchez

Page 135: Spring Framework-Javier Sevilla

135

Page 136: Spring Framework-Javier Sevilla

Contenido ¿Qué vamos a hacer? ................................................................................................................ 137

¿Por dónde empiezo? ............................................................................................................... 137

Maven........................................................................................................................................ 137

Web.xml .................................................................................................................................... 138

Ficheros de definición 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 creación de CustomEditor ........................................................................................... 159

Creando CustomEditors con PropertyEditorSupport ............................................................ 160

Uso de WebBindingInitializer: La clase CampusBindingInitializer ........................................ 162

Uso de la anotación @InitBinder .......................................................................................... 163

Page 137: Spring Framework-Javier Sevilla

137

¿Qué vamos a hacer?

A lo largo de este libro, hemos hecho referencias académicas como estudiantes, profesores o

la universidad de Alcalá. De modo que es bastante oportuno hacer una aplicación de campus

virtual que implemente cada una de las funcionalidades explicadas.

Así, esta aplicación tendrá que dar uso de las funcionalidades de Spring en materia de

conexión de beans (gracias al contenedor), aspectos, persistencia de datos, web y seguridad.

¿Por dónde empiezo?

Elegir un IDE para desarrollar puede ser el primer de los pasos que demos, cada desarrollador

tiene entornos preferidos. A pesar de que particularmente prefiero eclipse facilitaremos lo

posible el que el IDE sea independiente de la aplicación.

Maven Vamos a utilizar maven, una herramienta de software para la gestión y construcción de

proyectos Java. Es similar en funcionalidad a Apache Ant, pero tiene un modelo de

configuración de construcción más 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 módulos y componentes externos, y el orden de construcción de los

elementos. Viene con objetivos predefinidos para realizar ciertas tareas claramente definidas,

como la compilación del código y su empaquetado.

En el fichero pom.xml definiremos, entre otras cosas las dependencias de Spring, el siguiente

código 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 >

Page 138: Spring Framework-Javier Sevilla

<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 código tendremos todas las librerías de Spring que utilizaremos más adelante.

Web.xml

El fichero web.xml configura nuestra aplicación, 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 configuración del contexto.

El siguiente código muestra cómo configurar el contexto:

Page 139: Spring Framework-Javier Sevilla

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.ContextLoaderListen er </ listener-class >

</ listener >

El siguiente código muestra cómo configurar el DispatcherServlet:

<servlet > <servlet-name >campus</ servlet-name > <servlet-class > org.springframework.web.servlet.DispatcherServ let </ servlet-class > </ servlet > <servlet-mapping > <servlet-name >campus</ servlet-name > <url-pattern >/ </ url-pattern > </ servlet-mapping >

Este Servlet será el punto de partida para la carga de todos los componentes de Spring.

Buscará en la raíz de la aplicación un fichero llamado campus-servlet.xml que contendrá la

definición del contenedor de Spring.

Para poder implementar la seguridad tendremos que incluir los siguientes filtros:

<filter > <filter-name >springSecurityFilterChain </ filter-name > <filter-class >org.springframework.web.filter.DelegatingFilterProx y</ filter-class > </ filter > <filter-mapping > <filter-name >springSecurityFilterChain </ filter-name > <url-pattern >/* </ url-pattern > </ filter-mapping > Uno de los problemas más habituales que existe en las aplicaciones web es la codificación, los

servidores de aplicaciones han de ser configurados explícitamente para que transmitan y

reciban en una determinada codificación. De este modo, Spring hace posible que cada

aplicación tenga su configuración o incluso que se tengan varias configuraciones para una

misma aplicación. El siguiente filtro servirá para forzar la codificación, en este caso UTF-8.

<filter > <filter-name >CharacterEncodingFilter </ filter-name >

<filter-class > org.springframework.web.filter.CharacterEncodingFil ter </ filter-class >

<init-param > <param-name >encoding </ param-name > <param-value >UTF-8 </ param-value >

Page 140: Spring Framework-Javier Sevilla

</ init-param > <init-param > <param-name >forceEncoding </ param-name > <param-value >true </ param-value > </ init-param > </ filter >

Con esta definición creamos las herramientas necesarias para poder empezar nuestra

aplicación. El siguiente paso será crear y configurar nuestro fichero campus-servlet.xml.

Ficheros de definición de Beans

La forma más comúnmente utilizada por la comunidad para definir beans en Spring es a través

de los ficheros XML, ya sea de forma explícita con cada uno de los beans, o definiendo

etiquetas que buscaran anotaciones en nuestras clases. Existen múltiples combinaciones para

realizar esto, pero en la mayoría de los expertos dividen según su finalidad cada uno de los

ficheros. Acorde a nuestras necesidades y basándonos en estas normas de estilo dividiremos

nuestros beans basándonos en si el bean en cuestión es un objeto de persistencia, de

seguridad, de servicio o web. Así, los beans que definan el modelo así como el motor de

persistencia (en nuestro caso hibernate) irán en el fichero campus-hibernate.xml, los objetos

de servicio, irán en service.xml. Usaremos Spring Security y las definiciones de los beans irán

en el fichero campus-security.xml. Por último, El fichero campus-servlet.xml será el fichero en

el que se definan todos los objetos relativos a la parte web, en nuestro caso lo hemos dividido

en un segundo llamado Controllers.xml para tener un código más mantenible y más limpio, en

él definiremos los controladores.

Campus-servlet.xml

Vamos a explicar cuáles 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 estáticos y

cuál será su localización web. En este caso estamos diciendo que estará en la carpeta llamada

resources que está en la raíz de la aplicación y que tendrá la misma localización 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

Page 141: Spring Framework-Javier Sevilla

141

resolver las vistas y las ordenaremos. La que va en primer orden utiliza un fichero properties

que define dónde están 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 más sencillo, simplemente buscará el nombre de la vista

añadiendo 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.ResourceBund leViewResolver" > <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.InternalResou rceViewResolver" 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, beneficiándonos de la inyección de dependencia. El siguiente código muestra como

hacerlo:

<bean id ="tilesConfigurer" class ="org.springframework.web.servlet.view.tiles2.TilesC onfigurer" p:definitions ="/WEB-INF/tiles/templates.xml" />

Como vemos hemos de asignarle la propiedad del fichero .xml donde estarán 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 códigos de excepciones ocurridas en la

ejecución de nuestra petición, y por defecto, los servidores de aplicaciones muestran esa

excepción al usuario, que bien podría 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 excepción y así mostrar una

vista más amigable al usuario.

Page 142: Spring Framework-Javier Sevilla

<bean class ="org.springframework.web.servlet.handler.SimpleMapp ingExceptionResolver" > <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.TransactionExcepti on" >

dataAccessFailure </ prop >

</ props > </ property > </ bean > La internacionalización es un aspecto crucial en toda aplicación, y más para entornos web.

Gracias a la clase ReloadableResourceBundlemessageSource podemos configurar ficheros de

propiedades que contengan nuestros mensajes internacionalizados. El siguiente código

muestra cómo definir este bean:

<bean id ="messageSource" class ="org.springframework.context.support.ReloadableReso urceBundleMessageSource" > <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 parámetros como defaultEncoding, para la codificación,

cacheSeconds, que determina cada cuanto se recarga el fichero etc.

Otros beans necesarios en la internacionalización son el LocaleChangeInterceptor y el

SessionLocaleResolver definidos con el siguiente código:

<mvc:interceptors > <bean class ="org.springframework.web.servlet.i18n.LocaleChangeI nterceptor" p:paramName ="locale" /> </ mvc:interceptors > <bean id ="localeResolver" class ="org.springframework.web.servlet.i18n.SessionLocale Resolver" />

Page 143: Spring Framework-Javier Sevilla

143

Una de las funcionalidades de Spring MVC indispensables es el enlace de datos, gracias a

InitBinder podremos enlazar parámetros de petición 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.Ann otationMethodHandlerAdapter" > <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 definición, 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 escanearán los

controladores, en nuestro caso com.campus.web.controllers

El siguiente código es una muestra de cómo estos están 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 relación con la persistencia. Como bien

indica el nombre del fichero el motor de persistencia será Hibernate, sin embargo y gracias a la

modularización de Spring, múltiples motores de persistencia podrán ser configurados o

podremos tener varios ficheros, uno para JPA, iBatis, etc e intercambiarlos según nuestras

necesidades. Esto es posible gracias a que los demás beans que esperen los DAO definidos aquí

recibirán la implementación definida, pero ellos operarán con la interfaz, desconociendo cómo

estos DAO operan.

El primer bean definido en este fichero será un property placeholder, cuyo fichero será

jdbc.properties. El contenido de este fichero serán los distintos parámetros de configuración

para la fuente de datos así como para el sessionFactory de hibernate.

Page 144: Spring Framework-Javier Sevilla

El contenido de este fichero es el siguiente:

hibernate.generate_statistics= true hibernate.show_sql= true jpa.showSql= true jdbc.driverClassName= com.mysql.jdbc.Driver jdbc.url= jdbc\ : mysql\://localhost\:3306/campus jdbc.username= campus jdbc.password= campus hibernate.dialect= org.hibernate.dialect.MySQLDialect jpa.databasePlatform= oracle.toplink.essentials.platform.database.MySQL4Platform jpa.database= MYSQL

El siguiente bean definido es el dataSource. En esta aplicación de ejemplo hemos creado un

dataSource básico, si dispusiésemos de un dataSource en nuestro servidor podríamos acceder

a él también 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 cómodo 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.Anno tationS

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 k ey = "hibernate.generate_statistics" >${hibernate.generate_statistics}</ prop >

Page 145: Spring Framework-Javier Sevilla

145

<prop key ="hibernate.hbm2ddl.auto" >update </ prop > </ props > </ property > <property name="eventListeners" >

<map> <entry key ="merge" >

<bean class ="org.springframework.orm.hibernate3.support.IdTrans ferringMergeEventListener" /> </ entry > </ map> </ property > </ bean > El siguiente bean es un manager de transacción, en concreto el HibernateTransactionManager.

Este manager nos permitirá hacer operaciones transaccionales asegurando la atomicidad.

<bean id ="transactionManager" class ="org.springframework.orm.hibernate3.HibernateTransa ctionManager" p:sessionFactory-ref ="sessionFactory" />

Como vimos en el capítulo de persistencia, Spring otorga varias soluciones para facilitar la

creación de DAO, una de ellas son las plantillas, el siguiente código configura una plantilla.

<bean id ="hibernateTemplate" class ="org.springframework.orm.hibernate3.HibernateTempla te" p:sessionFactory-ref ="sessionFactory" />

El siguiente tag permite la configuración por anotaciones.

<context:annotation-config />

El siguiente tag indica que utilizaremos transacciones con anotaciones.

<tx:annotation-driven /> 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 configuración del transactionManager, el

bean necesario para las transacciones.

Page 146: Spring Framework-Javier Sevilla

<bean id ="transactionManager" class ="org.springframework.orm.hibernate3.HibernateTransa ctionManager" 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 ="all-service-methods" /> </ aop:config > <tx:advice id ="local-tx-advice" transaction-manager ="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 configuración diremos a Spring que cada vez que se ejecute un método

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 método sabe se

hará transacción y propagación.

De nada servirá esta configuración si no tenemos objetos DAO. Ellos serán nuestra conexión

entre base de datos y aplicación. Más tarde veremos cómo se definen estos DAO.

Page 147: Spring Framework-Javier Sevilla

147

Campus-security.xml La seguridad es una parte imprescindible en toda aplicación, especialmente las web. A menudo

vemos una infinidad de filtros tediosos y de difícil compresión cuya función es impedir que

usuarios malintencionados alteren el funcionamiento de la aplicación o que obtengan

información no permitida. Spring security, como ya comentamos en el capítulo dedicado a ello,

facilita todo esto otorgando una gran potencia.

El tag http permite que bajo patrones de petición se precise tener o no un rol específico. En la

siguiente definición vemos que por ejemplo para los recursos no se comprueba ningún

permiso, al igual que para la parte pública o el logeo. Sin embargo para todas las rutas que

comiencen por admin sólo un usuario con rol ADMIN tendrá acceso a ellas, así como teacher si

tiene el rol TEACHER etc.

También podemos definir cuál será nuestro formulario de logeo así como capacidades como la

posibilidad de introducir una cookie para recordarte en una máquina, o que haya un máximo

de sesiones abiertas para un usuario así como se puede configurar cúal será la ruta para salir

de la aplicación y su posterior redirección.

<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 también definimos el authenticationManager y le asignamos un proveedor, en

nuestro caso el DAO abstractUserDao el cuál implementa UserDetailService. También

definiremos un codificador de contraseña 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 autenticación sha gracias al tag

<password-encoder>.

Page 148: Spring Framework-Javier Sevilla

El método a implementar de UserDetailService es loadUserByUsername cuyo código es el

siguiente:

@Override public UserDetails loadUserByUsername( final String username) throws UsernameNotFoundException, DataAccessException { AbstractUser user = (AbstractUser) getHibernateTemplate().execute( new HibernateCallback() { @Override public Object doInHibernate(Session session) throws HibernateException { String hql = "from AbstractUser au " + " where au.username = :username\n" ; Query query = session.createQuery(hql); query.setString( "username" , username); AbstractUser abstractUser = (AbstractUser) query.uniqueResult(); return abstractUser; } }); if (user == null) { throw new UsernameNotFoundException( "User not found with the username of " + username); } return user; }

El objeto AbastractUser tendrá que implementar la clase UserDetails, el código siguiente es la

implementación de los métodos:

public String getPassword() { return password ; }

public String getUsername() { return username ; }

@OneToMany(cascade = CascadeType. ALL, fetch = FetchType. EAGER, targetEntity = Authority. class, mappedBy = "user" ) public Collection<GrantedAuthority> getAuthorities() { return authorities ; }

@Override @Transient public boolean isAccountNonExpired() { return true; }

Page 149: Spring Framework-Javier Sevilla

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 código

pertenece a Authority.java:

@Override @Transient public String getAuthority() { return this. value .name(); }

Sólo se ha de implementar este método, el cuál devolverá el nombre del rol en cuestión. La

siguiente clase, AutorityType, muestra cuales son los distintos roles:

package com.campus.model.security; public enum AuthorityType { ADMIN, TEACHER, STUDENT }

En nuestra aplicación sólo 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

estadísticas de las respuestas. También 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.

Page 150: Spring Framework-Javier Sevilla

Objetos de acceso a datos (DAO) y el modelo a persistir

En toda aplicación es aconsejable definir buenos interfaces que definan cuales serán las

operaciones que se harán con nuestros objetos. En el caso de los DAO si cabe, cobra más

importancia esta buena práctica, ya que gracias a Spring es muy fácil poder hacer uso así como

cambiar entre distintas herramientas de persistencia. Para nuestra aplicación hemos elegido

Hibernate, pero como ya dijimos, Spring es compatible con la mayoría de los framework de

persistencia como iBatis, JPA etc. Así si tenemos bien definidos los interfaces podremos hacer

que el cambio entre los framework sea relativamente sencillo.

Así tendremos dos paquetes:

com.campus.orm.dao.* en el que se declararán los distintos interfaces de cada uno de los

DAO.

com.campus.orm.dao.hibernate.* en el que estarán las implementaciones mediante el uso de

HibernateDaoSupport.

Como explicamos en el capítulo dedicado a la persistencia, HibernateDaoSupport facilita la

creación de DAO con Hibernate. En nuestro caso tendremos una clase base de la que

heredarán el resto de implementaciones. Esta se llamará BaseDao cuya implementación con el

framework de Hibernate será BaseDaoHibernate. En ella definiremos las operaciones más

comunes relativas al guardado, recuperación mediante clave etc de nuestros objetos de

dominio. Todo objeto de dominio tendrá que implementar BaseEntity y heredar

BaseEntityCampus.

El siguiente código pertenece a BaseEntityCampus:

@MappedSuperclass public abstract class BaseEntityCampus implements BaseEntity, Serializable { private Long id ; public BaseEntityCampus() { super(); } public BaseEntityCampus(Long id) { super(); this. id = id; } @Id @GeneratedValue (strategy = GenerationType. TABLE) @Override public Long getId() { return id ; } @Override public void setId(Long id) {

Page 151: Spring Framework-Javier Sevilla

151

this. id = id; } @Override @Transient public boolean isNew() { return this.getId() == null; } }

Con la anotación @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 código

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); }

Y su imlementación en con HibernateDaoSupport:

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; }

Page 152: Spring Framework-Javier Sevilla

@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 método constructor que tiene como parámetro una clase de tipo

entidad base. Esta será la clase de la cual heredarán el resto de los objetos DAO como

podemos comprobar en el siguiente código con StudentDaoHibernate:

Page 153: Spring Framework-Javier Sevilla

153

@Repository (value = "studentDao" ) public class StudentDaoHibernate extends BaseDaoHibernate<Student> implements StudentDao {

...

}

La etiqueta @Repository dirá a Spring que esta clase será un bean cuyo nombre será

studentDao, más tarde a la hora de utilizar este dao será tan sencillo como utilizar la anotación

@Autowired, el siguiente código muestra a un cómo esta clase será inyectada:

@Autowired private StudentDao studentDao ;

La anotación @Autowired dirá a Spring que el tipo de este objeto es un bean definido en el

contenedor y que es sujeto de ser inyectado.

Page 154: Spring Framework-Javier Sevilla

Spring MVC

Como ya dijimos, esta aplicación será web y hará uso de la herramienta que Spring otorga para

contruir aplicaciones web, Spring MVC.

Controllers

Tras ver cómo damos soporte a los componentes de Spring MVC con el fichero Campus-

Servlet.xml es tiempo de ver el tipo más utilizado en Spring, el Controller.

En anteriores versiones de Spring, los controladores tenían que ser definidos en el fichero xml

y tenían que heredar de alguna de las clases de soporte de Spring. Ahora esto se ha acabado

gracias a la anotación @Controller, con ella cualquier POJO podrá ser un controlador sin

necesidad de heredar clases específicas de Spring. El siguiente código de la aplicación 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 sería ver qué sencillo es decirle a

Spring que la clase es un controlador, simplemente con la anotación @Controller. Spring

buscará en los paquetes de escaneo definidos en el fichero xml.

La anotación @RequestMapping hace referencia a qué ruta mapeará este controlador, es

opcional el que esté encima de la definición de la clase, si es así será la raíz para el resto de los

métodos. Cada método que tenga la anotación @RequestMapping() será mapeado por Spring

como un método del controlador.

Los métodos de un controlador serán sujetos de ser utilizados dependiendo de la URL de la

petición y del método de la petición (es decir, POST, GET,..etc).

Page 155: Spring Framework-Javier Sevilla

155

Es decir, puede haber métodos con la misma URL pero con distintos RequestMethod.

En el ejemplo existe otra anotación a destacar, se trata de @InitBinder este método será del

que se sirva para el enlace de los valores de los parámetros de la petición 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 método.

Tras la ejecución del método vemos que devuelve un objeto ModelAndView, este objeto es

básico 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.

También podremos devolver un objeto String, de este modo simplemente Spring buscará la

vista y la devolverá. Si en los parámetros del método hemos pedido el objeto Model Spring lo

inyetará y si hacemos alguna modificación esta estará presente en la vista designada por la

cadena.

El siguiente código 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 método se devuelve el nombre lógico de la vista pero la modificación del objeto Model

estará presente en la vista.

Ajax con Spring MVC

En nuestra aplicación haremos uso de la librería de javascript jQuery la cual facilita, entre otras

funcionalidades, la creación de AJAX. Con Spring una manera de tratar estas peticiones es

devolviendo el cuerpo de la petición gracias a @ResponseBody. Con @ResponseBody diremos

que la cadena devuelta (u objeto) será el contenido de la respuesta y no el nombre lógico de la

vista.

El siguiente código muestra cómo hacerlo:

@RequestMapping (method = RequestMethod. POST) @ResponseBody public String processSubmit( @RequestParam(value = "lessonNote" ) String lessonNote, @RequestParam( "lessonId" ) Lesson lesson) { if (lessonNote.trim().equals( "" )) { return "false" ; } Note note = this. noteDao .saveFromActualUser(lesson, lessonNote);

Page 156: Spring Framework-Javier Sevilla

return "{text:'" + note.getText() + "',creator:'" + note.getCreator() + "',creationDate:'" + note.getCreationDate() + "'}" ; }

Vemos como el método 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 definición lógica y un física de la vista en

concreto, podremos utilizar distintas tecnologías como Velocity o FreeMarker, pero en nuestra

aplicación de ejemplo utilizaremos la más utilizada por al comunidad, los JSP.

Para la resolución de las vistas de lógico a físico utilizaremos dos clases, definidas en campus-

servlet.xml:

<bean id ="viewResolver" class ="org.springframework.web.servlet.view.ResourceBundl eViewResolver" > <property name="basename" value ="views" /> <property name="order" value ="0" /> </ bean >

E InternalResourceViewResolver:

<bean class ="org.springframework.web.servlet.view.InternalResou rceViewResolver" 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.TilesC onfigurer" p:definitions ="/WEB-INF/tiles/templates.xml" />

Page 157: Spring Framework-Javier Sevilla

157

EL siguiente código es un extracto de views.properties y muestra cómo mapearemos cada una

de las vistas lógicas a usar por Spring con cada una de las plantillas de Tiles:

home.(class)= org.springframework.web.servlet.view.tiles2.TilesVi ew home.url= home

El nombre (en este caso home) será el nombre lógico de la vista, el atributo .(class)= hace

referencia a la clase a utilizar por Spring y el atributo url será la plantilla de Tiles, (también

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 tecnologías y combinarlas con nuestro framework. Este

resolutor se ejecutará en primera posición, es decir a mismos nombres lógicos este será el

encargado de resolver la vista.

El siguiente resolutor es el InternalResourceViewResolver, el comportamiento de este es muy

sencillo, a el nombre lógico de la vista se le añadirán 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 serán muy útiles a la hora de

construir nuestras aplicaciones. En esta aplicación 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 estarán presentes en cada uno de los formularios de la aplicación. Las principales utilidades

son las siguientes:

• <Form:form> definirá el formulario en cuestión 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> serán 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 validación del

formulario en la parte servidora.

Page 158: Spring Framework-Javier Sevilla

Spring tag

Con <spring:*> tendremos múltiples funcionalidades como el enlace de objetos , la creación de

URL absolutas, evaluación 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 petición. Este tag lo utilizaremos

mucho para mostrar u ocultar información dependiendo del tipo de usuario que seas. Por

ejemplo las opciones del menú no serán 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 código 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 código de parte del menú, para ser más concreto de las preguntas. Como

hemos dicho un alumno sólo debería poder ver la opción de responder a preguntas y un

profesor sólo debería poder crear preguntas o ver las estadísticas 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 según que rutas.

Page 159: Spring Framework-Javier Sevilla

159

Binding con WebDataBinder

Los componentes de Binding o enlace que Spring otorga tienen la finalidad de enlazar

parámetros de la petición con objetos. Estos objetos pueden ser de diversos tipos, desde tipos

simples como Long, Boolean, String, Intege..etc hasta objetos complejos como en nuestro caso

Student, Teacher, Subject, Lesson así como datapickers y comandos. Todo ello será gestionado

por WebDataBinder.

Por lo general esta aplicación se sirve de objetos datapickers para recoger los datos de los

formularios. Estos datapicker pueden contener objetos de tipo complejo como Student,

Teacher etc…La manera que tiene Srping de enlazar estos objetos es ver qué tipo se espera

buscar en sus WebBindingInitializer definidos y en los métodos marcados con @InitBinder de

los controladores.

Las siguientes propiedades pertenecen a LessonDataPicker:

private Subject subject ; private String name; private String description ; private String content ; private Boolean active ; private boolean edit = false; private Long id ; private Date beginDate ; private Date endDate ;

Como vemos existen propiedades de diversos tipos, pero llama la atención cómo 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 intentarán 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 cómo 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.

Uso y creación de CustomEditor

Los custom editors son los objetos de los que se sirve Spring para realizar este enlace, como ya

hemos dicho se pueden basar en todo tipo de estrategias para convertir un texto enviado en la

petición a un objeto java.

El siguiente código muestra el uso de CustomDateEditor de Spring:

SimpleDateFormat dateFormat = new SimpleDateFormat( "dd/MM/yyyy" ); dateFormat.setLenient( false); binder.registerCustomEditor(Date. class, newCustomDateEditor(dateFormat, false));

Page 160: Spring Framework-Javier Sevilla

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 parámetro de

la petición 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.

Creando CustomEditors con PropertyEditorSupport

Cuando heredamos de PropertyCustomEditor tenemos que implementar el método setAsText

el cuál tiene como parámetro el texto enlazado desde la petición. El siguiente ejemplo es un

ejemplo de nuestro objeto CustomerEditor base para las entidades de nuestro modelo:

public class CampusCustomEditor extends PropertyEditorSupport { @SuppressWarnings ( "unchecked" ) private final BaseDao dao ; @SuppressWarnings ( "unchecked" ) protected CampusCustomEditor(BaseDao dao) { super(); this. dao = dao; } @Override public void setAsText(String text) { Long id = null; if (!StringUtils. hasLength (text.trim())) { setValue( null); return; } id = new Long(text); BaseEntity entity = dao .get(id); if (entity == null) { return; } setValue(entity); } }

Como vemos el constructor precisa de la implementación del objeto de tipo abstracto BaseDao

el cual hará uso para obtener la entidad esperada, así simplemente pasándole el identificador

de base de datos enlazará la entidad completa.

Luego la creación de un CustomEditor para un objeto del modelo será tan sencillo como el

siguiente código:

Page 161: Spring Framework-Javier Sevilla

161

public class LessonCustomEditor extends CampusCustomEditor {

public LessonCustomEditor(LessonDao dao) { super(dao); } }

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 métodos anotados con @InitBinder de nuestros controladores.

Page 162: Spring Framework-Javier Sevilla

Uso de WebBindingInitializer: La clase CampusBindingInitializer

Para nuestra aplicación definiremos una clase con los CustomEditor más comunes. Esta clase

tendrá que heredar de WebBindingInitializer e implementar el método initBinder.

El siguiente código pertenece a CampusBindingInitializer:

public class CampusBindingInitializer implements WebBindingInitializer { public CampusBindingInitializer() { } @Override public void initBinder(WebDataBinder binder, WebRequest reques t) { 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á sirviéndose de estos

CustomEditor.

Page 163: Spring Framework-Javier Sevilla

163

Uso de la anotación @InitBinder

Otra forma que Spring tiene para enlazar es mediante métodos anotados con @InitBinder. El

siguiente códig muestra cómo hacerlo:

@InitBinder public void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) throws Exception {

binder.registerCustomEditor( Lesson. class, new LessonCustomEditor( this. lessonDao )); }

Como vemos en el código, la manera de definirlo es igual que dentro del método initBinder de

los WebInitializer.

Page 164: Spring Framework-Javier Sevilla
Page 165: Spring Framework-Javier Sevilla

165

Page 166: Spring Framework-Javier Sevilla

Apéndice A:

Hibernate Configuración y puesta en marcha del

framework En este apéndice veremos las características básicas del marco de trabajo, veremos cómo crear objetos persistentes y definir el mapeo ORM.

Javier Sevilla Sánchez

Page 167: Spring Framework-Javier Sevilla

167

Contenido Introducción .............................................................................................................................. 168

Creación 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

Relación many-to-many ........................................................................................................ 173

Configuración de Hibernate: Fichero .cfg.xml ........................................................................... 173

Inicializar el objeto Session ....................................................................................................... 174

Operaciones típicas con el objeto Session ................................................................................ 175

Inserción ................................................................................................................................ 175

Actualización ......................................................................................................................... 175

Consulta ................................................................................................................................. 176

Borrado.................................................................................................................................. 176

Page 168: Spring Framework-Javier Sevilla

Introducción

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 aplicación,

mediante archivos declarativos (XML) que permiten establecer estas relaciones. Fue

desarrollada inicialmente por la iniciativa de un grupo de desarrolladores dispersos por el

mundo. Más tarde JBoss financiaría el proyecto. Con la información que otorgamos a

Hibernate relativa a las tablas, relaciones, etc. le permite a la aplicación manipular los datos de

la base operando sobre objetos, con todas las características 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

ejecución de dichas sentencias, manteniendo la portabilidad entre todos los motores de bases

de datos con un ligero incremento en el tiempo de ejecución. Éste es un framework de

software libre bajo licencia GNU-LGPL.

Hibernate está diseñado para ser flexible en cuanto al esquema de tablas utilizado, para poder

adaptarse a su uso sobre una base de datos ya existente. También tiene la funcionalidad de

crear la base de datos a partir de la información disponible. Hibernate otorga un lenguaje de

consulta de datos llamado HQL (Hibernate Query Language) y una API para construir las

consultas programáticamente criteria.

Hibernate para Java puede ser utilizado en aplicaciones Java independientes o en aplicaciones

Java EE, mediante el componente Hibernate Annotations que implementa el estándar JPA, que

es parte de esta plataforma.

Con lo que las principales características son:

• Framework Open Source.

• Independencia del motor de BBDD.

• Carga perezosa de objetos iniciándolos bajo demanda.

• Capacidad de generar el modelo de datos a partir de modelo de objetos.

• Compatible con la especificación JPA.

• Lenguaje propio unificado para las consultas (Hibernate Query Language)

• Api para realizar consultas Criteria.

• Utiliza la Programación Orientada a Aspectos (AOP), interceptando los objetos y

creando proxys.

• No incluye nuevos atributos a los POJOs simplemente genera interceptores.

Creación del esquema Hibernate es capaz de facilitar cual será el modelo de persistencia con simples POJOs y un

buen mapeo XML o anotaciones JPA. Con esos ingredientes Hibernate será capaz de poder

crear el esquema de diferentes maneras.

Page 169: Spring Framework-Javier Sevilla

169

Con un esquema existente

Cuando el esquema ya existe debemos crear nuestros objetos en consonancia con el esquema

ya existente. Realizaremos el mapeo de esos objetos y atributos de manera manual o también

de forma automática con una herramienta de Hibernate.

Gracias a Hibernate Tools podremos pasar grandes bases de datos con un gran número de

tablas de una forma sencilla.

Sin que exista un esquema

Cuando esto ocurre, Hibernate permite que una vez realizado el diseño orientado a objetos se

genere el modelo de datos relacional asociado. Es decir, de forma automática Hibernate creará

las tablas asociadas a nuestros POJOs. Esto lo hará de manera independiente a la base de

datos elegida.

Con un esquema Parcial

También Hibernate puede ampliar la parte que nuestra aplicación precise. Es útil cuando parte

del esquema ya existe pero es necesario ampliarlo con los objetos creados. Para ello

tendremos que generar nuestro modelo de dominio persistente, teniendo en cuenta el modelo

relacional y posteriormente podremos utilizar Hibernate pare generar el modelo de datos

relacional a partir de nuestro dominio.

Hibernate se sirve de un gran número de interceptores que crearán objetos de tipo Proxy. El

típico problema que surge al utilizar un único modelo de dominio persistente con Hibernate es

consultar los datos de nuestros objetos fuera de la sesión de Hibernate. Cuando esto ocurre

nos dará una excepción de sesión cerrada. Se recomienda utilizar el dominio de persistencia

sólo con los objetos que sigan el patrón DAO y generar un nuevo modelo de dominio para el

resto de la aplicación.

Utilizando Hibernate Tools podremos personalizar la creación 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 señala la

relación de la clase con la tabla del modelo entidad-relación, 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.

El siguiente fichero corresponde a la descripción HBM del objeto estudiante.

Page 170: Spring Framework-Javier Sevilla

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hi bernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernat e-mapping-3.0.dtd"> <hibernate-mapping> <class dynamic-insert="false" dynamic-update="f alse" mutable="true" name="es.uah.hibernateaddendum.model.Student" o ptimistic-lock="version" polymorphism="implicit" select-before-update="f alse" 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="STUDEN TS_HAS_EXAMS"> <key> <column name="STUDENT_ID"/> </key> <many-to-many class="es.uah.hibernatead dendum.model.Exam" column="EXAM_ID"/> </set> <set lazy="true" name="subjects" table="STU DENTS_HAS_SUBJECTS"> <key> <column name="STUDENT_ID"/> </key> <many-to-many class="es.uah.hibernatead dendum.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 cuál 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 generación de

identificadores como increment, identity, native, assigned etc. Estos se definirán dentro del

cuerpo de id con el tag genarator.

Propiedades

Para mapear los atributos utilizaremos el elemento “property”. Como vemos en la definición

del xml, la columna STUDENT_NAME tiene correspondencia con el atributo name del objeto.

<property name="name"> <column name="STUDENT_NAME"/> </property>

Page 171: Spring Framework-Javier Sevilla

171

Como vemos los nombres del atributo de la clase y la columna de la tabla no han de coincidir.

Aún que en el ejemplo no lo especificamos, se pueden añadir restricciones como por ejemplo

no permitir valores nulos o el tamaño.

Formas de mapear objetos dentro de objetos Las relaciones entre tablas pueden ser de uno a uno (1-1), de uno a muchos (1-n) y de muchos

a muchos (m-n). En Hibernate podemos mapear estas relaciones con cuatro etiquetas, ono-to-

one, one-to-many, many-to-many y many-to-one. Para explicar todas estas relaciones

hagamos un ejemplo consistente en cómo la información de una carrera, sus asigunaturas

exámenes y estudiantes está relacionada.

Figura 5 Diagrama relacional de estudiantes, exámenes, asignaturas y temas

En el diagrama vemos como un estudiante tiene n exámenes, que a su vez son realizados por

m estudiantes. Un examen se compone de una única asignatura, pero una asignatura puede

tener varios exámenes. 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 relación no es habitual, debido a que la mayoría de la información relacionada de esta

forma estaría en una sola tabla. En el ejemplo de la figura 1 vemos que no existe esta relación.

Esta relación sí se podría dar por ejemplo entre marido y esposa en culturas monógamas, ya

que una persona sólo puede estar casado con otra.

Relaciones many-to-one

Esta relación se da cuando la tabla de la parte del muchos tiene referencias a la clave primaria

de la tabla del uno. En esta relación 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 exámenes para cada

asignatura.

En el modelo de clases vemos como cada examen tiene una asignatura el fichero de definición

hbm sería:

Page 172: Spring Framework-Javier Sevilla

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hi bernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernat e-mapping-3.0.dtd"> <hibernate-mapping> <class dynamic-insert="false" dynamic-update="fal se" mutable="true" name="es.uah.hibernateaddendum.model.Exam" optimist ic-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.mo del.Subject" fetch="join" name="subject"> <column name="SUBJECT_ID" not-null="true"/> </many-to-one> </class> </hibernate-mapping>

La relación muchos a uno del examen indica que para una misma asignatura existirán varios

exámenes.

Relaciones one-to-many

Esta relación se da cuando el objeto que mapeamos contiene una colección, mapa, etc que

hace que se hagan múltiples referencias. En el esquema relacional esto se convierte en una

tabla es referenciada múltiples 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

sólo puede pertenecer a una asignatura, es decir la asignatura es la parte del uno y los temas

serán 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/Hi bernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernat e-mapping-3.0.dtd"> <hibernate-mapping> <class name="es.uah.hibernateaddendum.model.Subje ct" 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

habíamos visto también en el hbm del estudiante. Hemos de tener cuidado con este tipo de

Page 173: Spring Framework-Javier Sevilla

173

relación ya que esta propiedad determinará cómo se cargan los objetos dentro de la sesión de

Hibernate. Es decir cómo 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 podría ser que necesitásemos un

estudiante para saber su nombre y tuviésemos lazy=”false” en el atributo de los exámenes

realizados, esto haría que Hibernate instanciase todos los objetos Examen sin que fuesen

necesarios.

Relación many-to-many

Estas relaciones hacen que cada ocurrencia, en cualquiera de las dos entidades de la relación,

puede estar asociada con muchas (n) de la otra y viceversa. Es decir en caso un alumno realiza

muchos exámenes y un examen es realizado por muchos alumnos. A la hora de pasar eso al

modelo de objetos se traduce a una colección de exámenes en el objeto alumno y una

colección de alumnos en la clase examen.

<set lazy="true" name="exams" table="STUDEN TS_HAS_EXAMS"> <key> <column name="STUDENT_ID"/> </key> <many-to-many class="es.uah.hibernatead dendum.model.Exam" column="EXAM_ID"/> </set> <set lazy="true" name="subjects" table="STU DENTS_HAS_SUBJECTS"> <key> <column name="STUDENT_ID"/> </key> <many-to-many class="es.uah.hibernatead dendum.model.Subject" column="SUBJECT_ID"/> </set>

Una vez visto como mapear los objetos con Hibernate es preciso explicar cómo configurar

Hibernate con todo aquello que necesite para hacerlo funcionar.

Configuración de Hibernate: Fichero .cfg.xml

En los anteriores puntos hemos visto como crear un esquema y como mapear los distintos

objetos con ficheros hbm.xml. Lo que haremos ahora es unir todo ello para que cobre un

sentido así como definir propiedades relativas a la base de datos como la URL, el usuario, el

password, el driver, los distintos hbm y el dialecto que usaremos. El dialecto es el encargado

de trasformar las sentencias HQL al SQL de una base en cuestión. Así tendremos distintas

clases dialecto en medida de la base de datos que estemos usando. Para nuestro ejemplo

usaremos la clase MySQLDialect contenida en el paquete org.hibernate.dialect.*. Esta clase

extiende de la clase abstracta org.hibernate.dialect.Dialect. Al igual que el dialecto, tal y como

hemos comentado, también tendremos que definir la URL, el usuario, el password, el driver y

todos aquellos ficheros hbm que necesitemos para mapear nuestra base de datos. El siguiente

fichero muestra cómo lo hemos realizado en nuestro ejemplo hibernateAddendum.cfg.xml.

Page 174: Spring Framework-Javier Sevilla

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibern ate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibe rnate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <property name="hibernate.dialect">org.hibernate.dialect.MySQ LDialect</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_ADDENDUM2</property> <property name="hibernate.connection.userna me">ADMIN</property> <property name="hibernate.connection.passwo rd">ADMIN</property> <!--descomentar la siguiente linea si no ex isten 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/SubjectUni t.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 cuál será útil para cuando

hagamos pruebas.

Inicializar el objeto Session

En hibernate el objeto sesión es indispensable para cualquier proceso relativo a la persistencia.

Este objeto lo crearemos gracias una factoría org.hibernate.SessionFactory en la que

podremos obtener gracias al objeto org.hibernate.cfg.Configuration. El siguiente código

muestra una forma útil de realizar todo lo comentado.

private static final SessionFactory sessionFactory; static { try { sessionFactory = new Configuration().co nfigure( "hibernateAddendum.cfg.xml").bu ildSessionFactory(); } catch (Throwable ex) { System.err.println("Initial sessionFact ory creation failed." + ex); throw new ExceptionInInitializerError(e x); } }

Tras obtener la sesión existe una serie de pasos para poder realizar toda operación persistente.

Tras la obtención de la sesión crearemos la transacción, operaremos en la base de datos y

finalizaremos la transacción y después la sesión Hibernate. Todo ello lo haremos a través del

Page 175: Spring Framework-Javier Sevilla

175

objeto sesión. Como se habrá percatado el lector, Spring nos facilita todo proceso reiterativo,

es decir, todo proceso en el cuál existan una serie de pasos fijos.

Operaciones típicas con el objeto Session

Existen una serie de operaciones típicas que se suelen hacer con las bases de datos como

puedan ser la inserción, actualización, borrado o consulta.

Inserción

Como ya hemos contado, existen un número de pasos que hemos de dar con las operaciones

con el objeto Session. Así primero recuperaremos el objeto sesión de la factoría, empezaremos

la transacción, ejecutaremos el método save() al cuál le pasaremos el objeto que queremos

persistir y después forzaremos la inserción con el método flush(), haremos un commit en la

base de datos y cerraremos la sesión. El código siguiente muestra los pasos descritos.

public void insert(Exam exam) throws ExamsException { try { Session session = getSessionFactory().o penSession(); Transaction transaction = session.begin Transaction(); session.save(exam); session.flush(); transaction.commit(); session.close(); } catch (HibernateException hibernateExcept ion) { throw new ExamsException(INSERT, exam, hibernateException); } }

Actualización

Cuando hayamos modificado objetos existentes previamente el método update() será el

encargado de realizar un upadate en la base datos. Es muy similar al método save comentado,

siguiendo los mismos pasos. Un código de ejemplo sería el siguiente.

public void update(Exam exam) throws ExamsExcep tion { try { Session session = getSessionFactory().o penSession(); Transaction transaction = session.begin Transaction(); session.update(exam); session.flush(); transaction.commit(); session.close(); } catch (HibernateException hibernateExcept ion) { throw new ExamsException(INSERT, exam, hibernateException); } }

A pesar de que la manera más clara de actualizar un objeto es esta, existe también la

posibilidad de recuperar un objeto con el método get de la sesión, hacer los cambios

necesarios y después cerrar la sesión. Esto hará el mismo efecto que el método update.

Page 176: Spring Framework-Javier Sevilla

Consulta

Disponemos de varias formas de obtener objetos de la base de datos. Podremos utilizar el

método get de la sesión pasándole la clase y el identificador o podemos hacer consultas ya sea

en lenguaje HQL o mediante el API de criteria. El siguiente código hace uso del método del

objeto sesión get.

public Exam getExam(int id) throws ExamsExcepti on { Exam exam = null; try { Session session = getSessionFactory().o penSession(); exam = (Exam) session.get(Exam.class, i d); session.close(); } catch (HibernateException he) { throw new ExamsException(GET_STUDENT, e xam, he); } if (exam == null) { throw new ExamNotFoundException(id); } return exam; }

Para poder hacer consultas más complejas Hibernate dispone de un lenguaje unificado de

consulta llamado HQL. Gracias a este lenguaje podemos crear consultas que más tarde

Hibernate interpretará pasándolas al lenguaje de la base de datos utilizada gracias al dialecto

seleccionado. El objeto Query es el encargado de la personalización de la consulta teniendo

diversos métodos 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 cómo utilizamos criteria es el siguiente:

public Collection<Exam> getAllExams() { Session session = getSessionFactory().openS ession(); 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 método delete. Para eliminar primero

deberemos recuperar el objeto y después borrarlo. El siguiente código muestra cómo hacerlo:

public void delete(int id) throws ExamsExceptio n { try { Session session = getSessionFactory().o penSession(); Transaction transaction = session.begin Transaction();

Page 177: Spring Framework-Javier Sevilla

177

Exam exam = (Exam) session.get(Exam.cla ss, id); transaction.commit(); session.close(); } catch (HibernateException exception) { throw new ExamsException(DELETE); } }

Page 178: Spring Framework-Javier Sevilla
Page 179: Spring Framework-Javier Sevilla

179

Page 180: Spring Framework-Javier Sevilla

CONCLUSIONES

Page 181: Spring Framework-Javier Sevilla

181

Page 182: Spring Framework-Javier Sevilla

Contenido Experiencia personal ................................................................................................................. 183

Bibliografía ................................................................................................................................ 184

Page 183: Spring Framework-Javier Sevilla

183

Experiencia personal

Como tantos otros Informáticos, empecé a trabajar antes de terminar mi carrera, Ingeniería Técnica en Informática de Gestión. Mi primer trabajo lo conseguí como programador Java y fui poco a poco dándome cuenta de la importancia que tiene hacer aplicaciones mantenibles, desacopladas, con módulos reutilizables y entendibles. Tras estos años 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 más con una

herramienta que ya había utilizado y que en ese momento veía práctica. Más tarde, según me

fui documentando, caí en la cuenta de lo extenso, versátil y potente que es Spring Framework

y entendí cómo era uno de los requisitos más valorados dentro del mundo Java en las ofertas

de trabajo. Comprobé que para entender Spring y cómo este interactúa tenía 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 más sobre el mundo

Java.

Según 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 compañero en la cual desarrollaríamos

una aplicación web. Todo ese desarrollo ha sido creado haciendo uso de Spring.

Page 184: Spring Framework-Javier Sevilla

Bibliografía

• Spring. Craig Walls. Anaya multimedia 2009.

• Java 6. Felipe Lima Díaz. Anaya Multimedia.

• http://www.springsource.org/

• Spring Web Flow 2 Web Development. Sven Lüppken, Markus Stäuble 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/

Page 185: Spring Framework-Javier Sevilla

185