Capítulo 1: Primeros pasos con Spring Primer contacto con el framework En este primer capítulo haremos una introducción teórica y haremos unos ejemplos teóricos básicos para empezar a tomar noción de lo que es Spring. Javier Sevilla Sánchez Trabajo de fin de carrera de la Ingeniería Técnica en Informática de Gestión (I.T.I.G) Contenido Introducción al framework de Spring............................................................................................ 4 Inyección de dependencia e inversión de control ........................................................................ 4 Módulos .................................................................................................................................... 4 El contenedor ........................................................................................................................ 5 Integración y acceso de Datos............................................................................................... 6 Web ....................................................................................................................................... 6 AOP ........................................................................................................................................ 6 Test ........................................................................................................................................ 6 Primeros pasos con Spring ............................................................................................................ 7 Mi primer Hola mundo con Spring ............................................................................................ 7 Inyección de Dependencia ID (Dependency injection) ................................................................. 9 Inyección de dependencia en la práctica .................................................................................. 9 Programación orientada a aspectos............................................................................................ 12 ¿Qué ventajas tenemos con AOP? .......................................................................................... 13 Resumen...................................................................................................................................... 13 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: 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. 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 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() { 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.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class HolaMundo { public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("holamundo.xml"); Saludo saludoConstructor = (Saludo) ctx.getBean("saludoConstructor"); saludoConstructor.saluda(); Saludo saludoMetodo = (Saludo) ctx.getBean("saludoMetodo"); 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 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 public public public void setCarrer(Carrer carrer); String getName(); void setName(String name); 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 universityName); } 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 universityName) { this.universityName = universityName; } public Certificate graduate() { return new UniversityCertificate(carrerName, universityName); } } 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 Certificate{ private String carrerName; private String universityName; public UniversityCertificate(String carrerName, String 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; } } 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/spring-beans-2.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-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 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.