manual spring

54
jueves, 2 de septiembre de 2010 Spring 3 - Parte 1: Introducción En la actualidad el desarrollo de aplicaciones empresariales, aunque es más sencillo ya que hay muchas herramientas, APIs, y Frameworks; los cuales aunque son opcionales, el aprender a usarlos nos ayudará a desarrollar nuestras aplicaciones en menos tiempo y a que estas sean más robustas y contengan menos errores. También se vuelve un poco más complicado porque nuestras aplicaciones deben ser capaces de conectarse con otras aplicaciones y servicios. Además, como desarrolladores, somos los responsables de coordinar cada una de las partes de nuestra aplicación para que todo funcione correctamente. Por si no fuera suficiente, debemos tomar en cuenta que debemos darle mantenimiento a nuestras aplicaciones, y que en algunos casos será necesario que cambiemos módulos o capas enteras de la misma para mejorarla; como por ejemplo sustituir nuestras consultas JDBC con Hibernate en los casos en los que sea prudente. Afortunadamente existen super-frameworks que nos ayudan haciéndose cargo de todas o casi todas las “complicaciones” mencionadas anteriormente. Spring es el más popular de estos super-frameworks Java. Nos proporciona varios módulos los cuales abarcan la mayor parte de las cosas que debemos hacer en cualquiera de las capas de nuestras aplicaciones, desde plantillas para trabajar con JDBC o invocación de Web Services y JMS, pasando por sus propias soluciones, ORM o MVC (web), hasta integración con otros frameworks, como Struts 2, Hibernate, JSF, etc. Todo esto de una forma elegante y haciendo uso de muchos buenos principios de programación. Además Spring maneja la infraestructura de la aplicación, por lo que nosotros solo deberemos preocuparnos de la lógica de la misma (y de la configuración de Spring). En esta serie de tutoriales aprenderemos como usar Spring, en su versión 3, para facilitarnos la vida en el desarrollo de nuestras aplicaciones Java. Para aprender a usar Spring de forma correcta hay que ver muuucha teoría, así que en este primer tutorial hablaremos solo de los conceptos básicos de Spring y explicaré cada una de las partes, junto con su teoría correspondiente, en el post correspondiente. Spring es, como lo definen sus autores, un framework ligero para construir aplicaciones empresariales. Aunque Spring se encuentra dividido en distintos módulos, cada uno de los cuales se encarga de partes diferentes de nuestra aplicación, no deja de ser un monstruo, ya que es tan grande que alguien podría nunca usar todos estos módulos en aplicaciones pequeñas o medianas; pero en aplicaciones grandes o realmente grandes puede ahorrarnos mucho trabajo ya que puede coordinar todas las partes de la aplicación. Esta separación en módulos nos permite usar solo las partes que necesitamos, sin tener la carga de los que no usemos. Spring está diseñado para no ser intrusivo, esto significa que no es necesario que nuestra aplicación extienda o implemente alguna clase o interface de Spring (si no lo queremos), por lo que nuestro código de lógica quedará libre y completamente reutilizable para un proyecto sin Spring, o por si debemos quitarlo de una aplicación que ya lo esté usando. Gracias a esto es posible usar un POJO o un objeto Java para hacer cosas que antes solo podían hacerse con EJBs. Sin embargo la utilidad de Spring no es solo para el desarrollo de aplicaciones web, o no solo en el servidor. Cualquier aplicación Java puede beneficiarse del uso de Spring. Además, si usamos Spring de la forma correcta (lo cual no es difícil) nuestra aplicación quedará dividida en capas bien delimitadas, y con buenas prácticas de programación.

Upload: marco-rodriguez-abad

Post on 08-Aug-2015

147 views

Category:

Documents


2 download

TRANSCRIPT

Page 1: Manual Spring

jueves, 2 de septiembre de 2010

Spring 3 - Parte 1: Introducción

En la actualidad el desarrollo de aplicaciones empresariales, aunque es más sencillo ya que hay muchas herramientas, APIs, y Frameworks; los cuales aunque son opcionales, el aprender a usarlos nos ayudará a desarrollar nuestras aplicaciones en menos tiempo y a que estas sean más robustas y contengan menos errores.

También se vuelve un poco más complicado porque nuestras aplicaciones deben ser capaces de conectarse con otras aplicaciones y servicios. Además, como desarrolladores, somos los responsables de coordinar cada una de las partes de nuestra aplicación para que todo funcione correctamente.

Por si no fuera suficiente, debemos tomar en cuenta que debemos darle mantenimiento a nuestras aplicaciones, y que en algunos casos será necesario que cambiemos módulos o capas enteras de la misma para mejorarla; como por ejemplo sustituir nuestras consultas JDBC con Hibernate en los casos en los que sea prudente.

Afortunadamente existen super-frameworks que nos ayudan haciéndose cargo de todas o casi todas las “complicaciones” mencionadas anteriormente. Spring es el más popular de estos super-frameworks Java. Nos proporciona varios módulos los cuales abarcan la mayor parte de las cosas que debemos hacer en cualquiera de las capas de nuestras aplicaciones, desde plantillas para trabajar con JDBC o invocación de Web Services y JMS, pasando por sus propias soluciones, ORM o MVC (web), hasta integración con otros frameworks, como Struts 2, Hibernate, JSF, etc. Todo esto de una forma elegante y haciendo uso de muchos buenos principios de programación. Además Spring maneja la infraestructura de la aplicación, por lo que nosotros solo deberemos preocuparnos de la lógica de la misma (y de la configuración de Spring).

En esta serie de tutoriales aprenderemos como usar Spring, en su versión 3, para facilitarnos la vida en el desarrollo de nuestras aplicaciones Java. Para aprender a usar Spring de forma correcta hay que ver muuucha teoría, así que en este primer tutorial hablaremos solo de los conceptos básicos de Spring y explicaré cada una de las partes, junto con su teoría correspondiente, en el post correspondiente.

Spring es, como lo definen sus autores, un framework ligero para construir aplicaciones empresariales. Aunque Spring se encuentra dividido en distintos módulos, cada uno de los cuales se encarga de partes diferentes de nuestra aplicación, no deja de ser un monstruo, ya que es tan grande que alguien podría nunca usar todos estos módulos en aplicaciones pequeñas o medianas; pero en aplicaciones grandes o realmente grandes puede ahorrarnos mucho trabajo ya que puede coordinar todas las partes de la aplicación. Esta separación en módulos nos permite usar solo las partes que necesitamos, sin tener la carga de los que no usemos.

Spring está diseñado para no ser intrusivo, esto significa que no es necesario que nuestra aplicación extienda o implemente alguna clase o interface de Spring (si no lo queremos), por lo que nuestro código de lógica quedará libre y completamente reutilizable para un proyecto sin Spring, o por si debemos quitarlo de una aplicación que ya lo esté usando. Gracias a esto es posible usar un POJO o un objeto Java para hacer cosas que antes solo podían hacerse con EJBs. Sin embargo la utilidad de Spring no es solo para el desarrollo de aplicaciones web, o no solo en el servidor. Cualquier aplicación Java puede beneficiarse del uso de Spring.

Además, si usamos Spring de la forma correcta (lo cual no es difícil) nuestra aplicación quedará dividida en capas bien delimitadas, y con buenas prácticas de programación.

El núcleo de Spring está basado en un principio o patrón de diseño llamado Inversión de Control (IoC por sus siglas en inglés). Las aplicaciones que usan el principio de IoC se basan en su configuración (que en este caso puede ser en archivos XML o con anotaciones como en Hibernate) para describir las dependencias entre sus componentes, esto es, los otros objetos con los que interactúa. En este caso “inversión” significa que la aplicación no controla su estructura; permite que sea el framework de IoC (en este caso Spring) quien lo haga.

Por ejemplo, supongamos que tenemos una clase “AlmacenUsuario”, que depende de una instancia de una clase “UsuariosDAO” para realizar su tarea. “AlmacenUsuario” crea una instancia de “UsuariosDAO” usando el operador “new” u obtiene una de algún tipo de Fabrica. Usando la técnica de IoC, una instancia de “UsuariosDAO”, o una subclase de esta, es proporcionada a “AlmacenUsuario” en tiempo de ejecución por el motor de Spring. En este caso “UsuariosDAO” también podría ser una interface y Spring se encargará de proporcionarnos una instancia de una clase que implemente esa interface. Esta inyección de dependencia en tiempo de ejecución ha hecho que a este tipo de IoC se le dé el nombre más descriptivo de inyección de dependencia (DI por sus siglas en inglés). El concepto importante es que los componentes no saben cuál implementación concreta de otros componentes están usando; solo ven sus interfaces.

El uso de interfaces y DI son mutuamente benéficos, ya que hace más flexible y robusta nuestra aplicación y es mucho más fácil realizar pruebas unitarias. Pero la complejidad de escribir código que maneje las dependencias entre los componentes de una aplicación diseñada para usar interfaces puede llegar a ser mucho y esto, además, hace que los desarrolladores tengamos que escribir aún más código.

Afortunadamente, usando DI reducimos la cantidad de código extra que debemos escribir, para un diseño basado en interfaces, casi a cero.

Page 2: Manual Spring

En el contexto de DI, Spring actúa como un contenedor que proporciona las instancias de las clases de nuestra aplicación todas las dependencias que necesita, pero en una forma no intrusiva y automática. Todo lo que debemos hacer es crear un archivo de configuración que describa las dependencias; Spring se hará cargo del resto.

Como dije antes: Spring es un contenedor ya que no solo crea los componentes de nuestra aplicación, sino porque contiene y maneja al ciclo de vida y configuración de estos componentes. En Spring, podemos declarar cómo debe ser creado cada uno de los objetos de nuestra aplicación, cómo deben ser configurados, y cómo deben asociarse con los demás.

La implementación de DI de Spring se enfoca en el acoplamiento débil: los componentes de nuestra aplicación deben asumir lo menos posible acerca de otros componentes. La forma más fácil de lograr este bajo acoplamiento en Java es mediante el uso de Interfaces. Como cada componente de la aplicación solo está consciente de la interface de otros componentes, podemos cambiar la implementación del componente sin afectar a los componentes que usan el nuevo componente. Hablaré un poco más de esto cuando veamos los ejemplos.

El uso de DI tiene como beneficios, además de lo que ya he mencionado arriba, los siguientes:

Reduce el código pegamento: Esto quiere decir que reduce dramáticamente la cantidad de código que debemos escribir para unir los distintos componentes. Aunque algunas veces esté código puede ser tan simple como usar el operador “new” para instanciar un nuevo objeto, otras puede ser más complejo, como realizar una búsqueda de dicha dependencia en un repositorio a través de JNDI, como en el caso de los recursos remotos. En esto caso, el uso de DI puede reducir de forma dramática la cantidad de código pegamento (o glue code) proporcionando búsquedas automáticas.

Externaliza dependencias: Como es posible colocar la configuración de dependencias en archivos XML podemos realizar una reconfiguración fácilmente, sin necesidad de recompilar nuestro código. Gracias a esto es posible realizar el cambio de la implementación de una dependencia a otra (como en el ejemplo de Hibernate que mencioné antes)

Las dependencias se manejan en un solo lugar: Toda la información de dependencias es responsabilidad de un solo componente, el contenedor de IoC de Spring, haciendo que este manejo de dependencias más simple y menos propenso a errores.

Hace que las pruebas sean más fáciles: Nuevamente, como nuestras clases serán diseñadas para que sea fácil el reemplazo de dependencias, podemos proporcionar mocks o dummies, que regresen datos de prueba, de servicios o cualquier dependencia que necesite el componente que estamos probando.

Como podemos ver, el uso de DI nos proporciona muchos beneficios, pero no sin sus correspondientes desventajas. En particular, es difícil ver qué implementación particular de una dependencia está siendo usada para qué objeto, especialmente para alguien que no está familiarizado con esta forma de trabajo.

¿Y por qué tanto hablar de DI? Pues porque estos dos conceptos (IoC y DI) son los puntos centrales alrededor del cual gira todo en Spring, así que es mejor entenderlos desde el principio ^_^.

Como dije: Spring está dividido en alrededor de 20 módulos y colocados en los siguientes grupos:

Contenedor Central (Core Container) Acceso a Datos / Integración

WEB

AOP (Programación Orientada a Aspectos)

Instrumentación

Pruebas

Estos grupos se muestran en la siguiente imagen:

Page 3: Manual Spring

A lo largo de estos tutoriales trataré de explicar la mayor cantidad de módulos. Pero como dije antes: Spring es en realidad un monstruo. Así que probablemente cuando termine de explicarlos todos, ya habrá salido una nueva versión, así que me centraré solo en los más importantes y en su integración con otros frameworks que comencemos a usar en otros tutoriales.

En general, estas son algunas de las características de Spring:

Simplificación de la programación orientada a aspectos. Simplificación del acceso a datos.

Simplificación e integración con JEE

Soporte para planificación de trabajos.

Soporte para envió de mail.

Interacción con lenguajes dinámicos (como BeanShell, JRuby, y Groovy).

Soporte para acceso a componentes remotos.

Manejo de Transacciones.

Su propio framework MVC.

Su propio Web Flow.

Manejo simplificado de excepciones.

La versión 3 de Spring es una versión revisada y mejorada de la versión estable anterior (2.5), que incluye nuevas características, entre las que se incluyen:

Soporte para Java 5: Proporciona configuración basada en anotaciones y soporta características como varargs y generics, además la parte web es compatible con las versiones 1.4 y 5 de Java EE. Debido a esta nueva característica, ahora es necesario tener el JRE versión 5 o superior.

Lenguaje de Expresiones (SpEL): En esta nueva versión se incluye un lenguaje de expresiones que puede ser usando cuando se definen beans, tanto en XML como con anotaciones y también da soporte a través de todos los módulos de Spring.

Soporte para Servicios Web REST: Ahora Spring soporte servicios web de tipo REST.

Soporte para Java EE6: Ofrece soporte de características como JPA 2.0, JSF 2.0 y JRS 303 (validaciones de Beans).

Soporte para bases de datos embebidas: Un soporte conveniente para bases de datos embebidas como HSQL, H2 y Derby.

Soporte para formateo de datos mediante anotaciones: Ahora los campos de fecha, divisas, etc., serán formateados automáticamente y convertidos usando anotaciones.

Nueva organización de los módulos: Los módulos han sido revisados y separados en diferentes paquetes, mas organizados, de acuerdo a su funcionalidad. Para que se den una idea, estos son los nuevos paquetes:

o org.springframework.aop

o org.springframework.beans

o org.springframework.context

o org.springframework.context.support

Page 4: Manual Spring

o org.springframework.expression

o org.springframework.instrument

o org.springframework.jdbc

o org.springframework.jms

o org.springframework.orm

o org.springframework.oxm

o org.springframework.test

o org.springframework.transaction

o org.springframework.web

o org.springframework.web.portlet

o org.springframework.web.servlet

o org.springframework.web.struts

A lo largo de estos tutoriales veremos algunos de estos paquetes. Aunque este post, como ya se habrán dado cuenta es solo de teoría, crearemos la librería para NetBeans de Spring que estaremos usando en esta serie de tutoriales.

Así que lo primero que haremos es abrir el NetBeans. En realidad el NetBeans 9 ya tiene una biblioteca de Spring 3, la 3.0.2.

Pero han salido actualizaciones desde esta versión (actualmente la versión más nueva es la 3.0.4). Así que lo que haremos es crear una nueva biblioteca que hará uso de los jars que necesitamos (y los cuales iremos incrementando en cada tutorial).

Bajamos la última versión de Spring de su página de descargas. También necesitaremos el archivo “commons-logging-api-1.1.1.jar” que podemos encontrar en la página de descarga de commons-logging, en el archivo "commons-logging-1.1.1-bin.zip”, tal y como lo hicimos en los tutoriales de JasperReports.

Una vez que hayamos bajado el archivo de Spring, lo descomprimimos y veremos que tiene un subdirectorio “dist” el cual contiene todos los jars de Spring para cada uno de sus módulos. Vamos al NetBeans y nos dirigimos al menú “Tools -> Libraries”.

En la ventana que se abre presionamos el botón “New Library...”:

Page 5: Manual Spring

En esta nueva ventana colocamos como nombre de la biblioteca "Spring3" y como tipo dejamos "Class Libraries":

Ahora que tenemos nuestra biblioteca , presionamos el botón “Add Jar/Folder” para agregar los nuevos archivos que conformarán la biblioteca:

Agregamos a la biblioteca los siguientes archivos:

org.springframework.asm-3.0.4.RELEASE.jar org.springframework.beans-3.0.4.RELEASE.jar

org.springframework.context-3.0.4.RELEASE.jar

org.springframework.core-3.0.4.RELEASE.jar

org.springframework.expression-3.0.4.RELEASE.jar

commons-logging-api-1.1.1.jar (que bajamos aparte)

Con estos es suficiente para hacer nuestros hola mundo iniciales :D. Una vez seleccionados estos archivos, nuestra biblioteca debe verse así:

Page 6: Manual Spring

Presionamos el botón “OK” y nuestra biblioteca estará lista para ser usada… en los siguientes tutoriales ^_^.Por el momento hemos cubierto una muy pequeña parte de la teoría con respecto a Spring 3, que iremos ampliando en los siguientes posts, así que no desesperen, pronto comenzaremos a usar este framework, que muchas alegrías (y dolores de cabeza) nos dará a más de uno.No olviden dejar cualquier comentario, duda o sugerencia en la sección de comentarios.

Saludos.

jueves, 23 de diciembre de 2010

Spring 3 - Parte 2: Contenedores de IoC e Inyección de Dependencias

Como vimos en el primer tutorial de la serie, el core de Spring se basa en el concepto de Inversión de Control (IoC por sus siglas en inglés), más específicamente en la Inyección de Dependencias (DI). Este es un proceso en el cual los objetos definen sus dependencias (o sea, los otros objetos con los que trabajan) solo a través de los argumentos de su constructor, argumentos a un método de factory, o métodos setter que son invocados después de que el objeto se ha construido. En este caso en vez de ser el mismo objeto quien se encargue de instanciar, o localizar, las dependencias con las que trabaja (usando directamente su constructor o un localizador de servicios), es el contenedor el que inyecta estas dependencias cuando crea al bean. Este proceso, como podemos observar, es el inverso al que normalmente se hace, y de ahí el nombre de Inversión de Control (es el framework el que hace el trabajo, no el programador ^_^).

En este tutorial veremos como configurar Spring de 4 formas distintas para que realice este proceso de Inyección de Dependencias de forma automáticaSpring coloca las clases básicas de su contenedor de IoC en dos paquetes:

org.springframework.beans org.springframework.context

Estos paquetes contienen dos interfaces que son las que realizan la "magia" de la instanciación de objetos. La primera es "org.springframework.beans.factory.BeanFactory", que proporciona un mecanismo de configuración avanzada capaz de manejar cualquier tipo de objeto. Además proporciona una sub-interface de esta última, "org.springframework.context.ApplicationContext", la cual agrega una integración más fácil con algunas de las características más interesantes de Spring, como su módulo de Programación Orientada a Aspectos, manejo de recursos de mensajes (para la internacionalización), publicación de eventos, y contextos específicos para ciertas capas de aplicaciones (como aplicaciones web), entre otras.

"BeanFactory" proporciona el framework de configuración y la funcionalidad básica, mientras que "ApplicationContext" agrega más funcionalidad específica para ciertos productos empresariales. Ambas interfaces representan contenedores de beans, solo que de tipos distintos.

"BeanFactory" representa las fábricas de beans y son el tipo de contenedor más simple. Este proporciona el soporte básico para DI. "ApplicationContext" representa... el contexto de la aplicación, estos están construidos con las mismas nociones de las fábricas de beans, pero proporcionan a la aplicación servicios adicionales.

En Spring, los objetos que forman la columna vertebral de las aplicaciones, y que son administradas por el contenedor de IoC de Spring, son llamados "Beans". Un bean es un objeto que es instanciado, ensamblado (cuando sus dependencias son inyectadas), y en general, administrado por el contenedor de IoC. Dicho de otra forma: un bean es simplemente uno de muchos objetos de nuestra aplicación. Los Beans, y las dependencias entre ellos, las declaramos en los metadatos del contenedor de IoC (lo cual puede ser mediante anotaciones o archivos de mapeo).

Como dije antes: la interface "ApplicationContext" representa un tipo de contenedor de IoC de Spring, y es el responsable de instanciar, configurar, y ensamblar los antes mencionados beans. El contenedor obtiene la información de qué objetos debe crear leyendo los metadatos de la configuración, los cuales, como dije antes, pueden ser colocados en archivos XML, anotaciones, o dentro del código Java.

Page 7: Manual Spring

Spring proporciona muchas implementaciones de "ApplicationContext". En particular en las aplicaciones que NO son web, usamos "org.springframework.context.support.ClassPathXmlApplicationContext", que busca los archivos de configuración dentro del classpath, o "org.springframework.context.support.FileSystemXmlApplicationContext", que los busca en el sistema de archivos de la máquina donde se ejecute la aplicación.

Para ejemplificar todo esto, haremos una aplicación la cual tendrá una clase de prueba que invocará a un " ServicioRemoto". Este servicio no hará algo especial, solamente regresará un número aleatorio entre 0 y 10; lo interesante de esto es que el cliente obtendrá una instancia del ServicioRemoto sin tener que crearla de manera directa, sino usando la DI de Spring.

Lo primero que haremos es crear un nuevo proyecto en NetBeans. Para esto vamos al Menú "File->New Project...". En la ventana que se abre seleccionamos la categoría "Java" y en el tipo de proyecto "Java Application". Le damos una ubicación y un nombre al proyecto, en mi caso será "SpringIoC". Nos aseguramos que las opciones "Create Main Class" y "Set as Main Project" estén habilitadas. Presionamos el botón "Finish" y veremos aparecer en el editor nuestra clase "Main".

Nota: Yo usaré dos proyectos, uno para usar archivos de mapeo y otro para usar anotaciones y que el código de ambos no se mezcle.

Agregamos la biblioteca de "Spring 3" que creamos en el primer tutorial de la serie. Hacemos clic derecho sobre el nodo "Libraries" del proyecto, en el menú que aparece elegimos la opción "Add Library..."

En la ventana que se abre seleccionamos la biblioteca "Spring 3":

Y presionamos el botón "Add Library". Con esto los archivos de la biblioteca deben agregarse a nuestro proyecto:

Page 8: Manual Spring

Ahora crearemos un nuevo paquete que contendrá a nuestros beans, este paquete se llamará " beans". Hacemos clic derecho en el paquete en el que se encuentra nuestra clase "Main" (que en mi caso es "ejemplos.spring.ioc") y en el menú que se abre seleccionamos la opción "New -> Java package..." y al nombre del paquete le agregamos "beans". Presionamos el botón "Finish" y con esto aparecerá nuestro nuevo paquete:

Crearemos nuestra clase "ServicioRemoto", dentro del paquete "beans". Esta clase será muy sencilla, y solo tendrá un método llamado "consultaDato" el cual no recibirá ningún parámetro y regresará un "int" aleatorio entre 1 y 10. La clase "ServicioRemoto" queda de esta forma:

public class ServicioRemoto{ public int consultaDato() { return (int)(Math.random()*10.0); }}

Como ven es una clase sumamente sencilla, así que no hará falta explicar mucho.

Normalmente para obtener una nueva instancia de "ServicioRemoto" tendríamos que colocar la siguiente línea en alguna parte de la clase de prueba:

servicioRemoto = new ServicioRemoto();

Como había dicho antes: el cliente no debería ser responsable de la instanciación de este ServicioRemoto, ya que es una tarea que no le corresponde. Gracias a la DI podremos evitarnos el escribir la línea anterior, y por lo tanto (como veremos un poco más adelante) muchos problemas potenciales que pueden surgir con ella. En Spring, los objetos no son responsables de encontrar o crear los otros objetos que necesitan para hacer su trabajo. En vez de eso, el contenedor les da las referencias de los objetos con los que colaborarán.

El acto de crear estas asociaciones entre objetos de la aplicación, usando DI, es llamado "wiring" (cableado en español). En el resto del tutorial veremos la configuración básica de este wiring con Spring, y veremos las posibilidades que este nos ofrece.

En las aplicaciones basadas en Spring, los objetos de la aplicación viven dentro del contenedor de beans. El contenedor se encarga de crear los objetos y "cablearlos", configurarlos, y administrar su ciclo de vida completo. Entender la forma en la que funciona este contenedor, nos permitirá comprender la forma en la que nuestros objetos serán manejados, y por lo tanto cómo podemos aprovecharlos mejor dentro de nuestra aplicación.

Configuración de Beans en Spring

Como mencione al principio: existen, básicamente, dos formas de configurar los beans y sus dependencias en Spring, la primera es mediante un archivo de configuración en XML, la segunda es mediante anotaciones proporcionadas por Spring. Como siempre, cada una tiene sus ventajas y sus desventajas. Nosotros veremos ambas formas de configuración. Iniciaremos viendo la configuración con archivos en XML y posteriormente pasaremos a verla con anotaciones.

1. Configuración con archivos XML

La configuración de Spring consiste de, al menos, la definición de un bean que será administrado por el contenedor. Los metadatos de configuración basados en XML muestran estos beans configurados como elementos "<bean />" (sin s) dentro de un elemento "<beans />" (con s).

Estas definiciones de beans corresponden con los objetos que componen nuestra aplicación. Cuando creamos una aplicación, típicamente definimos una capa de servicios, una capa de acceso a datos (o DAOs) una capa de presentación, y una capa de infraestructura (con objetos como

Page 9: Manual Spring

el "SessionFactory" de Hibernate). En Spring normalmente declaramos objetos que definen estas 4 capas (y no objetos muy específicos del dominio), ya que son las capas anteriores quienes se encargan de crear y manejar los objetos del dominio.

Crearemos el archivo de configuración en nuestro proyecto, así que hacemos clic derecho sobre el nodo "Source Packages" de nuestro proyecto, y en el menú contextual que se abre seleccionamos "new -> Other...":

En la ventana que se abre, seleccionamos la categoría "Other" y como tipo de archivo "Spring XML Configuration File":

Le damos un nombre al archivo, en mi caso será "applicationContext". Hacemos clic en el botón "Next". En la siguiente pantalla debemos seleccionar los namespaces que queremos que tenga nuestro archivo. No seleccionaremos ninguno. Más adelante en este mismo tutorial explicaré qué es esto. Presionamos el botón "Finish" y con eso aparecerá en nuestro editor el archivo "applicationContext.xml" con el siguiente contenido:

<?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-3.0.xsd">

</beans>

El archivo contiene únicamente el elemento raíz "<beans>" dentro del cual declararemos cada uno de nuestros beans usando un elemento "<bean>" para cada uno ^_^.

Lo siguiente que haremos es declarar nuestro bean "ServicioRemoto" para que el contenedor de Spring lo administre. Para esto declaramos un elemento "<bean>" en nuestro archivo de configuración:

<bean />El elemento "<bean>" es la unidad de configuración más básica en Spring, le dice que cree y administre un objeto.

Debemos darle un identificador a cada uno de nuestros beans dentro de la aplicación; esto no es obligatorio pero si no lo hacemos no podremos distinguir a los beans con los que trabajamos (normalmente NO especificamos un identificador cuando los usamos como beans internos). Estos deben estar identificados de manera única dentro del contenedor que tiene al bean. Un bean por lo regular solo tiene un identificador (aunque hay formas de colocarle más en caso de ser necesario). En esta configuración basada en XML usamos el atributo "id" o el atributo "name" para especificar el identificador del bean. El atributo "id" nos permite especificar exactamente un identificador, esto es porque hace uso del elemento "id" real de la especificación XML, lo que nos permite algunas validaciones extra con parsers normales de XML, sin embargo nos limita al uso de caracteres que son legales en XML para definir este identificador. Si necesitamos usar algún carácter especial como identificador, o si queremos indicar algún alias para nuestro bean, podemos usar el atributo "name", separando los alias con una coma (,), un punto y coma (;), o un espacio en blanco.

Page 10: Manual Spring

Para este bean, usaremos el identificador "servicioRemoto":

<bean id="servicioRemoto" />Ahora nuestro bean puede ser referenciado por el nombre de "servicioRemoto".

El último paso que debemos hacer en nuestro archivo de configuración es decirle a Spring de qué tipo es el bean, esto es, la clase de la que queremos que cree los objetos. Para hacer esto usamos el atributo "class". En este caso el bean será de tipo "ejemplos.spring.ioc.beans.ServicioRemoto". Por lo que la configuración de nuestro bean queda de la siguiente forma:

<bean id="servicioRemoto" class="ejemplos.spring.ioc.beans.ServicioRemoto" />Y el archivo de configuración queda así:<?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-3.0.xsd"> <bean id="servicioRemoto" class="ejemplos.spring.ioc.beans.ServicioRemoto" /> </beans>

Ahora que tenemos nuestro archivo de configuración listo ha llegado el momento tan esperado, ahora usaremos uno de los contenedores de Spring para crear nuestros beans. Como había dicho, hay dos tipos de contenedores los " BeanFactory" y los "ApplicationContext". Veremos cómo usar estos dos tipos de contenedores.

1.1 Archivos XML con BeanFactoryComo podemos imaginarnos por su nombre, un BeanFactory es una implementación del patrón de diseño factory. Esto es, una clase cuya responsabilidad es crear beans.

BeanFactory hace más que solo crear objetos. Como un BeanFactory conoce a los objetos que componen nuestra aplicación (los declarados en el archivo de configuración), es capaz de crear las asociaciones entre estos objetos en el momento de ser instanciados. Además, como el BeanFactory tiene el control del ciclo de vida del bean, puede hacer llamadas a métodos personalizados de inicialización y destrucción (si estos métodos están definidos).

Hay muchas implementaciones de la interface "BeanFactory" (supuestamente, porque yo solo conozco una). La más usada (y la única que conozco) es "org.springframework.beans.factory.xml.XmlBeanFactory", el cual carga los beans basándose en la configuración del archivo XML que creamos hace un momento.

Para crear un "XmlBeanFactory" debemos indicar de dónde se leerá este XML, ya que podemos obtenerlo de un stream, de un archivo en nuestro classpath, hasta de un arreglo de bytes. Para esto usamos una instancia de "org.springframework.core.io.Resource".

Spring proporciona varias implementaciones de esta interface:

org.springframework.core.io.ByteArrayResource Permite cargar contenido de un arreglo de bytes. Según la documentación es útil para crear adjuntos de mail de contenido local.

org.springframework.core.io.ClassPathResource Carga la configuración desde un recurso que se encuentre en el classpath de la aplicación. Este es el que normalmente estaremos usando.

org.springframework.core.io.FileSystemResource Carga la configuración desde un archivo situado en cualquier parte de la máquina.

org.springframework.core.io.InputStreamResource Carga la configuración desde un InputStream

org.springframework.web.portlet.context.PortletContextResource Carga la configuración desde un recurso que está disponible en el contexto de un portlet.

org.springframework.web.context.support.ServletContextResource Carga la configuración desde un recurso que está disponible en el contexto de una aplicación web.

org.springframework.core.io.UrlResource Carga la configuración desde un recurso que está disponible en una URL dada, usando la resolución de un protocolo, como "file:" o "classpath:"

org.springframework.core.io.VfsResource Carga la configuración desde un recurso que se encuentra en un sistema de archivos virtual (Virtual file system).

Nosotros usaremos "ClassPathResource" para cargar nuestra configuración, ya que el archivo "applicationContext.xml" que creamos está dentro del classpath de nuestra aplicación (más específicamente en la raíz de la misma). A este "ClassPathResource" le pasamos una cadena que contiene la ruta (dentro del classpath) de nuestro archivo de configuración, de esta forma:

Page 11: Manual Spring

BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));

Ahora que tenemos esta "BeanFactory" ya podremos obtener nuestro bean.

Como una nota importante hay que decir que en el momento en el que creamos nuestro BeanFactory, este lee las definiciones de los beans del archivo indicado, sin embargo los beans NO son creados en ese momento. Los beans se crean hasta el momento que son necesitados (cuando los pedimos en nuestro código).

Ahora, con nuestro "BeanFactory" podemos obtener un bean haciendo uso del identificador que le dimos en el archivo XML, en este caso "servicioRemoto". Para esto usamos el método "getBean" del BeanFactory. A este método podemos (opcionalmente) indicarle la clase que esperamos que devuelva el método (y con esto ahorrarnos un cast ^_^).

ServicioRemoto servicio = beanFactory.getBean("servicioRemoto", ServicioRemoto.class);

En el momento en el que invocamos a "getBean", la fabrica instanciará el bean y establecerá los valores de sus propiedades usando DI. Es aquí cuando comienza el ciclo de vida de un bean en el contenedor de Spring. Veremos este ciclo de vida más adelante.Ahora que ya tenemos una instancia de "ServicioRemoto" podemos invocar sus métodos como lo hacemos normalmente:

System.out.println("El valor es " + servicio.consultaDato());

Todo esto lo hacemos dentro de nuestro método "main", que queda de esta forma:

public static void main(String[] args){ BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml")); ServicioRemoto servicio = beanFactory.getBean("servicioRemoto", ServicioRemoto.class); System.out.println("El valor es " + servicio.consultaDato());}

Si ejecutamos nuestra aplicación obtenemos la siguiente salida:

run:XX-xxx-XXXX XX:XX:XX org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitionsINFO: Loading XML bean definitions from class path resource [applicationContext.xml]El valor es 7BUILD SUCCESSFUL (total time: 0 seconds)

Como podemos ver, obtenemos un mensaje que indica que las definiciones de beans del archivo "applicationContext.xml". Además obtenemos el valor que está regresando nuestro servicio remoto (en mi caso "7").

Todo esto lo logramos sin hacer ni un solo "new". Este ejemplo aunque es muy simple (por no decir tonto ^_^) nos muestra las bases de Spring. Un poco más adelante veremos, en este mismo tutorial, dónde está la verdadera ventaja de Spring (solo confíen en mi y continúen leyendo).

Ahora veremos cómo hacer esto mismo usando un "ApplicationContext".

1.2 - Archivos XML con ApplicationContextUn "BeanFactory" está bien para aplicaciones simples, pero no toma ventaja de todo el poder que nos proporciona el framework de Spring, y como desarrolladores queremos cargar los beans de nuestra aplicación usando el contenedor más avanzado que nos proporciona Spring, el ApplicationContext.

Usar "ApplicationContext" es similar a usar "BeanFactory". Ambos cargan definiciones de beans, cablean los beans, y los envían a quienes se los piden. Pero "ApplicationContext" ofrece más cosas:

Proporciona un medio para resolver mensajes de texto (no, no son SMSs), incluyendo soporte para internacionalización (I18N) de estos mensajes.

Proporciona una manera genérica de cargar archivos de recursos, como imágenes.

Puede publicar eventos a beans que están registrados como listeners.

Debido a que "ApplicationContext" proporciona más funcionalidades, es preferible el uso de este sobre "BeanFactory" en casi todo tipo de aplicaciones, la única excepción es cuando estamos ejecutando una aplicación donde los recursos son escasos, como en un dispositivo móvil.

Aunque hay muchas implementaciones de "ApplicationContext" (de esta si =) ), hay 3 que son las más usadas comúnmente:

Page 12: Manual Spring

org.springframework.context.support.ClassPathXmlApplicationContext : Carga la configuración desde un archivo XML ubicado en el classpath.

org.springframework.context.support.FileSystemXmlApplicationContext : Carga la configuración desde un archivo XML ubicado en cualquier parte del sistema de archivos de la computadora.

org.springframework.web.context.support.XmlWebApplicationContext : Carga la configuración desde un archivo XML ubicado en el contexto de una aplicación web

Hablaremos de "XmlWebApplicationContext" cuando hablemos de aplicaciones web en uno de los siguientes tutoriales.

En este caso usaremos "ClassPathXmlApplicationContext". En este caso el constructor recibe una cadena (o cadenas) indicando la ubicación de este archivo (o archivos):

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");

Con este ApplicationContext creado ahora podemos recuperar beans de la misma forma que lo hicimos cuando usamos el "BeanFactory", usando el método "getBean", ya que "ApplicationContext" es una interface que extiende de "BeanFactory":

ServicioRemoto servicio = applicationContext.getBean("servicioRemoto", ServicioRemoto.class);

Además de la funcionalidad extra, mencionada hace un momento, existe otra diferencia entre "ApplicationContext" y "BeanFactory", y es la manera en la que cargan los beans. Hay que decir que por default todos los beans manejados por Spring son Singletons, esto es, solo existe una instancia de ellos por aplicación. Como dije antes: "BeanFactory" crea los beans al momento que estos son pedidos (con la invocación de "getBean"). "ApplicationContext" trabaja de una forma un poco mejor, ya carga todos los singletons cuando se inicia el contexto, y de esta forma se asegura de que estarán listos en el momento en el que sean solicitados.

Ahora que tenemos nuestra instancia de "ServicioRemoto", proporcionada por Spring, podemos hacer uso de sus métodos de forma normal:

System.out.println("El valor es " + servicio.consultaDato());Finalmente, nuestro método "main" queda de la siguiente forma:

public static void main(String[] args){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); ServicioRemoto servicio = applicationContext.getBean("servicioRemoto", ServicioRemoto.class); System.out.println("El valor es " + servicio.consultaDato());}

Si ejecutamos nuestra aplicación, obtendremos una salida similar a esta:

run:XX/xxx/XXXX XX:XX:XX PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitionsINFO: Loading XML bean definitions from class path resource [applicationContext.xml]El valor es 5BUILD SUCCESSFUL (total time: 0 seconds)Como podemos ver, hacer el cambio de "BeanFactory" a "ApplicationContext" nos llevó solamente una línea. Y la configuración en el archivo XML también es bastante pequeña.

Cuando el contenido de nuestro archivo de configuración comience a crecer podemos separar nuestra configuración en varios archivos (por ejemplo, uno por cada capa de nuestra aplicación) y después hacer referencia a estos archivos en un archivo "maestro" usando el elemento "<import>", por ejemplo, en nuestro archivo "applicationContext.xml" podríamos tener:

<beans> <import resource="servicios.xml" /> <import resource="persistencia.xml" /> <import resource="presentacion.xml" /></beans>Esto también puede ayudarnos cuando queramos organizar mejor los beans de nuestra aplicación para agruparlos de alguna manera, y seguir cargando solo un archivo XML de configuración.

Existe una alternativa para el uso de estos archivos XML, y es realizar la configuración a través de las anotaciones que nos proporciona Spring, que es lo que veremos a continuación.

2. Configuración con Anotaciones

Page 13: Manual Spring

El uso de anotaciones nos permite eliminar el tener la definición de nuestros beans en un archivo XML, y esto es "útil" cuando tenemos declarados muchos beans y nuestro archivo se vuelve engorroso (aunque siempre podremos combinar ambas formas de declaración).

Spring proporciona una serie (relativamente grande) de anotaciones con las cuales podemos indicar exactamente cómo queremos que maneje la DI de cada uno de los componentes. Existen en realidad varias formas de manejar estas anotaciones, pero en este momento solo nos centraremos en anotar nuestros beans. Para esto existen tres anotaciones básicas, las cuales se conocen como "estereotipos":

@Repository – Indica que las clases marcadas con esta anotación están relacionada de alguna forma con una capa de persistencia de datos.

@Service – Indica que las clases marcadas con esta anotación está en una capa de servicios o de lógica de negocios.

@Controller – Indica que las clases marcadas con esta anotación son el controlador de una aplicación web.

Las tres anotaciones anteriores extienden de "@Component", la cual indica que la clase es un componente, y por lo tanto son candidatas para ser auto-detectadas cuando usamos una configuración basada en anotaciones.

Veremos dos formas de configuración con anotaciones, en la primera (irónicamente) usaremos un archivo XML para indicar dónde están nuestras clases anotadas. En la segunda forma veremos cómo hacer esto mismo, pero sin necesidad de el archivo XML.

2.1. Anotaciones con Archivo XMLEn esta configuración lo primero que haremos es eliminar el contenido del archivo "applicationContext.xml" que creamos hace un momento, dejándolo de esta forma:

<?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-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

</beans>

Ahora agregaremos un namespace especial a este archivo de configuración que hace referencia a los elementos de configuración del contexto de Spring. En XML un namespace es una forma que se usa para proporcionar elementos con nombres únicos. Spring utiliza estos namespaces para separar conjuntos de etiquetas que están relacionadas y que ofrecen ciertas funcionalidades especiales. Además esto hace que leer y escribir los archivos de configuración sea más fácil. Algunas veces escucharán que se refieren a estos namespaces como "esquemas"

Cada uno de estos namespaces hace referencia a un esquema XML distinto. Así por ejemplo tenemos un namespace para programación orientada a aspectos ("aop"), otro para manejo de configuración de elementos jee ("jee"), otro para manejo de mensajes ("jme"), otro para transacciones ("tx"), etc.

El namespace "context" se encarga de la configuración del "ApplicationContext". Para indicar que haremos uso de este namespace, solo lo agregamos la siguiente línea dentro del elemento "beans" de archivo "applicationContext.xml":

xmlns:context="http://www.springframework.org/schema/context"

Dejándolo de la siguiente forma:

<?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:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

</beans>Ahora indicaremos, usando el namespace "context", que queremos activar la configuración de Spring para que detecte las anotaciones (propias de Spring) en las clases. Para eso usamos el elemento "annotation-config":

<context:annotation-config/>

Ahora debemos indicar en dónde (en qué paquetes) se encuentran las clases que hemos anotado. Si no indicamos este paquete Spring no buscará de forma automática las clases (no sé realmente por qué). Usamos el elemento "component-scan" para indicar, mediante su elemento "base-

Page 14: Manual Spring

package", el paquete raíz en el que se encuentran nuestros beans anotados. Para este ejemplo el paquete raíz es " ejemplos.spring.ioc.beans". Por lo que el elemento queda de la siguiente forma:

<context:component-scan base-package="ejemplos.spring.ioc.beans" />

Y el archivo de configuración así:

<?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:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

<context:annotation-config/> <context:component-scan base-package="ejemplos.spring.ioc.beans" />

</beans>

Como vemos no hemos definido ni un solo bean en el archivo, así que ahora deberemos indicarlo directamente en las clases que se convertirán en nuestros beans (que en ese caso solo es "ServicioRemoto". Para indicar esto usaremos las anotaciones que mencioné antes ("@Repository", "@Service", "@Controller"). Como "ServicioRemoto" pertenece a la capa de servicios, lo mercaremos con la anotación "@Service", de esta forma:

@Servicepublic class ServicioRemoto{}

Si recuerdan, hacemos referencia a los beans mediante su identificador. Pero "ServicioRemoto" no tiene indicado ninguno. Spring asigna un nombre por default a sus beans, que es el nombre de la case pero con la primer letra en minúscula. Así que en este caso el nombre del bean será "servicioRemoto". Si quisiéramos darle a nuestro bean un nombre distinto podemos hacerlo mediante el atributo "value" de la anotación "@Service", de la siguiente forma:

@Service(value="servicioRemoto")Por lo que nuestra clase "ServicioRemoto" queda de la siguiente forma:

@Service(value="servicioRemoto")public class ServicioRemoto{ public int consultaDato() { return (int) (Math.random() * 10.0); }}

No hay que hacerle ningún cambio al código de la clase "Main". Seguimos obteniendo una instancia de "ApplicationContext" (o de "BeanFactory") y le indicamos la ubicación del archivo que contiene la configuración de Spring, que en este caso es "applicationContext.xml". De la misma forma seguimos obteniendo el bean "servicioRemoto" invocando al método "getBean" del objeto "ApplicationContext". Finalmente llamamos al método "consultaDato" del bean "servicioRemoto". Nuestro Método "main" queda de la siguiente forma:

public static void main(String[] args){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); ServicioRemoto servicioRemoto = applicationContext.getBean("servicioRemoto", ServicioRemoto.class);

System.out.println("El valor es: " + servicioRemoto.consultaDato());}Si ejecutamos nuestra aplicación veremos la siguiente salida en la consola:

run:XX/xx/XXXX XX:XX:XX PM org.springframework.context.support.AbstractApplicationContext prepareRefreshINFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@19c26f5: startup date [Xxx Xxx XX XX:XX:XX CST XXXX]; root of context hierarchyXX/xx/XXXX XX:XX:XX PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions

Page 15: Manual Spring

INFO: Loading XML bean definitions from class path resource [applicationContext.xml]XX/xx/XXXX XX:XX:XX PM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletonsINFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@5329c5: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,servicioRemoto]; root of factory hierarchyEl valor es: 3BUILD SUCCESSFUL (total time: 0 seconds)

Podemos ver que ahora hay más información en la consola pero que finalmente nuestro código se ejecuta y obtenemos la salida esperada. Por lo que nuestra aplicación ha quedado configurada de forma adecuada ^_^.

Como vemos, usando anotaciones, nuestro archivo de configuración ha quedado aún más pequeño. Sin embargo, aún seguimos dependiendo de un archivo XML para que nuestra aplicación funcione correctamente. Ahora veremos cómo configurar nuestra aplicación, pero sin depender de ningún XML.2.2. Anotaciones sin Archivo XML

Como para esta última configuración no usaremos el archivo "applicationContext.xml" podemos eliminarlo de la aplicación. Nuevamente usaremos la clase "ServicioRemoto" con la anotación "@Service", de esta forma:

@Service(value="servicioRemoto")public class ServicioRemoto

Para esta configuración, el único cambio necesario es en el código que crea el objeto "ApplicationContext" (en el método "main"). En este caso necesitamos un objeto especial ya que, como no existe un archivo XML, debemos indicar cuáles son las clases anotadas (o el paquete que las contiene).El objeto "especial" que necesitamos crear es una implementación de "ApplicationContext" (y por lo tanto de "BeanFactory"), la clase "AnnotationConfigApplicationContext". Esta clase tiene dos constructores que nos interesan; el primero recibe una lista, separada por comas, de las clases que tenemos anotadas con @Component (o alguna de sus subclases), como es el caso de "ServicioRemoto". Por lo que crearíamos un nuevo objeto de esta clase de la siguiente manera:

new AnnotationConfigApplicationContext(ServicioRemoto.class);

Esto es muy cómodo si solo tenemos dos o tres clases anotadas, pero ¿qué pasa en el caso de que tengamos más? Bueno, en este caso podemos usar el segundo constructor de "AnnotationConfigApplicationContext" en el cual indicamos el paquete (o paquetes) raíz en el que se encuentran las clases anotadas, como en el caso del elemento "context:component-scan" del archivo XML, de la siguiente forma:

new AnnotationConfigApplicationContext("ejemplos.spring.ioc.beans");

Con este cambio nuestro método "main" queda de la siguiente forma:

public static void main(String[] args){ ApplicationContext applicationContext = new AnnotationConfigApplicationContext("ejemplos.spring.ioc.beans"); ServicioRemoto servicio = applicationContext.getBean("servicioRemoto", ServicioRemoto.class); System.out.println("El valor es " + servicio.consultaDato());}

Si ejecutamos nuestra aplicación veremos la siguiente salida en la consola:

run:XX-xxx-XXXX XX:XX:XX org.springframework.context.support.AbstractApplicationContext prepareRefreshINFO: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@d1e604: startup date [Xxx Xxx XX XX:XX:XX CST XXXX]; root of context hierarchyXX-xxx-XXXX XX:XX:XX org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletonsINFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@186d4c1: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,servicioRemoto]; root of factory hierarchyEl valor es 4BUILD SUCCESSFUL (total time: 0 seconds)

Page 16: Manual Spring

Que como podemos observar indica que todo quedó configurado correctamente ^_^.

De esta forma podemos declarar todas las dependencias y la configuración (casi toda) de nuestros beans directamente en las clases. Esta manera de configuración de beans, aunque es más cómoda y fácil para nosotros como desarrolladores al momento de estar escribiendo el código, es mucho menos clara y (para ser sinceros) no nos permite usar todo el poder que Spring nos proporciona. Es por esto que en estos tutoriales nos centraremos más en cómo hacer las cosas usando el archivo de configuración, haciendo las respectivas indicaciones para hacer lo mismo con anotaciones (en caso de que sea posible hacerlo).

Ahora que hemos visto 4 formas distintas de configurar el contenedor de beans de Spring para indicarle cuales son los beans que debe manejar, veremos algunos detalles un poco más interesantes de este framework con respecto a la inyección de dependencias.

Inyección de Dependencias

Una aplicación empresarial típica no consiste de un solo objeto o bean (como en el caso de nuestra prueba con "ServicioRemoto"). Aún la aplicación más simple tiene unos cuantos objetos que trabajan juntos para realizar sus procesos de una manera coherente. Como ya había dicho antes, Spring nos proporciona la forma de unir estos objetos sin tener que indicarlo de manera explícita en el código. Ahora veremos (por fin ^_^) como hacer esto de una manera rápida y sencilla.

Cuando usamos el mecanismo de DI de Spring, el código es más limpio, y está desacoplado de una forma más efectiva. El objeto no busca sus dependencias, de hecho ni siquiera conoce la ubicación o clase de su dependencia. Por esta misma razón, las clases se hacen más fáciles de probar, en particular cuando las dependencias las definimos como interfaces o clases abstractas, ya que podemos usar objetos mock (que veremos en algún otro tutorial sobre pruebas) para realizar pruebas.

En Spring, la DI existe en dos formas principales:

Inyección de dependencia basada en constructor Inyección de dependencia basada en setter

Ahora veremos ambas formas de inyección de dependencia y entraremos en detalle de cada una de ellas. Pero para esto, modificaremos un poco nuestra clase "ServicioRemoto" para que haga uso de un par de dependencias y por lo tanto la cosa se vuelva más interesante :D.

Lo primero que haremos es crear una nueva interface llamada "Proceso". Esta interface representará... bueno el proceso que se realizará dentro del servicio remoto. Tendrá un método llamado "ejecuta()", que será el que se encargará de realizar el proceso. Colocamos esta interface en el paquete de nuestros beans. "Proceso" queda de la siguiente forma:

public interface Proceso { Object ejecuta();}

Como vemos, "ejecuta" regresa un "Object". Este irá cambiando de acuerdo a las implementaciones que realicemos de esta interface, de las cuales por cierto haremos 3. La primer implementación será la clase "Calculo". La cual solo regresará un número al azar entre 0 y 100 (que es lo que ya actualmente está haciendo la clase "ServicioRemoto"), quedando de la siguiente forma:

public class Calculo implements Proceso{ public Object ejecuta() { return (int)(Math.random()*100.0); }}

La siguiente implementación de "Proceso" se llamará "Concatenacion" y simplemente concatenerá las cadenas "Hola " y "mundo" y nos regresará esta cadena. La clase queda de la siguiente forma:

public class Concatenacion implements Proceso{ public Object ejecuta() { return new StringBuilder().append("Hola ").append(" mundo"); }}

La tercer y última implementación de "Proceso" se llamará "Ordenamiento" y lo que hará es regresar una lista de enteros de forma ordenada. La clase "Ordenamiento" queda de la siguiente forma:

Page 17: Manual Spring

public class Ordenamiento implements Proceso{ public Object ejecuta() { List< Integer> listaEnteros = new ArrayList<Integer>();

listaEnteros.add(9); listaEnteros.add(3); listaEnteros.add(1); listaEnteros.add(6); listaEnteros.add(5); listaEnteros.add(10);

Collections.sort(listaEnteros);

return listaEnteros; }}

Ahora que tenemos nuestros tres "Procesos" modifiquemos un poco nuestra clase "ServicioRemoto" para que en vez de ser él quien realice la funcionalidad, delegue este trabajo a una de las implementaciones de "Proceso". "ServicioRemoto" podrá recibir el Proceso que ejecutará de dos formas, la primera es mediante su constructor, y la segunda es pasando este Proceso mediante un setter.

Como ahora "ServicioRemoto" delegará la ejecución de los Procesos a otras clases, su única función será invocar el método "ejecuta" del Proceso correspondiente, y como estos regresan Objects, cambiará su tipo de retorno.

Con las modificaciones hechas, la clase "ServicioRemoto" queda de la siguiente forma:

public class ServicioRemoto{ private Proceso proceso; public ServicioRemoto() { }

public ServicioRemoto(Proceso proceso) { this.proceso = proceso; } public Object consultaDato() { return proceso.ejecuta(); }}

Ahora sí, veremos cómo hacer que Spring inyecte estas dependencias de forma automática, haciendo uso de sus dos formas de DI.

3. Inyección de Dependencia basada en Constructor

En este tipo de inyección de dependencia, el contenedor invoca a un constructor de la clase, que recibe cierto número de argumentos, cada uno de los cuales representa una dependencia.

Spring invocará al constructor correspondiente dependiendo del número y tipo de dependencias que pasemos, ya sea a través de los archivos de configuración en XML (que será la primera forma que veremos) o haciendo uso de anotaciones.

3.1. Inyección de Dependencia basada en Constructor con Archivos XMLLo primero que haremos es indicar, en el archivo de configuración "applicationContext.xml", que usaremos un bean llamado "proceso", que en este caso será de tipo "Ordenamiento":

<bean id="proceso" class="ejemplos.spring.ioc.beans.Ordenamiento" />Ahora modificaremos la declaración actual de nuestro bean "servicioRemoto" que actualmente está de esta forma:

<bean id="servicioRemoto" class="ejemplos.spring.ioc.beans.ServicioRemoto" />

Lo primero que haremos es que en vez de que el elemento se auto-cierre, agregar una etiqueta de cierre, esto es porque agregaremos un contenido en el cuerpo de la etiqueta:

Page 18: Manual Spring

<bean id="servicioRemoto" class="ejemplos.spring.ioc.beans.ServicioRemoto"> </bean>

Para indicar que haremos inyección de dependencia basada en constructor, usamos el elemento "<constructor-arg>". Si no indicamos este elemento se usa el constructor por default de la clase (como en los ejemplos anteriores), pero si se usa, Spring buscará un constructor que reciba el tipo de objeto indicado en él. Tenemos dos maneras de usar este elemento, la primera es indicando el valor que pasaremos al constructor (útil para "constantes") usando el atributo "value". Por ejemplo, si "ServicioRemoto" tuviera un constructor que recibiera una cadena, podríamos usar "<constructor-arg>" de la siguiente forma:

<constructor-arg value="5" />

Pero como no es el caso, usaremos la otra forma de "<constructor-arg>" que recibe una referencia a otro bean declarado en el archivo de configuración, usando su elemento "ref", de esta forma:

<constructor-arg ref="proceso" />

En "ref" indicamos el identificador del bean al que haremos referencia, en este caso es el bean "proceso" que creamos antes. La declaración del bean "servicioRemoto" queda de la siguiente forma:

<bean id="servicioRemoto" class="ejemplos.spring.ioc.beans.ServicioRemoto"> <constructor-arg ref="proceso" /> </bean>

¿Recuerdan que anteriormente dijimos que también podían declararse beans internos para usarse dentro de otras declaraciones? Pues aquí podemos hacerlo para ahorrarnos la declaración del bean "proceso", de esta forma:

<bean id="servicioRemoto" class="ejemplos.spring.ioc.beans.ServicioRemoto" > <constructor-arg> <bean class="ejemplos.spring.ioc.beans.Ordenamiento" /> </constructor-arg></bean>

Pero para hacer las cosas más claras lo dejaremos declarado como lo teníamos ^_^. Nuestro archivo de configuración queda de la siguiente forma:

<?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-3.0.xsd">

<bean id="proceso" class="ejemplos.spring.ioc.beans.Ordenamiento" /> <bean id="servicioRemoto" class="ejemplos.spring.ioc.beans.ServicioRemoto"> <constructor-arg ref="proceso" /> </bean></beans>

Si nuestro constructor tuviera más de un argumento, bastaría con agregar otro elemento "<constructor-arg>".

Ahora ejecutaremos nuestra aplicación de forma normal. Con el siguiente contenido en el método "main":

public static void main(String[] args){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); ServicioRemoto servicio = applicationContext.getBean("servicioRemoto", ServicioRemoto.class); System.out.println("El valor es " + servicio.consultaDato());}

Que es el mismo código que ya teníamos. Al correr nuestro código obtendremos la siguiente salida (le he quitado algo de la "basura" que genera Spring, para poder concentrarnos en la parte importante):

run:El valor es [1, 3, 5, 6, 9, 10]BUILD SUCCESSFUL (total time: 0 seconds)

Como vemos, hemos obtenido la salida esperada, sin tener que escribir ninguna línea de código para resolver las dependencias que usa la clase "ServicioRemoto", que en este caso es el proceso. Es más, "ServicioRemoto" ni siquiera sabe de qué tipo es el "Proceso" que está ejecutando, ni cómo encontrarlo, es el motor de IoC de Spring, que haciendo uso de DI se encarga de crear una instancia de la clase apropiada e inyectarla, en este caso mediante el constructor, a "ServicioRemoto".

Page 19: Manual Spring

Ahora veremos otro de los encantos de Spring. Sin tocar una sola línea de nuestro código (o más bien dicho, sin mover ninguna clase) haremos que "ServicioRemoto" obtenga una dependencia distinta. Para esto cambiaremos la línea:

<bean id="proceso" class="ejemplos.spring.ioc.beans.Ordenamiento" />

Por

<bean id="proceso" class="ejemplos.spring.ioc.beans.Concatenacion" />

Si volvemos a ejecutar nuestra aplicación ahora obtendremos la siguiente salida:

run:El valor es Hola mundoBUILD SUCCESSFUL (total time: 0 seconds)

Ahora se ha inyectado una instancia de "Concatenacion" y "ServicioRemoto" la usa para ejecutar su Proceso. ¿Por qué es útil esto? Pues imagínense el caso que tengamos nuestra aplicación ya compilada y en producción. Si quisiéramos hacer un cambio sería solo cuestión de modificar el archivo XML de configuración (que siempre está en texto plano) y reiniciar la aplicación para que haya un cambio. Esta es una de las ventajas que tiene la configuración en XML sobre las anotaciones.

Pero, para efectos académicos, veamos cómo hacer la inyección por constructor, usando anotaciones.

3.2. Inyección de Dependencia basada en Constructor con AnotacionesEn este caso debemos recordar colocar alguna de las anotaciones que extienden de "@Component" en las clases que implementan nuestra lógica de Procesos. Nosotros, nuevamente, usaremos la anotación "@Service". Las clases que implementan la interface "Proceso" se ven así (omitiendo los cuerpos de las mismas):

@Servicepublic class Calculo implements Proceso{}

@Servicepublic class Concatenacion implements Proceso{}

@Servicepublic class Ordenamiento implements Proceso{}

Ahora modificaremos nuestra clase "ServicioRemoto". Lo primero que haremos es indicarle que queremos que Spring realice el wiring de forma automática para el constructor de "ServicioRemoto" que recibe como parámetro la instancia de "Proceso". La anotación "@Autowired" nos permite hacer esto.

"@Autowired" nos permite marcar un constructor, campo, setter, o método de configuración para ser auto-cableado por el proceso de inyección de dependencia de Spring. Si colocamos esta anotación en un constructor, solo podemos anotar uno de los que tenga la clase. Así que será este constructor el que se invoque, aunque la clase tenga más.

Colocamos la anotación justo sobre el constructor, de esta forma:

@Autowiredpublic ServicioRemoto(Proceso proceso){ this.proceso = proceso;}

Si ejecutamos nuestra aplicación en este momento veremos que en la consola obtenemos el siguiente mensaje de error:

Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'servicioRemoto' defined in file [ejemplos\spring\ioc\beans\ServicioRemoto.class]: Unsatisfied dependency expressed through constructor argument with index 0 of type [ejemplos.spring.ioc.beans.Proceso]: No unique bean of type [ejemplos.spring.ioc.beans.Proceso] is defined: expected single matching bean but found 3: [calculo, concatenacion, ordenamiento];

Page 20: Manual Spring

Lo que este error trata, tan sutilmente, de decirnos, es que Spring no sabe de cuál de las clases que implementa la interface "Proceso" debe inyectar ya que encuentra 3 clases candidatas: "calculo", "concatencacion", y "ordenamiento". Debemos indicar de cuál de estas tres clases queremos que sea inyectada una instancia. Para hacer esto Spring proporciona la anotación "@Qualifier" para poder referirnos a un bean a través de su nombre (que si recordamos, cuando usamos anotaciones, por default es el mismo nombre que la clase, pero iniciando con la primer letra en minúscula). Esta anotación la colocamos directamente dentro de los paréntesis que representan el parámetro al que haremos referencia (esta anotación también puede colocarse a nivel de campo) e indicamos el nombre del bean que queremos usar, que en este caso será "ordenamiento":

@Autowiredpublic ServicioRemoto(@Qualifier("ordenamiento")Proceso proceso){ this.proceso = proceso;}

Finalmente, nuestra clase "ServicioRemoto" queda de la siguiente forma:

@Service(value="servicioRemoto")public class ServicioRemoto{ private Proceso proceso; public ServicioRemoto() { } @Autowired public ServicioRemoto(@Qualifier("ordenamiento")Proceso proceso) { this.proceso = proceso; }

public Object consultaDato() { return proceso.ejecuta(); }}

Ahora sí, si ejecutamos nuestra aplicación veremos la siguiente salida en la consola:

run:El valor es: [1, 3, 5, 6, 9, 10]BUILD SUCCESSFUL (total time: 0 seconds)

Como vemos, también ha resultado bastante simple hacer la inyección basada en constructor mediante anotaciones. De la misma forma en la que, usando el archivo XML, pudimos hacer que nuestra aplicación usara una instancia de una clase o de otra moviendo solo una línea, aquí podemos hacer lo mismo; si cambiamos el valor de la anotación "@Qualifier" de "ordenamiento" a "calculo", de esta forma:

public ServicioRemoto(@Qualifier("calculo")Proceso proceso){}

Obtendremos la siguiente salida:

run:El valor es: 49BUILD SUCCESSFUL (total time: 0 seconds)

Como vemos, todo ha salido perfectamente usando la inyección de dependencia por constructor. Ahora veamos la segunda forma de inyección de dependencia que nos proporciona Spring: inyección de dependencia basada en setter.

4. Inyección de Dependencia basada en Setter

Las propiedades de los JavaBeans, típicamente son declaradas como private y tienen un par de métodos de acceso, un setter y un getter. La inyección de dependencia basada en setter se realiza cuando el contenedor llama a los métodos setter de los beans, para inyectar sus dependencias requeridas.

Para mostrar este ejemplo modificaremos nuestra clase "ServicioRemoto" para que ahora ejecute el Proceso correspondiente un cierto número de veces. Este número de veces será representado por un objeto de tipo Integer, que será insertado haciendo uso de la inyección de dependencia por setter.Agregaremos esta variable, llamada "repeticiones", junto con su setter, de la siguiente forma:

Page 21: Manual Spring

private Integer repeticiones;public void setRepeticiones(Integer repeticiones){ this.repeticiones = repeticiones;}

Ahora modificaremos el método "consultaDato" para colocar un ciclo que ejecutará el método el número de veces indicado, a través de la variable "repeticiones":

public Object consultaDato(){ StringBuilder stringBuilder = new StringBuilder(); for(int i = 0; i < repeticiones; i++) { stringBuilder.append(i + 1).append(" ").append(proceso.ejecuta()).append("\n"); } return stringBuilder.toString();}Ahora haremos que Spring inyecte el valor de la propiedad "repeticiones" haciendo uso de inyección por setter. Primero veremos cómo hacerlo usando el archivo de configuración en XML.

4.1. Inyección de Dependencia Basado en Setter, mediante archivo XMLPara este ejemplo, modificaremos el archivo "applicationContext.xml".

La inyección por setter se realiza utilizando el elemento "<property>" del archivo de configuración. "<property>", al igual que "<constructor-arg>", permite recibir un valor, a través del atributo "value", o usar una referencia a otro bean declarado en el archivo, a través del atributo "ref". En este elemento debemos indicar cuál atributo, o propiedad, es el que queremos que sea inyectado, para esto usamos el atributo "name" del elemento "<property>". Como nosotros pasaremos un valor constante a la propiedad "repeticiones" del "servicioRemoto" usaremos el atributo "value", que estableceremos con un valor de "5" (aunque pueden poner el valor que quieran).

Colocaremos el elemento "<property>" dentro de las etiquetas "<bean>" que definen al bean "servicioRemoto", justo debajo del "<constructor-arg>" que colocamos hace un momento, de esta forma:

<bean id="servicioRemoto" class="ejemplos.spring.ioc.beans.ServicioRemoto" > <constructor-arg ref="proceso" /> <property name="repeticiones" value="5" /></bean>

Este es el único cambio que necesitamos hacer para realizar la inyección de dependencias por setter. Recuerden que si quisieran inyectar un objeto, en vez de una constante, deben declarar el bean (o usar un bean interno), y el atributo "ref" de "<property>".

Si ejecutamos nuestra aplicación, obtenemos la siguiente salida en la consola:

run:El valor es 1 - [1, 3, 5, 6, 9, 10]2 - [1, 3, 5, 6, 9, 10]3 - [1, 3, 5, 6, 9, 10]4 - [1, 3, 5, 6, 9, 10]5 - [1, 3, 5, 6, 9, 10]

Si modifican el valor de "repeticiones", en el archivo de configuración, deberán ver reflejado este cambio, de forma inmediata, en la aplicación.

Ahora veamos cómo hacer esta misma inyección de dependencias, pero usando anotaciones.

4.2. Inyección de Dependencia Basado en Setter, mediante AnotacionesCuando trabajamos con anotaciones, hacer inyección de dependencias se hace de forma distinta si vamos a inyectar una referencia a un objeto que si vamos a inyectar un valor constante.

Si quisiéramos inyectar una referencia a un objeto lo haríamos como vimos en el caso de la DI basada en constructor (usando las anotaciones "@Autowired" y "@Qualifier"). Cuando vamos a inyectar un valor constante usamos la anotación "@Value" e indicamos en su atributo "value" el valor que inyectaremos.

En la DI basada en setter, podemos colocar estas anotaciones en dos lugares. El primero es directamente sobre el setter del atributo que vamos a establecer, en el caso de "repeticiones" sería de esta forma:

@Value(value="5")public void setRepeticiones(Integer repeticiones){

Page 22: Manual Spring

this.repeticiones = repeticiones;}

El segundo es directamente en el atributo que queremos establecer, de esta forma:

@Value(value="5") private Integer repeticiones;

Yo usaré esta segunda forma, ya que me parece un poco más clara que la anterior. Al final, nuestra clase " ServicioRemoto" anotada queda de la siguiente forma:

@Service(value="servicioRemoto")public class ServicioRemoto{ private Proceso proceso; @Value(value="5") private Integer repeticiones;

public ServicioRemoto() { }

@Autowired public ServicioRemoto(@Qualifier("calculo")Proceso proceso) { this.proceso = proceso; }

public Object consultaDato() { StringBuilder stringBuilder = new StringBuilder(); for(int i = 0; i < repeticiones; i++) { stringBuilder.append(i + 1).append(" - ").append(proceso.ejecuta()).append("\n"); } return stringBuilder.toString(); } public void setRepeticiones(Integer repeticiones) { this.repeticiones = repeticiones; }}

Si ejecutamos este código veremos la siguiente salida en consola:

El valor es: 1 - 82 - 643 - 504 - 455 – 50

Como vemos, nuevamente ha sido muy fácil modificar nuestra aplicación para agregar este tipo de inyección de dependencias.

¿Cuál Forma de Inyección de Dependencias Elegir?

La elección entre cuál forma de inyección de dependencia, por setter o por constructor, elegir no es fácil, ya que ambas tienen sus ventajas y sus desventajas.

Spring nos permite elegir el método que más nos guste (o nos convenga), además de que nos permite mezclar ambas formas, como lo hemos hecho en el ejemplo. Sin embargo hay una “regla” o consejo que podemos seguir: usar DI basada en constructor para las dependencias obligatorias y la DI basada en setter para las dependencias opcionales.

El equipo desarrollador de Spring (y yo también) generalmente sugieren usar inyección por setter, porque tener constructores con un número grande de argumentos pueden ser engorrosos, especialmente si algunos de los argumentos son opcionales. Los métodos setter también hacen que los objetos de esas clases sean más fáciles de reconfigurar o re-inyectar después.

Lo mejor es usar el tipo de DI que tenga más sentido para la clase en la que estamos trabajando.

Page 23: Manual Spring

Dejaremos este tutorial aquí. Hemos aprendido a realizar inyección de dependencias de las dos formas que nos proporciona Spring: basada en constructor y basada en setter, y a inyectar referencias a otros objetos y valores constantes, tanto con archivos de configuración en XML como con anotaciones. Solo hemos visto un par de ejemplos muy simples, pero recuerden que usando esta misma lógica pueden extender estos ejemplos para armar aplicaciones grandes.

También es importante recordar que es posible combinar ambas formas de configuración, anotando unos beans, y declarando otros en el archivo de configuración XML.

Espero que el tutorial les sea de utilidad. En los siguientes tutoriales veremos cómo inyectar colecciones en nuestros beans, además del scope y ciclo de vida de los mismos para saber cómo sacar una mayor ventaja de estos.

No olviden dejar sus dudas, comentarios, y sugerencias. Todo es bien recibido ^_^.

Saludos.

Spring 3 - Parte 3: Inyección de Colecciones

En el tutorial anterior vimos cómo hacer la configuración de Spring para poder hacer el cableado o wiring de propiedades simples, ya fuera un valor constante o una referencia a un objeto.

Esto es muy útil cuando esperamos solo una referencia o un valor, ¿pero qué ocurre en el caso en el que necesitemos hacer la inyección de varios objetos? Java proporciona un conjunto de clases que nos permiten manejar conjuntos de objetos. Estas clases son conocidas como colecciones.

En este tutorial aprenderemos cómo realizar la inyección de dependencias en 3 tipos de colecciones ("List", "Set", y "Map") y, adicionalmente, aprenderemos a inyectar propiedades ("properties") y arreglos ("arrays"). Además veremos cómo insertar valores nulos en las propiedades de nuestros beans.

Spring ofrece cinco tipos de elementos para configuración de colecciones. La siguiente tabla muestra estos elementos y una descripción de dónde pueden usarse:

Elemento Útil para<list> Wiring de una lista de valores, que permite duplicados y mantienen un orden.<array> Wiring de un arreglo de objetos, que permite duplicados y mantienen un orden.<set> Wiring de un conjunto de valores, asegurando que no hay duplicados.<map> Wiring de una colección de pares nombre-valor donde el nombre y el valor pueden ser de cualquier tipo.<props> Wiring de una colección de pares nombre-valor donde el nombre y el valor son de tipo String.

Para este ejemplo crearemos una clase a la que le inyectaremos varias colecciones, de los distintos tipos mencionados arriba, y que las recorrerá para poder mostrarlas en pantalla. Algunos de los elementos de las colecciones serán constantes y otros serán referencias a objetos de un tipo "Persona", que crearemos en unos momentos.

Lo primero que haremos es crear un nuevo proyecto en NetBeans. Para esto vamos al Menú "File->New Project...". En la ventana que se abre seleccionamos la categoría "Java" y en el tipo de proyecto "Java Application". Le damos una ubicación y un nombre al proyecto, en mi caso será "SpringColecciones". Nos aseguramos que las opciones "Create Main Class" y "Set as Main Project" estén habilitadas. Presionamos el botón "Finish" y veremos aparecer en el editor nuestra clase "Main".

Agregamos la biblioteca "Spring 3" que creamos en el primer tutorial de la serie. Hacemos clic derecho sobre el nodo "Libraries" del proyecto, en el menú que aparece elegimos la opción "Add Library...". En la ventana que se abre seleccionamos la biblioteca "Spring 3" y presionamos el botón "Add Library". Con esto los archivos de la biblioteca deben agregarse a nuestro proyecto.

Ahora crearemos un nuevo paquete que contendrá a nuestros beans, este paquete se llamará "beans". Hacemos clic derecho en el paquete en el que se encuentra nuestra clase "Main" (que en mi caso es "ejemplos.spring.colecciones") y en el menú que se abre seleccionamos la opción "New -> Java package..." y al nombre del paquete le agregamos "beans". Presionamos el botón "Finish" y con esto aparecerá nuestro nuevo paquete.

Hasta ahora nuestro proyecto debe verse así:

Page 24: Manual Spring

Ahora podemos crear nuestro archivo de configuración XML, "applicationContext.xml", en el paquete raíz (default package). Para esto hacemos clic derecho sobre el nodo "Source Packages" de nuestro proyecto, y en el menú contextual que se abre seleccionamos "new -> Other...". En la ventana que se abre, seleccionamos la categoría "Other" y como tipo de archivo "Spring XML Configuration File". Le damos un nombre al archivo, en mi caso será "applicationContext". Hacemos clic en el botón "Next". En la siguiente pantalla debemos seleccionar los namespaces que queremos que tenga nuestro archivo. No seleccionaremos ninguno. Presionamos el botón "Finish" y con eso aparecerá en nuestro editor el archivo "applicationContext.xml".

Ahora crearemos nuestra clase "Persona", dentro del paquete "beans", cuyas instancias serán inyectadas dentro de las colecciones que crearemos en unos momentos. "Persona" será una clase muy sencilla que solo tendrá 2 atributos: "nombre", de tipo String, y "edad", de tipo int, con sus respectivos setters y getters. Por lo que "Persona" queda de la siguiente forma:

public class Persona{ private String nombre; private int edad; public int getEdad() { return edad; }

public void setEdad(int edad) { this.edad = edad; }

public String getNombre() { return nombre; }

public void setNombre(String nombre) { this.nombre = nombre; }}

También sobre-escribiremos el método "toString" de la clase "Persona". Esto con la finalidad de que al mostrar una de sus instancias, usando "System.out.println()", podamos ver los valores de sus atributos sin llamar a sus métodos getter de forma explícita:

@Overridepublic String toString(){ return new StringBuilder("nombre: ").append(nombre).append(", edad: ").append(edad).toString();}

La clase "Persona" queda de la siguiente forma:

public class Persona{ private String nombre; private int edad; public int getEdad() { return edad; } public void setEdad(int edad) { this.edad = edad;

Page 25: Manual Spring

} public String getNombre() { return nombre; } public void setNombre(String nombre) { this.nombre = nombre; } @Override public String toString() { return new StringBuilder("nombre: ").append(nombre).append(", edad: ").append(edad).toString(); }}

Ahora crearemos una clase llamada "ManejadorColecciones", en el paquete "beans". Esta clase será a la que le inyectaremos las colecciones que declararemos en el archivo de configuración, y que se encargará de mostrar en consola los contenidos de estas colecciones. Esta clase tendrá una variable para cada uno de los tipos de colecciones que inyectaremos (list, arreglo, set, map, y properties), y como haremos inyección por setter (si no saben qué es esto, vean el segundo tutorial de la serie), tendrá un setter para cada una de las propiedades anteriores, de esta forma:

public class ManejadorColecciones{ private List lista; private Persona[] arreglo; private Set conjunto; private Map mapa; private Properties propiedades;

public void setArreglo(Persona[] arreglo) { this.arreglo = arreglo; }

public void setConjunto(Set conjunto) { this.conjunto = conjunto; }

public void setLista(List lista) { this.lista = lista; }

public void setMapa(Map mapa) { this.mapa = mapa; }

public void setPropiedades(Properties propiedades) { this.propiedades = propiedades; }}

Como podemos ver, "ManejadorColecciones" tiene una propiedad de cada uno de los tipos de colecciones. También podemos ver que en este caso no hemos usado "generics" al marcar las colecciones. Esto es porque dentro de la colección pondremos objetos de distintos tipos.

Ahora crearemos un método para mostrar el contenido de cada uno de estas colecciones. Estos métodos serán muy simples, solo las recorreremos en un ciclo for, y las mostraremos con un "System.out.println". Este es el método para mostrar la lista:

public void muestraLista(){ System.out.println("---Mostrando lista---");

for(Object o : lista) { System.out.println(o.getClass() + ": " + o); }}

Este el método para mostrar el arreglo:

public void muestraArreglo(){

Page 26: Manual Spring

System.out.println("---Mostrando arreglo---"); for(Object o : arreglo) { System.out.println(o.getClass() + ": " + o); }}

Este es el método para mostrar el Set:

public void muestraConjunto(){ System.out.println("---Mostrando conjunto---"); for (Iterator it = conjunto.iterator(); it.hasNext();) { Object o = it.next();

System.out.println(o.getClass() + ": " + o); }}

Este es el método para mostrar el Map:

public void muestraMapa(){ System.out.println("---Mostrando mapa---");

for (Iterator it = mapa.keySet().iterator(); it.hasNext();) { Object o = it.next();

System.out.println("[llave] " + o.getClass() + ": " + o + ", [valor]" + mapa.get(o).getClass() + ": " + mapa.get(o) ); }}

Y, finalmente, este es el método para mostrar el objeto "Properties":

public void muestraPropiedades(){ System.out.println("\n---Mostrando propiedades---"); for (Iterator it = propiedades.keySet().iterator(); it.hasNext();) { Object o = it.next(); System.out.println("[llave] " + o.getClass() + ": " + o + ", [valor]" + propiedades.get(o).getClass() + ": " + propiedades.get(o) ); }}

Como podemos ver, los 5 métodos anteriores son muy sencillos, por lo que no necesitan más explicación. La clase "ManejadorColecciones" queda finalmente de la siguiente manera:

public class ManejadorColecciones{ private List lista; private Persona[] arreglo; private Set conjunto; private Map mapa; private Properties propiedades; public void muestraLista() { System.out.println("\n---Mostrando lista---");

for (Object o : lista) { System.out.println(o.getClass() + ": " + o); } }

public void muestraArreglo() { System.out.println("\n---Mostrando arreglo---");

for (Object o : arreglo) { System.out.println(o.getClass() + ": " + o); }

Page 27: Manual Spring

} public void muestraConjunto() { System.out.println("\n---Mostrando conjunto---"); for (Iterator it = conjunto.iterator(); it.hasNext();) { Object o = it.next(); System.out.println(o.getClass() + ": " + o); } } public void muestraMapa() { System.out.println("\n---Mostrando mapa---"); for (Iterator it = mapa.keySet().iterator(); it.hasNext();) { Object o = it.next(); System.out.println("[llave] " + o.getClass() + ": " + o + ", [valor]" + mapa.get(o).getClass() + ": " + mapa.get(o) ); } } public void muestraPropiedades() { System.out.println("\n---Mostrando propiedades---");

for (Iterator it = propiedades.keySet().iterator(); it.hasNext();) { Object o = it.next();

System.out.println("[llave] " + o.getClass() + ": " + o + ", [valor]" + propiedades.get(o).getClass() + ": " + propiedades.get(o) ); } } public void setArreglo(Persona[] arreglo) { this.arreglo = arreglo; } public void setConjunto(Set conjunto) { this.conjunto = conjunto; } public void setLista(List lista) { this.lista = lista; } public void setMapa(Map mapa) { this.mapa = mapa; } public void setPropiedades(Properties propiedades) { this.propiedades = propiedades; }}

Ahora regresemos al archivo de "applicationContext.xml" para configurar la inyección de todos los objetos que se inyectarán a "ManejadorBeans". Lo primero que haremos es declarar dos objetos de tipo "Persona":

<bean id="persona1" class="ejemplos.spring.colecciones.beans.Persona"></bean>

<bean id="persona2" class="ejemplos.spring.colecciones.beans.Persona"></bean>

Ahora, haciendo uso de la inyección por setter, estableceremos los valores de los atributos "nombre" y "edad" de estos dos beans. Pueden colocar los valores que quieran:

<bean id="persona1" class="ejemplos.spring.colecciones.beans.Persona"> <property name="nombre" value="Persona1" /> <property name="edad" value="26" /></bean>

<bean id="persona2" class="ejemplos.spring.colecciones.beans.Persona"> <property name="nombre" value="Persona2" /> <property name="edad" value="62" /></bean>

Page 28: Manual Spring

Lo siguiente que debemos hacer es declarar un bean de tipo "ManejadorColecciones" que será el que usaremos en nuestro método "main", y al que le inyectaremos, en unos momentos más, las colecciones:

<bean id="manejador" class="ejemplos.spring.colecciones.beans.ManejadorColecciones"></bean>

Comencemos con la inyección de cada una de nuestras colecciones.

En general, todas nuestras colecciones (incluyendo los arreglos) se declaran como beans internos, así que no tendrán un identificador propio. Comencemos inyectando las listas:

Inyección de Listas

Para declarar una lista en el archivo de configuración usamos el elemento "<list>" en la propiedad correspondiente, que en este caso se llama "lista":

<property name="lista"> <list> </list></property>

Cuando queremos declarar una referencia a un bean dentro de una lista, usamos el elemento "<ref>", y dentro de este usamos el atributo "bean" para indicar el identificador del bean al que queremos hacer referencia, en nuestro caso serán los beans "persona1" y "persona2":

<property name="lista"> <list> <ref bean="persona1" /> <ref bean="persona2" /> </list></property>

Cuando queremos declarar algún valor primitivo, como un int o un String (ya lo sé, los String son objetos, no tipos primitivos, pero en Java los manejamos como si lo fueran u_u!), usamos el elemento "<value>". Dentro de ese elemento declaramos el valor que usaremos, de esta forma:

<value>Cadena</value><value>5</value>

Si lo dejamos de la forma anterior, Spring entenderá ambos valores como si fueran Strings, sin embargo, si queremos declarar algún valor como otro tipo, como en el caso del "5" que queremos que sea un entero, usamos el atributo "type" del elemento "<value>" para indicar el tipo del valor. En este caso será de tipo "java.lang.Integer":

<value type="java.lang.Integer">5</value>

La declaración de nuestra lista queda de la siguiente forma:

<property name="lista"> <list> <ref bean="persona1" /> <ref bean="persona2" /> <value>Cadena</value> <value type="java.lang.Integer">5</value> </list></property>

Si recordamos, en una lista el orden en el que se encuentran los elementos dentro de la colección es importante. Cuando inyectamos una lista a un bean, los elementos de la misma serán inyectados en el orden en el que los declaramos en el archivo de configuración. Esto quiere decir que en este caso, el orden de los elementos en la lista será: "persona1", "persona2", "Cadena", "5".

Ahora veremos cómo inyectar los elementos para la siguiente "colección", los arreglos.

Inyección de Arreglos

Ahora inyectaremos un arreglo a nuestro bean "ManejadorColecciones". Spring proporciona el elemento "<array>" para declarar un arreglo dentro del archivo de configuración:

<property name="arreglo"> <array> </array></property>

Al igual que con las listas, podemos declarar una referencia a un bean que se agregará a nuestro arreglo, usando el elemento "<ref>", o podemos inyectar un valor "primitivo" usando el elemento "<value>".

Page 29: Manual Spring

Como en nuestra clase "ManejadorColecciones" tenemos declarada la variable "arreglo" de tipo "Persona[]", solo podemos pasar referencias de tipo "Persona" al arreglo que inyectaremos. Para esto pasaremos las referencias a los beans "persona1" y "persona2" que ya tenemos declarados:

<array> <ref bean="persona2" /> <ref bean="persona1" /></array>

Agregaremos una Personas más a nuestro arreglo, pero en este caso en forma de bean interno:

<array> <ref bean="persona2" /> <ref bean="persona1" /> <bean class="ejemplos.spring.colecciones.beans.Persona"> <property name="nombre" value="Persona Nueva" /> <property name="edad" value="10" /> </bean> </array>

Con esto ya tenemos declarado un arreglo de 3 elementos, de tipo "Persona", que serán inyectados en el momento de que nuestro bean sea creado.

La declaración del arreglo queda así:

<property name="arreglo"> <array> <ref bean="persona2" /> <ref bean="persona1" /> <bean class="ejemplos.spring.colecciones.beans.Persona"> <property name="nombre" value="Persona Nueva" /> <property name="edad" value="10" /> </bean> </array></property>

En los arreglos, igual que en las listas, el orden en el que se encuentran los elementos dentro de la colección es importante. En este caso también, los elementos del arreglo serán inyectados en el orden en el que los declaramos en el archivo de configuración. Esto quiere decir que en este caso, el orden de los elementos en la lista será: "persona2", "persona1", "Persona Interna".

Pasemos a ver cómo inyectar el siguiente tipo de colección, los conjuntos o Sets.

Inyección de Sets

Cuando queremos inyectar un Set a un bean, usamos el elemento "<set>" del archivo de configuración:

<property name="conjunto"> <set> </set></property>

Igual que en los casos anteriores, podemos pasar referencias a otros beans, usando en elemento "<ref>", o valores "primitivos", usando el elemento "<value>". Nosotros pasaremos cuatro referencias a 2 beans: 2 referencias al bean "persona1" y 2 referencias al bean "persona2", de la siguiente forma:

<set> <ref bean="persona1" /> <ref bean="persona2" /> <ref bean="persona2" /> <ref bean="persona1" /></set>

¿Por qué hacemos esto? Bueno, esto lo hemos hecho porque los Sets son un tipo especial de colección que NO permite tener elementos repetidos. Esto quiere decir que aunque hayamos pasado 4 referencias, como en realidad solo estamos pasando 2 objetos (y 2 repetidos) la colección al final solo tendrá 2 objetos, esto lo veremos al momento de mostrar los valores en la consola.

Al final, la declaración de nuestro conjunto queda de la siguiente forma:

<property name="conjunto"> <set> <ref bean="persona1" /> <ref bean="persona2" /> <ref bean="persona2" /> <ref bean="persona1" /> </set>

Page 30: Manual Spring

</property>

Ahora veamos cómo inyectar nuestra cuarta colección, los mapas.

Inyección de Maps

Para hacer inyección de Maps, Spring proporciona el elemento… así es, adivinaron, el elemento "<map>":

<property name="mapa"> <map> </map></property>

Recordemos que un mapa es una colección que tiene elementos de tipo llave-valor, donde tanto la llave como el valor pueden ser de cualquier tipo. Para declarar los miembros del mapa, usamos el elemento "<entry>". Dentro de este elemento podemos indicar dos tipos de llaves, una llave que es un valor, usando el atributo "key", que es tomado como String, o una llave que es una referencia a un bean, usando el atributo "key-ref". De la misma forma, podemos declarar dos tipos de valores, unos que son referencias a otros beans, usando "value-ref", y otros que son valores que son tomados como String, usando "value". La siguiente tabla da el nombre y una descripción de estos atributos:

Atributo Propósitokey Especifica la llave de la entrada del mapa como un String.key-ref Especifica la llave de la entrada del mapa como una referencia a un bean.Value Especifica el valor de la entrada del mapa como un String.value-ref Especifica el valor de la entrada del mapa como una referencia a un bean.

Nosotros inyectaremos 4 entradas a nuestro mapa. Trataremos de combinar varios tipos de objetos, tanto de beans como de valores. La primer entrada tendrá como llave una cadena y como valor una referencia a uno de nuestros beans de tipo "Persona":

<entry key="persona1" value-ref="persona1" />

La siguiente entrada será al revés, la llave será una referencia a uno de los beans de tipo "Persona" y el valor será una cadena:

<entry key-ref="persona2" value="otra persona" />

La tercer entrada tendrá como llave una cadena y como valor otra cadena:

<entry key="sin persona" value="no personas" />

La cuarta y última entrada tendrá como llave una referencia a un objeto "Persona" y como valor otra referencia a un objeto "Persona":

<entry key-ref="persona1" value-ref="persona2" />

La declaración final de nuestro mapa queda de la siguiente forma:

<property name="mapa"> <map> <entry key="persona1" value-ref="persona1" /> <entry key-ref="persona2" value="otra persona" /> <entry key="sin persona" value="no personas" /> <entry key-ref="persona1" value-ref="persona2" /> </map></property>

Veremos cómo configurar nuestra última "colección", las propiedades.

Inyección de Propiedades

Las propiedades son muy similares a los mapas; son colecciones donde se tienen pares llave-valor. De hecho la clase "Properties" implementa la interface "Map". La diferencia principal es que, mientras que en los mapas la llave y el valor pueden ser de cualquier tipo, en las propiedades ambos elementos solo pueden ser de tipo String.

Spring proporciona el elemento "<props>" para declarar atributos de tipo "java.util.Properties".

<property name="propiedades"> <props> </props></property>

Para agregar cada uno de los miembros de las propiedades tenemos dos formas. La primera es usando el elemento "<prop>", en el cual indicamos la

Page 31: Manual Spring

llave usando el atributo "key", y el valor lo colocamos como contenido del elemento. Por ejemplo, si quisiéramos declarar cuatro propiedades: nombre, país, sitio, y facebook, lo haríamos de la siguiente manera:

<prop key="nombre">Alex</prop> <prop key="pais">Mexico</prop> <prop key="sitio">http://javatutoriales.blogspot.com/</prop> <prop key="facebook">http://www.facebook.com/pages/Java-Tutoriales/121935157864634</prop>

La segunda forma es aprovechando el soporte que nos proporciona Spring para manejo de propiedades usando su "PropertyEditor" integrado para escribir directamente las llaves y valores de las propiedades, como cuerpo del elemento "<value>" que colocamos directamente dentro de la propiedad (sin hacer uso del elemento "<props>"), de esta forma:

<property name="propiedades"> <value> nombre=Alex pais=Mexico sitio=http://javatutoriales.blogspot.com facebook=http://www.facebook.com/pages/Java-Tutoriales/121935157864634 </value></property>

Y esto es todo, ya tenemos configuradas todas las colecciones de nuestro ejemplo. Al final el archivo de configuración, "applicationContext.xml", queda de la siguiente forma:

<?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-3.0.xsd">

<bean id="persona1" class="ejemplos.spring.colecciones.beans.Persona"> <property name="nombre" value="Persona1" /> <property name="edad" value="26" /> </bean>

<bean id="persona2" class="ejemplos.spring.colecciones.beans.Persona"> <property name="nombre" value="Persona2" /> <property name="edad" value="62" /> </bean>

<bean id="manejador" class="ejemplos.spring.colecciones.beans.ManejadorColecciones"> <property name="lista"> <list> <ref bean="persona1" /> <ref bean="persona2" /> <value>Cadena</value> <value type="java.lang.Integer">5</value> </list> </property> <property name="arreglo"> <array> <ref bean="persona2" /> <ref bean="persona1" /> <bean class="ejemplos.spring.colecciones.beans.Persona"> <property name="nombre" value="Persona Nueva" /> <property name="edad" value="10" /> </bean> </array> </property> <property name="conjunto"> <set> <ref bean="persona1" /> <ref bean="persona2" /> <ref bean="persona2" /> <ref bean="persona1" /> </set> </property> <property name="mapa"> <map> <entry key="persona1" value-ref="persona1" /> <entry key-ref="persona2" value="otra persona" /> <entry key="sin persona" value="no personas" /> <entry key-ref="persona1" value-ref="persona2" /> </map>

Page 32: Manual Spring

</property> <property name="propiedades"> <props> <prop key="nombre">Alex</prop> <prop key="pais">Mexico</prop> <prop key="sitio">http://javatutoriales.blogspot.com/</prop> <prop key="facebook">http://www.facebook.com/pages/Java-Tutoriales/121935157864634</prop> </props> </property> </bean></beans>

Lo siguiente que debemos hacer es, en nuestro método "main", crear un objeto de tipo "ApplicationContext" para poder obtener nuestros beans. Para ver más a detalle cómo hacer esto, pueden consultar el segundo tutorial de la serie, aquí lo resumiré todo diciendo que crearemos una instancia de "ClassPathXmlApplicationContext" y pasaremos como parámetro de su constructor la ubicación del archivo de configuración dentro del classpath, de la siguiente forma:

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");

Lo siguiente que haremos es obtener una referencia al bean "manejador", declarado en el archivo de configuración, usando el método "getBean" de nuestro objeto "ApplicationContext":

ManejadorColecciones manejador = applicationContext.getBean("manejador", ManejadorColecciones.class);

Para terminar invocaremos, en el objeto "manejador", cada uno de los métodos para mostrar los valores de las colecciones, de la siguiente forma:

manejador.muestraLista(); manejador.muestraArreglo(); manejador.muestraConjunto(); manejador.muestraMapa(); manejador.muestraPropiedades();

El método "main" queda de la siguiente forma:

public static void main(String[] args){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");

ManejadorColecciones manejador = applicationContext.getBean("manejador", ManejadorColecciones.class);

manejador.muestraLista(); manejador.muestraArreglo(); manejador.muestraConjunto(); manejador.muestraMapa(); manejador.muestraPropiedades();}

Al ejecutar nuestra aplicación obtenemos la siguiente salida:

---Mostrando lista---class ejemplos.spring.colecciones.beans.Persona: nombre: Persona1, edad: 26class ejemplos.spring.colecciones.beans.Persona: nombre: Persona2, edad: 62class java.lang.String: Cadenaclass java.lang.Integer: 5

---Mostrando arreglo---class ejemplos.spring.colecciones.beans.Persona: nombre: Persona2, edad: 62class ejemplos.spring.colecciones.beans.Persona: nombre: Persona1, edad: 26class ejemplos.spring.colecciones.beans.Persona: nombre: Persona Nueva, edad: 10

---Mostrando conjunto---class ejemplos.spring.colecciones.beans.Persona: nombre: Persona1, edad: 26class ejemplos.spring.colecciones.beans.Persona: nombre: Persona2, edad: 62

---Mostrando mapa---[llave] class java.lang.String: persona1, [valor]class ejemplos.spring.colecciones.beans.Persona: nombre: Persona1, edad: 26[llave] class ejemplos.spring.colecciones.beans.Persona: nombre: Persona2, edad: 62, [valor]class java.lang.String: otra persona[llave] class java.lang.String: sin persona, [valor]class java.lang.String: no personas

Page 33: Manual Spring

[llave] class ejemplos.spring.colecciones.beans.Persona: nombre: Persona1, edad: 26, [valor]class ejemplos.spring.colecciones.beans.Persona: nombre: Persona2, edad: 62

---Mostrando propiedades---[llave] class java.lang.String: facebook, [valor]class java.lang.String: http://www.facebook.com/pages/Java-Tutoriales/121935157864634[llave] class java.lang.String: pais, [valor]class java.lang.String: Mexico[llave] class java.lang.String: nombre, [valor]class java.lang.String: Alex[llave] class java.lang.String: sitio, [valor]class java.lang.String: http://javatutoriales.blogspot.com/

Analicemos un poco esta salida.

La primera que obtenemos es la salida de la lista. En esta, podemos comprobar que los elementos de la misma se encuentran en el mismo orden en el que los declaramos en el archivo de configuración. Primero los dos beans de tipo "Persona", después la cadena "Cadena" que declaramos como un valor, y finalmente el valor "5" que declaramos como un objeto de tipo "java.lang.Integer":

En la siguiente parte de la salida, tenemos el valor del arreglo. Aquí nuevamente, los elementos se encuentran en el mismo orden en el que los declaramos en el archivo de configuración. Tenemos primero los dos beans de tipo "Persona", y después el bean interno que declaramos, con el "nombre" de "Persona Nueva" y la "edad" de "10":

Posteriormente está el segmento de la salida correspondiente con el Set. Aquí vemos que aunque en el archivo de configuración declaramos 4 miembros para esta colección, al final en esta solo se insertan 2 objetos, ya que los Sets son colecciones que no permiten duplicados:

La que sigue es la salida de los elementos del mapa. Aquí podemos observar que, efectivamente, tenemos una combinación tanto de llaves como de valores de tipos "Persona" y "String", de la misma forma que en el archivo de configuración:

La última parte de la salida es la que corresponde con las propiedades. En esta, podemos ver que tanto las llaves como los valores son de tipo "String":

Como vemos, nuestras colecciones se han inyectado de forma correcta ^_^.

Tal vez algunos se hayan preguntado ¿y qué ocurre si quiero insertar una propiedad con un valor de "null", o un elemento de las colecciones como "null"? Bueno, responderemos esta pregunta en la última parte del tutorial

Inyectando valores nulos

En casi cada situación, usaremos DI para cablear un valor constante o una referencia a un objeto dentro de una propiedad de un bean. Sin embargo en algunas ocasiones será necesario inyectar un valor null a una propiedad.

Aunque en Java las referencias a objetos son por default nulas, algunas veces asumir esto no es suficiente para lograr nuestros propósitos.

Para establecer una propiedad como "null" usamos el elemento... "<null />". Por ejemplo, si quisiéramos establecer la propiedad "nombre" de un bean de tipo "Persona" como "null", lo declararíamos de la siguiente forma:

<bean id="persona1" class="ejemplos.spring.colecciones.beans.Persona"> <property name="nombre"><null /></property > <property name="edad" value="26" /></bean>

Y eso es todo lo que debemos hacer ^_^.

Hemos llegado al final de este tercer tutorial sobre Spring 3, espero que les sea de utilidad. No olviden dejar sus dudas, comentarios y sugerencias.

Page 34: Manual Spring

Saludos.

Descarga los archivos de este tutorial desde aquí:

domingo, 6 de febrero de 2011

Spring 3 - Parte 4: Ciclo de Vida de los Beans

En los tutoriales anteriores hemos visto cómo trabajar configurar beans, tanto con anotaciones como con archivo de configuración en XML, y cómo cablearlos a las propiedades de otros beans.

Sin embargo Spring nos proporciona mecanismos para controlar la creación de beans y ejecutar ciertas acciones en las distintas etapas del ciclo de vida de nuestros beans.

En este tutorial aprenderemos cómo controlar cuándo deben ser creadas las instancias de los beans y a crearlos a través de un método estático de factoría, además de recibir notificaciones cuando un bean es creado y antes de que sea destruido.

Crearemos un proyecto en el que iremos probando cada uno de los conceptos que veamos. Lo primero que haremos es crear un nuevo proyecto en NetBeans. Para esto vamos al Menú "File->New Project...". En la ventana que se abre seleccionamos la categoría "Java" y en el tipo de proyecto "Java Application". Le damos una ubicación y un nombre al proyecto. Nos aseguramos que las opciones "Create Main Class" y "Set as Main Project" estén habilitadas. Presionamos el botón "Finish" y veremos aparecer en el editor nuestra clase "Main".

Agregamos la biblioteca "Spring3" que creamos en el primer tutorial de la serie. Hacemos clic derecho sobre el nodo "Libraries" del proyecto, en el menú que aparece elegimos la opción "Add Library...". En la ventana que se abre seleccionamos la biblioteca "Spring3" y presionamos el botón "Add Library". Con esto los archivos de la biblioteca deben agregarse a nuestro proyecto.

Ahora crearemos un nuevo paquete que contendrá a nuestros beans, este paquete se llamará " beans". Hacemos clic derecho en el paquete en el que se encuentra nuestra clase "Main" (que en mi caso es "ejemplos.spring.ciclovida") y en el menú que se abre seleccionamos la opción "New -> Java package..." y al nombre del paquete le agregamos "beans". Presionamos el botón "Finish" y con esto aparecerá nuestro nuevo paquete.

El proyecto debe verse así:

Crearemos, en el paquete "beans", una interface llamada "Estudiante" que será la que usaremos para realizar nuestras inyecciones de dependencias y pruebas. Esta interface solo tendrá un método llamado "presentaExamen()" que regresará un entero entre 0 y 10 que representará la calificación del Estudiante en el examen. Esta interface queda de la siguiente forma:

public interface Estudiante{ int presentaExamen();}

También crearemos, en ese mismo paquete, una clase que implemente esta interface. La clase se llamará "Universitario". Su implementación del método "presentaExamen()" simplemente será regresar un número aleatorio entre 0 y 10. Esta clase queda de la siguiente forma:

public class Universitario implements Estudiante{ public int presentaExamen() { return (int) (Math.random() * 10.0); }}

Ahora crearemos, en el paquete default (default package), el archivo de configuración de Spring, "applicationContext.xml". Para esto hacemos clic derecho sobre el nodo "Source Packages" de nuestro proyecto, y en el menú contextual que se abre seleccionamos "new -> Other...". En la ventana

Page 35: Manual Spring

que se abre, seleccionamos la categoría "Other" y como tipo de archivo "Spring XML Configuration File". Le damos un nombre al archivo, en mi caso será "applicationContext". Hacemos clic en el botón "Next". En la siguiente pantalla debemos seleccionar los namespaces que queremos que tenga nuestro archivo. No seleccionaremos ninguno. Presionamos el botón "Finish" y con eso aparecerá en nuestro editor el archivo "applicationContext.xml".Declaramos un bean llamado "estudiante" de tipo "Universitario" dentro del archivo de configuración, de la siguiente forma:

<bean id="estudiante" class="ejemplos.spring.ciclovida.beans.Universitario" />

Comencemos viendo cómo podemos controlar el momento en el que los beans se crean. A esto se le conoce como el scope del bean.

Scope de los Beans

Por default, los beans en Spring son Singletons...¿que qué es un singleton?...Singleton es un patrón de diseño con el que se asegura que solo hay una instancia de un bean en nuestra aplicación. Si, leyeron bien, una y solo una instancia del bean por aplicación (o más exactamente por ClassLoader). Esto normalmente se logra haciendo que el constructor de la clase sea privado y proporcionando un método estático de fábrica que se encarga de controlar que solo haya una instancia de la clase. No entraré más en detalles porque ese es tema de otro tutorial.

Lo importante es remarcar el hecho de que en Spring, por default, todos los beans son singletons, o sea que el contenedor crea una sola instancia de un bean, no importa cuántas veces llamemos al método "getBean" de "ApplicationContext" (siempre que llamemos al mismo bean, claro está ^_^) Spring siempre nos regresará la misma instancia.

¿Cómo podemos comprobar que esto es verdad? Muy fácilmente ^_^. En Java, cada vez que un objeto es construido, se invoca el constructor de dicho objeto, los beans son objetos; por lo tanto, cada vez que un bean es creado se invoca su constructor.

Por lo tanto, podemos poner un mensaje en el constructor para que nos muestre algo en la consola cada vez que un bean de tipo " Universitario" sea creado. Modificaremos por un momento la clase "Universitario" para agregarle un constructor, que no reciba ningún parámetro, que nos muestre un mensaje de creación del objeto. Agregaremos el siguiente constructor a la clase "Universitario":

public Universitario(){ System.out.println("--Construyendo un universitario--");}Ahora obtendremos, en nuestro método "main", un bean de este tipo haciendo uso de un "ApplicationContext" (si no recuerdan bien cómo hacer eso pueden ver cómo en el segundo tutorial de la serie):

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");

A continuación invocaremos dos veces el método "getBean" del applicationContext para obtener una referencia al bean "estudiante", y lo mostraremos en la consola:

System.out.println("Estudiante 1: " + applicationContext.getBean("estudiante"));System.out.println("Estudiante 2: " + applicationContext.getBean("estudiante"));

No hacemos nada especial, solo obtenemos dos veces una referencia al bean "estudiante" y lo mostramos en la consola. Nuestro método "main" queda de la siguiente forma:

public static void main(String[] args){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); System.out.println("Estudiante 1: " + applicationContext.getBean("estudiante")); System.out.println("Estudiante 2: " + applicationContext.getBean("estudiante"));}

Si ejecutamos nuestra aplicación obtendremos la siguiente salida en la consola:

--Construyendo un universitario--

Estudiante 1: ejemplos.spring.ciclovida.beans.Universitario@a46701Estudiante 2: ejemplos.spring.ciclovida.beans.Universitario@a46701

Como podemos ver, solo se invocó una vez al constructor de la clase "Universitario" y, por lo tanto, solo se construyó un objeto de esta clase. Las dos veces que invocamos al método "getBean" este nos regresó las dos veces la misma instancia.

Page 36: Manual Spring

Esto suena lógico ¿no? Ya que solo declaramos una vez el bean "estudiante" solo debería crearse una vez y siempre que queramos usar este bean deberíamos obtener la misma instancia ¿cierto?

Si bien esta idea es muy correcta, algunas veces en las que necesitaremos una nueva instancia de un bean cada vez que la pidamos (que llamemos a "getBean"). Afortunadamente existe una forma de hacer esto.

Cuando declaramos un elemento "<bean>" en el archivo de configuración de Spring, tenemos la opción de declarar un scope para ese bean. Por default el valor de este elemento es "singleton". Spring proporciona 5 posibles valores para este elemento:

singleton – Existirá una sola instancia del bean por contenedor. Este es el scope por defáult. prototype – Se creará una nueva instancia del bean por cada llamada a "getBean", o sea, cada vez que el bean vaya a ser usado.

request – Existirá una sola instancia del bean por cada petición HTTP; esto es, cada petición HTTP tiene su propia instancia de un bean. Este scope solo es válido cuándo se usa un contenedor de Spring con capacidades web como Spring MVC.

session – Existirá una instancia del bean por cada sesión HTTP. Este scope solo es válido cuando se usa un contenedor de Spring con capacidades web como Spring MVC.

globalSession – Existirá una instancia del bean por cada sesión global HTTP. Típicamente solo es válida cuando se trabaja con portlets. Este scope solo es válido cuando se usa un contenedor de Spring con capacidades web como Spring MVC.

En Spring 3 existe un nuevo y secreto sexto scope llamado "thread scope", pero no está registrado por default. Pueden encontrar más información de este scope en la documentación de Spring .

Veamos cada uno de estos scopes en detalle:

El scope singleton

Con este scope, como dije anteriormente, solo existe una instancia del bean por contenedor, y esta instancia es obtenida cada vez que se llama al método "getBean".

Diciéndolo de otra forma: cuándo definimos un bean, y este tiene scope definido como singleton (el default), el contenedor de IoC de Spring crea exactamente una instancia del bean definido. Esa instancia única es almacenada en un caché especial para estos beans singleton, y todas las llamadas subsecuentes para obtener ese bean, recibirán el objeto que se encuentra en el caché.

El scope prototype

Este scope es, se podría decir, el opuesto a singleton, ya que en este caso se crea una nueva instancia del bean cada vez que se solicita un bean específico, esto es, cada vez que pedimos un bean invocando al método "getBean". Como regla general, usamos el scope prototype para todos los beans con estado, y el scope singleton para todos los beans sin estado.

A diferencia de los otros scopes, Spring no administra el ciclo de vida completo de los beans prototype (esto es muy importante para lo que veremos un poco más adelante); el contenedor crea una instancia, la configura, y la ensambla, también la envía al cliente, pero después de eso Spring ya no mantiene un registro de esa instancia. Entonces, aunque los métodos de callback de inicialización del ciclo de vida son llamados en todos los objetos sin importar su scope, en el caso de los objetos prototype, los métodos de callback de destrucción NO son llamados.

Los scopes request, session, y globalSession

Los scopes request, session, y globalSession, solo están disponibles si usamos usando Spring en una aplicación web, y usamos una implementación de "ApplicationContext" que funcione en web (como "XmlWebApplicationContext"). Si intentamos usar estos scopes con alguna otra implementación (como con "ClassPathXmlApplicationContext"), obtendremos una excepción de tipo "IllegalStateException".

El scope requestCon este scope, el contenedor una nueva instancia del bean por cada petición HTTP. Cuando la petición termina de ser procesada, el bean es descartado.

El scope sessionLos beans con scope "session" son creados cada vez que se crea una sesión HTTP, y vive mientras la sesión permanezca viva, o sea que cuándo la sesión se descarta, el bean almacenado en esa sesión también es descartado.

El scope globalSessionEl scope "globalSession" es similar al scope al scope "session" y solo es aplicable en el contexto de aplicaciones web basadas en portlets. La especificación de portlets define la noción de una sesión global que es compartida por todos los portlets que componen una aplicación web.

Page 37: Manual Spring

Los beans con este scope viven por el tiempo de la sesión global de los portlets.

Si estamos creando una aplicación web no-portlet (basada en servlets) y definimos algún bean con este scope, entonces se usará el scope " session" normal y no obtendremos ningún error.

Ahora que hemos visto que existen varios scopes, y las deferencias entre estos, veamos cómo arreglar el problema que teníamos anteriormente, en donde únicamente obteníamos una instancia del bean "estudiante", sin importar el número de veces que llamemos al método "getBean". En esta ocasión lo que queremos es que se cree una nueva instancia del bean cada vez que lo requiramos. Como vimos en la definición de los scopes, esto es posible lograrlo si definimos el bean con el scope "prototype", por lo que modificaremos la declaración del bean, agregándole el atributo "scope" de la siguiente forma:

<bean id="estudiante" class="ejemplos.spring.ciclovida.beans.Universitario" scope="prototype" />

Si volvemos a ejecutar nuestra aplicación, veremos la siguiente salida en la consola:

--Construyendo un universitario--Estudiante 1: ejemplos.spring.ciclovida.beans.Universitario@1808199--Construyendo un universitario--Estudiante 2: ejemplos.spring.ciclovida.beans.Universitario@1bc887

Como podemos ver, en esta ocasión Spring ha construido un nuevo objeto de tipo "Universitario" por cada llamada el método "getBean".

El resto de los scopes los revisaremos cuando veamos cómo crear aplicaciones web con Spring. Ahora veamos cómo podemos crear beans usando métodos estáticos de fábrica, como se hace en el patrón de diseño factory.

Creando Beans desde Métodos Estáticos de Factory

La mayor parte del tiempo, los beans que configuramos en el contexto de aplicación de Spring son creados invocando uno de los constructores de la clase. Esto está bien si es que creamos nuestros beans siempre con constructores públicos. Pero ¿qué ocurre si por alguna razón no podemos usar un constructor público? O ¿qué pasa si usamos el API de un tercero que expone los objetos a través de un método estático de fábrica?

Afortunadamente Spring nos permite seguir creando beans con este tipo de clases. Para ejemplificar esto veamos el siguiente bean, el cuál es una implementación del patrón de diseño singleton, del cual hablamos anteriormente.

Crearemos una nueva clase, en el paquete "beans", llamada "Sun", por Sun Microsystems ^_^ (ya saben, por el asunto de que solo hay un Sun Microsystems ñ_ñ ). La clase "Sun", como dije, será una implementación del patrón de diseño singleton. Agregaremos un método de instancia, el cual solo se encargará de regresar un cadena que podremos postrar en la consola. La clase queda de la siguiente forma:

public class Sun{ private static Sun instancia;

static { instancia = new Sun(); }

private Sun() { }

public static Sun getInstancia() { return instancia; }

public String getMensaje() { return "Hola a todos los desarrolladores Java"; }}

La clase anterior tiene un constructor privado, lo que significa que solo puede usarse desde dentro de la misma aplicación. Además tiene una instancia del mismo tipo "Sun", la cual se inicializa dentro de un método de inicialización estático, o sea que la instancia se creará en cuanto la clase se cargue en el classloader.

Como el constructor y la instancia de la clase son privadas, necesitamos una forma de obtener dicha instancia. Para eso proporcionamos un método de fábrica, el método "getInstancia", el cual nos regresará la instancia cada vez que la necesitemos.

Page 38: Manual Spring

Con las dos consideraciones anteriores, nos aseguramos de que solo existirá una instancia de la clase anterior en todo el classloader y que no se hay forma de crear otra instancia. Podemos ver que con esta medida ya no podemos crear una instancia de la clase usando el operador "new", sino que debemos usar el método "getInstancia". Entonces, ¿cómo haríamos para que Spring creara un bean de esta clase?

Para casos como este, el elemento "<bean>" proporciona un atributo llamado "factory-method". Este atributo nos permite especificar un método estático que será invocado, en vez del constructor, para crear una instancia de la clase.

Así que ahora configuraremos un bean de tipo "Sun" en el archivo de configuración, para esto agregamos un nuevo elemento "<bean>" de esta forma:

<bean id="sun" class="ejemplos.spring.ciclovida.beans.Sun" factory-method="getInstancia" />

Ahora modificaremos nuestro método "main". Seguiremos creando una instancia de "ApplicationContext" a partir del archivo "applicationContext.xml", pero ahora obtendremos una referencia al bean llamado "sun". También invocaremos a su método "getMensaje" y mostraremos la cadena que regresa en la consola, de la siguiente forma:

Sun sun = applicationContext.getBean("sun", Sun.class);System.out.println(sun.getMensaje());

Por lo que el método "main" queda de la siguiente forma:

public static void main(String[] args){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); Sun sun = applicationContext.getBean("sun", Sun.class);

System.out.println(sun.getMensaje());}

Al ejecutar la aplicación obtendremos la siguiente salida en la consola:

Hola a todos los desarrolladores Java

Con lo cual podemos darnos cuenta de que la instancia del bean se ha creado correctamente, usando el método de fábrica, y que podemos usar sus métodos de forma normal ^_^.

Ahora veremos el ciclo de vida de un bean en Spring, y cómo recibir notificaciones durante algunos de los distintos eventos del ciclo de vida de nuestros beans.

El ciclo de vida de un bean

En una aplicación Java tradicional, el ciclo de vida de un bean es muy simple. Se usa el operador "new" para instanciar el bean, y está listo para ser usado. Una vez que dejamos de usar el bean, es elegible para ser recolectado por el garbage collector. En contraste, el ciclo de vida de un bean dentro del contenedor de Spring es un poco más elaborado. Es importante entender el ciclo de vida de un bean de Spring, ya que es posible que querramos tener ventaja de algunas de las oportunidades que Spring nos ofrece para personalizar la forma en la que un bean es creado.

La siguiente figura muestra el ciclo de vida de un bean típico que es cargado en un contenedor de tipo, y pongan atención porque esto es importante, "BeanFactory":

Después de ver esta figura, podemos darnos cuenta de que Spring realiza bastantes pasos de configuración antes de que un bean esté listo para ser

usado por un, y nuevamente pongan atención, "BeanFactory". La siguiente tabla explica cada uno de estos pasos con más detalle:

Page 39: Manual Spring

Paso Descripción

1. Instanciar Spring instancia el bean

2. Llenar Propiedades Spring inyecta las propiedades del bean

3. Establecer el nombre del bean

Si el bean implementa "BeanNameAware", Spring pasa el id del bean a "setBeanName()"

4. Establecer el bean factory Si el bean implementa "BeanFactoryAware", Spring pasa el bean factory a "setBeanFactory()"

5. Post procesar (antes de la inicialización)

Si hay algún "BeanPostProcessors", Spring llama a sus métodos "postProcessBeforeInitialization()"

6. Inicializar beansSi el bean implementa "InitializingBean", se llamará a su método "afterPropertiesSet()". Si el bean tiene un método "init-method" propio (que veremos en la siguiente sección), el método será llamado.

7. Post procesar (después de la inicialización)

Si hay algún "BeanPostProcessors", Spring llama a sus métodos "postProcessAfterInitialization()"

8. El bean está listo para usarse

En este punto el bean está listo para ser usado por la aplicación y permanecerá en el bean factory hasta que deje de ser ocupado.

9. Destruir el beanSi el bean implementa "DisposableBean", se llama a su método "destroy()". Si el bean tiene un método "destroy-bean" propio, el método especificado será llamado.

¿Por qué recalqué el hecho de que este es el ciclo de vida de un bean en un " BeanFactory"? Pues bien, esto lo hice por el hecho de que el ciclo de vida del bean es un poco diferente dentro de un "ApplicationContext", el cual podemos ver en la siguiente imagen:

La única diferencia es que si el bean implementa la interface "ApplicationContextAware", se llama a su método "setApplicationContext".

Ahora que ya tenemos una mejor idea del ciclo de vida de los beans, veamos cómo podemos recibir notificaciones cuando nuestros beans son creados o destruidos.

Callbacks de creación y destrucción de beans

Cuando un nuevo bean es creado, puede ser necesario que debamos realizar algún proceso de inicialización para dejarlo en un estado razonable para su uso. De la misma forma, cuando ya no necesitamos un bean, y este va a ser removido del contenedor, podría ser necesario realizar algún proceso de limpieza.

Spring nos proporciona varios mecanismos para interactuar con la administración del ciclo de vida, de los beans, de nuestro contenedor; más específicamente para el momento en el que los beans son creados y en el momento en el que serán destruidos. Estos mecanismos se conocen como métodos de retrollamada, o callback en inglés.

Cada uno de estos mecanismos, como ocurre regularmente, tiene sus ventajas y sus desventajas, pero los tres son igual de fáciles de usar. Los tres mecanismos son:

Implementación de interfaces para recibir notificaciones. Declaración de los métodos que recibirán las notificaciones, usando archivos de configuración.

Marcar los métodos que recibirán las notificaciones, usando anotaciones.

Page 40: Manual Spring

Implementación de Interfaces para Recibir NotificacionesPara recibir las notificaciones de la creación o destrucción de un bean, podemos implementar las interfaces "org.springframework.beans.factory.InitializingBean" y "org.springframework.beans.factory.DisposableBean". El contenedor llama al método "afterPropertiesSet()" al momento de inicializar al vean, y a "destroy()" antes de destruir al bean, cada uno en su interface correspondiente.

Vemos un ejemplo del uso de estas interfaces. Para esto modificaremos nuestra clase " Universitario" haciendo que implemente estas dos interfaces. El método "afterPropertiesSet()" servirá para indicar cuando nuestro objeto sea creado, o sea cuando nuestro nuevo Universitario entre a la escuela ^_^. Usaremos este método para mostrar un mensaje de bienvenida en la consola; y usaremos el método " destroy()" para mostrar un mensaje de despedida, también en la consola.

La clase "Universitario" queda de la siguiente forma:

public class Universitario implements Estudiante, InitializingBean, DisposableBean{ public Universitario() { System.out.println("--Construyendo un universitario--"); }

public int presentaExamen() { return (int) (Math.random() * 10.0); }

public void afterPropertiesSet() throws Exception { System.out.println("Bienvenido universitario a tu nuevo segundo hogar."); }

public void destroy() throws Exception { System.out.println("Vuela libre con tus alas propias."); } }

Un poco dramático pero muestra la idea del ejemplo ^_^!.

Si ejecutamos nuestra aplicación, obteniendo en el método "main" nuestro bean "estudiante", veremos la siguiente salida en consola:

--Construyendo un universitario--INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@134bed0: defining beans [estudiante,sun]; root of factory hierarchyBienvenido universitario a tu nuevo segundo hogar.Estudiante 1: ejemplos.spring.ciclovida.beans.Universitario@6e293a

Si observamos la salida anterior con calma, podremos observar que falta un mensaje, el mensaje correspondiente con la destrucción del bean. Esto ocurre porque Spring no sabe que la aplicación ha dejado de ejecutarse (ya que esta termina de manera súbita al terminarse el método " main") y por lo tanto no sabe cuándo llamar al método "destroy". Entonces ¿cómo hacemos para que Spring sepa cuándo es que la aplicación se termina? O dicho de otra forma ¿cómo sabe Spring cuándo debe destruir el contenedor de beans y por lo tanto llamar al método "destroy"? Afortunadamente Spring nos proporciona una manera elegante de hacer esto.

Si recuerdan estamos usando, como contenedor de beans, una clase que implementa la interface "ApplicationContext", en este caso la clase "ClassPathXmlApplicationContext". Esta clase tiene una clase base llamada "AbstractApplicationContext", la cual contiene un método "close" que nos sirve para indicarle a Spring cuándo debe cerrar el ApplicationContext, y destruir todos los beans que se encuentran en el. Modificamos nuestro método "main" para declarar nuestra variable "applicatonContext" para que haga uso de una referencia de tipo "AbstractApplicationContext", y llamamos, al final del "main", a su método "close". El método "main" queda de la siguiente forma:

AbstractApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");

System.out.println("Estudiante 1: " + applicationContext.getBean("estudiante"));applicationContext.close();

Además de este cambio debemos modificar una cosa en el archivo de configuración de Spring. En este momento el bean "estudiante" está declarado como un bean con scope "prototype", o sea que se crea uno cada vez que se llama al método "getBean". Si recuerdan en la explicación de los scopes de los beans, dijimos que los métodos de destrucción de beans no serán llamados en los beans "prototype", por lo que debemos cambiar el scope del bean para que siga siendo un singleton. Cambiamos la siguiente declaración:

Page 41: Manual Spring

<bean id="estudiante" class="ejemplos.spring.ciclovida.beans.Universitario" scope="prototype" />

Para que quede de esta forma:

<bean id="estudiante" class="ejemplos.spring.ciclovida.beans.Universitario" />

Si volvemos a ejecutar nuestra aplicación, veremos la siguiente salida en la consola:

--Construyendo un universitario--INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1db4f6f: defining beans [estudiante,sun]; root of factory hierarchyBienvenido universitario a tu nuevo segundo hogar.Estudiante 1: ejemplos.spring.ciclovida.beans.Universitario@54a328

31/01/2011 01:31:44 AM org.springframework.context.support.AbstractApplicationContext doCloseINFO: Closing org.springframework.context.support.ClassPathXmlApplicationContext@c1b531: startup date [Mon Jan 31 01:31:43 CST 2011]; root of context hierarchy31/01/2011 01:31:44 AM org.springframework.beans.factory.support.DefaultSingletonBeanRegistry destroySingletonsINFO: Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1db4f6f: defining beans [estudiante,sun]; root of factory hierarchy

Vuela libre con tus alas propias.

Con esto comprobamos que los métodos se han llamado en los momentos correspondientes. Sin embargo ahora nuestra aplicación ha quedado acoplada a las librerías de Spring (lo cual no queremos). Como siempre, Spring nos proporciona una forma de evitar el tener que hacer este acomplamiento, declarando en el archivo de configuración los métodos que serán usados para la inicialización y destrucción del bean.

Declaración de los métodos que recibirán las notificaciones, usando archivos de configuraciónComo pasa con todo en Spring, el declarar los métodos que se usarán para la inicialización y destrucción del bean es de lo más simple. Lo único que debemos hacer es declarar, en el elemento "&lt;bean>", el atributo "init-method", para el método de inicialización, y "destroy-method", para el método de destrucción. Estos métodos pueden ser cualquiera que exista dentro de nuestra clase. Para ilustrar eso modificaremos nuestra clase "Universitario" agregando dos nuevos métodos. El primero será el método "preparaEstudiante", el cual mostrará un mensaje en la consola mostrando la inicialización del bean. El segundo método será "despideEstudiante", que también mostrará un mensaje en consola, pero indicando que el bean será destruido:

public void preparaEstudiante(){ System.out.println("Preparando al nuevo estudiante para entrar en la universidad.");} public void despideEstudiante(){ System.out.println("Finalmente podemos decir adios a este estudiante.");}

Y declaramos, en el archivo "applicationContext.xml", que el bean "estudiante" usará estos dos métodos:

<bean id="estudiante" class="ejemplos.spring.ciclovida.beans.Universitario" init-method="preparaEstudiante" destroy-method="despideEstudiante" />

Si ejecutamos nuestra aplicación en este momento obtendremos los mensajes combinados de la implementación de las interfaces del punto anterior y de los métodos declarados en el archivo de configuración. Para evitar esto eliminaremos temporalmente la declaración de las interfaces de nuestra clase "Universitario", dejándolo de esta forma:

public class Universitario implements Estudiante{ public Universitario() { System.out.println("--Construyendo un universitario--"); }

public int presentaExamen() { return (int) (Math.random() * 10.0); }

public void preparaEstudiante()

Page 42: Manual Spring

{ System.out.println("Preparando al nuevo estudiante para entrar en la universidad."); } public void despideEstudiante() { System.out.println("Finalmente podemos decir adios a este estudiante."); }}

Si ejecutamos nuestra aplicación obtendremos la siguiente salida:

--Construyendo un universitario--Preparando al nuevo estudiante para entrar en la universidad.Estudiante 1: ejemplos.spring.ciclovida.beans.Universitario@6e293aFinalmente podemos decir adios a este estudiante.

Como podemos ver, los métodos han sido llamados sin necesidad de implementar ninguna interface, por lo que ahora están completamente desacoplados de Spring.

Esto nos sirve bastante si tenemos una o dos clases que debemos inicializar de esta manera. Pero ¿qué ocurre si queremos realizar procesos de inicialización o destrucción en muchos beans? Si usamos alguna convención de nombres para estos métodos de inicialización y destrucción (como por ejemplo "inicializa" y "destruye") no es necesario que declaremos "init-method" y "destroy-method" en cada bean de forma individual. En vez de eso, podemos tomar ventaja de los atributos "default-init-method" y "default-destroy-method" de elemento "<beans>". De esta forma estamos configurando el contenedor de beans de Spring para que busque estos métodos de inicialización y destrucción en cada bean que declaremos en el contenedor. El contenedor de IoC de Spring llamará a estos métodos, de forma automática, cada vez que un bean sea creado o destruido. Esto también nos ayuda a usar una convención de nombres consistente para métodos callback de inicialización y destrucción ^_^.

Para ejemplificar esto, modifiquemos una vez más nuestra clase "Universitario", agregando un nuevo método de inicialización llamado "inicializa", que nuevamente mostrará un mensaje en la consola, y un método de destrucción llamado "destruye", que también mostrará un mensaje en la consola:

public void inicializa(){ System.out.println("Realizando inicializacion de rutina en el bean.");} public void destruye(){ System.out.println("Realizando destruccion de rutina en el bean.");}

Y modificamos el archivo "applicationContext.xml" para que quede de esta forma:

<?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-3.0.xsd" default-init-method="inicializa" default-destroy-method="destruye" > <bean id="estudiante" class="ejemplos.spring.ciclovida.beans.Universitario" /></beans>

Si ejecutamos nuevamente nuestra aplicación obtendremos la siguiente salida en la consola:

--Construyendo un universitario--Realizando inicializacion de rutina en el bean.Estudiante 1: ejemplos.spring.ciclovida.beans.Universitario@6e293aRealizando destruccion de rutina en el bean.

Que como podemos ver, ha ejecutado los métodos de inicialización y destrucción por default de manera correcta.

¿Qué pasa si indicamos dos métodos, uno con "init-method" y otro con "default-init-method"? En ese caso, el método más específico ("init-method" que se indica a nivel de bean) toma precedencia sobre el método más general.

Estas dos formas de configurar los métodos de inicialización y destrucción de beans es muy buena, pero ¿qué ocurre si estamos trabajando con anotaciones? Afortunadamente también tenemos una forma de indicar estos métodos callback en caso de que estemos usando anotaciones.

Page 43: Manual Spring

Marcar los métodos que recibirán las notificaciones, usando anotaciones.Spring (bueno, en realidad la especificación JEE) proporciona un par de anotaciones para el ciclo de vida. Para que estas anotaciones tengan efecto, debemos configurar nuestra aplicación para el uso de anotaciones. Si no recuerdan cómo hacer eso, pueden revisar el tutorial de la configuración del contenedor de IoC de Spring .

Una vez que tenemos nuestro bean, lo único que debemos hacer es marcar el método de inicialización usando la anotación " @PostConstruct", y el método de destrucción usando la anotación "@PreDestroy", de la siguiente forma:

@PostConstructpublic void inicializa(){ System.out.println("Realizando inicializacion de rutina en el bean.");} @PreDestroypublic void destruye(){ System.out.println("Realizando destruccion de rutina en el bean.");}

Cuando ejecutemos esta aplicación veremos aparecer la siguiente salida en la consola:

--Construyendo un universitario--Realizando inicializacion de rutina en el bean.Estudiante 1: ejemplos.spring.ciclovida.beans.Universitario@187814Realizando destruccion de rutina en el bean.

Que como podemos ver nos indica que esta configuración también ha funcionado adecuadamente ^_^.

Combinando Mecanismos de Callback del Ciclo de VidaComo vimos, tenemos 3 opciones para recibir notificaciones sobre la creación y destrucción del bean. Si es necesario, tenemos la posibilidad de combinar estos tres mecanismos en el mismo bean.

En el caso de la construcción del bean, las llamadas a los distintos métodos se realizan en el siguiente orden:

Métodos marcados con la anotación "@PostConstruct". El método "afterPropertiesSet", de la interface "InitializingBean".

El método que indiquemos en el archivo de configuración (recordemos que si indicamos un valor para "init-method", este toma precedencia sobre "default-init-method").

En la destrucción del bean, los métodos se ejecutan en el mismo orden:

Métodos marcados con la anotación "@PreDestroy". El método "destroy" de la interface "DisposableBean".

El método que indiquemos en el archivo de configuración ("destroy-method" o "default-destroy-method").

Si por alguna razón marcáramos un método para usar más de un mecanismo (digamos que marcáramos el método " afterPropertiesSet", de la interface "InitializingBean", con la anotación "@PostConstruct") el método ejecutará una sola vez.Ahora que sabemos todo lo que debemos saber al respecto del ciclo de vida de los beans en Spring, podemos terminar tranquilos este tutorial ^_^. Espero que les sea de utilidad, y como siempre no olviden dejar todas sus dudas, comentarios, y sugerencias.

Saludos.

Descarga los archivos de este tutorial desde aquí: