Capítulo 2: Conexión de beans

Anuncio
Capítulo 2:
Conexión de beans
Tipos de contenedores, conexiones y vida
de los beans
En este capítulo hablaremos sobre los contenedores de
beans, veremos las diferentes formas que hay en Spring
para conectarlos, las conexiones entre los propios
beans así como cómo conectar los beans con sus
distintas vías. Cerraremos el capítulo con la
delimitación, vida e inicio y destrucción que tienen los
beans dentro de este framework.
Javier Sevilla Sánchez
Trabajo de fin de carrera de la Ingeniería Técnica en
Informática de Gestión (I.T.I.G)
1
Contenido
Conexión de Beans ........................................................................................................................ 3
Introducción .............................................................................................................................. 3
Bean dentro de un contenedor ................................................................................................. 3
Contenedor de beans ................................................................................................................ 3
BeanFactory .............................................................................................................................. 4
ApplicationContext.................................................................................................................... 4
Pasos de la vida de un Bean ..................................................................................................... 5
Ejemplo de creación y conexión de beans ................................................................................ 6
Conectar tipos múltiples ......................................................................................................... 10
Tipos de conexión de beans ........................................................................................................ 13
Auto-conexión byName .......................................................................................................... 13
Auto-conexión byType ............................................................................................................ 14
Auto-conexión constructor ..................................................................................................... 14
Auto-conexión autodetect ...................................................................................................... 14
La auto-conexión default ........................................................................................................ 15
La Auto-conexión no ............................................................................................................... 15
Configuración de la creación de los beans .................................................................................. 16
Delimitación de un bean ......................................................................................................... 16
Creación de beans con clases singleton .................................................................................. 17
Uso de init-method y de destroy-method (inicializar y destruir)............................................ 17
Conexión de Beans
Introducción
En el capítulo anterior hemos dado los primeros pasos con Spring, hemos visto por primera vez
la inyección de dependencia (DI) y la programación orientada a aspectos (AOP) creando unos
sencillos proyectos a modo de ejemplo.
A lo largo de este capítulo veremos más en profundidad el contenedor de Spring y sus distintas
implementaciones. Las principales categorías de las implementaciones son la factoría de beans
y el contexto de aplicación. En este capítulo veremos cómo conectar beans dentro del
contenedor y qué información daremos al fichero XML para crear una aplicación con una DI
potente.
Bean dentro de un contenedor
A diferencia de Java tradicional en el que el ciclo de vida del objeto es desde su instanciación
con new hasta su etiquetado por el recolector, en Spring el ciclo de vida es más complejo.
Aunque dependa de si el bean ha sido creado por una factoría o un contexto, el bean pasa por
una serie de pasos entre los que se encuentran la instanciación, la asignación de propiedades y
nombre, nombre de la fábrica, procesamiento, iniciación…etc. Así hasta que se ejecuta el
método destroy() sobre él.
Contenedor de beans
Como ya dijimos, en el enfoque tradicional las asociaciones llevan a código complejo y no
reutilizable ni “testeable”. En Spring el contenedor es el responsable de la interconexión de
beans.
El contenedor será el responsable de la inyección de dependencia y del ciclo de vida de los
beans, creándolos, interconectándolos y destruyéndolos.
Spring tiene diversos contenedores, pero responden a dos grandes grupos según la
implementación del interfaz BeanFactory o AplicationContext.
BeanFactory
La implementación más usual es la XmlBeanFactory al cual se le pasa un objeto que
implemente Resource (hay ocho implementaciones) que proporcionará el XML con las
definiciones de los beans.
Es el contenedor más básico, pero es más que la instanciación y entrega de beans. Cuando una
fábrica entrega un bean lo entrega configurado, consciente de sus dependencias y listo para
usar.
El siguiente ejemplo crea un XmlBeanFactory como implementación de BeanFactory con un
FileSystemResource como implementación de Resource y obtiene el bean llamado
beanEjemplo.
BeanFactory factoria = new XmlBeanFactory(new
FileSystemResource(“/home/user/spring/beans.xml”));
BeanExample beanEjemlo = (BeanExample) factory.getBean(“beanEjemplo”);
ApplicationContext
Las aplicaciones creadas con Spring con normalidad usan implementaciones de
AplicationContext en vez de BeanFactory. Esto es debido a que AplicationContext tiene
funcionalidades adicionales dejando la BeanFactory para aplicaciones con recursos más
limitados como pueda ser un móvil, o sistemas empotrados.
Otra diferencia es cómo abren los beans. Mientras la factoría crea el bean cuando se llama a
getBean() el contexto abre previamente todos los beans.
ApplicationContext tiene la posibilidad de internacionalizar aplicaciones (I18N), generalizar la
apertura de recursos de archivo así como otorgar la posibilidad de publicar eventos para beans
receptores.
De las diversas implementaciones las más comunes son ClassPathXmlApplicationContext,
FileSystemXmlApplicationContext y XmlWebApplicationContext. Ésta última la trataremos más
adelante en el capítulo destinado a Spring MVC.
La diferencia entre FileSystemXmlApplicationContext y ClassPathXmlApplicationContext es que
el primero buscará en el sistema de archivos y el segundo según el path de la clase incluyendo
archivos jar.
Ejemplo de ello sería:
ApplicationContext ctx = new ClassPathXmlApplicationContext(“miFichero.xml”);
Pasos de la vida de un Bean
Como ya hemos visto, un bean en Spring tiene una vida más compleja que en Java puro. Sus
pasos son:
1.
2.
3.
4.
5.
6.
7.
Instanciar: Spring instancia el bean.
Informar de las propiedades: Spring hace la inyección a las propiedades.
Asignación del nombre: Spring pasa el nombre del bean si este implementa
BeanNameAware.
Establecer nombre de la fábrica y el contexto: Si el bean implementa
BeanNameAware le pasará el nombre de la fábrica y en el caso de implementar
ApplicationContextAware y de estar contenido en un contexto se le pasaría el
nombre del contexto.
Iniciación del Bean: Si el bean implementa InitializingBean, se llamaría a su
método afterPropertiesSet(). Existen dos métodos que se llamarían si hubiese
algún BeanPostProccessor, uno antes de la inicialización y otro después.
Estado de listo para su uso: Tras esto, el bean ya estaría listo para usarse.
Destrucción del bean: Si el bean implementa DisposableBean se llamará al método
destroy().
Ejemplo de creación y conexión de beans
Hasta ahora hemos visto distintos proyectos con Spring que creaban y usaban beans, pero no
habíamos entrado en detalle en la explicación de estas conexiones.
Existe un eterno debate de cuál es la mejor opción para la inyección de dependencias si por
constructor o por el contrario por setter. Como para casi todos los debates la respuesta es
“depende”.
Inyección por constructor
Por un lado la inyección por constructor crea una fuerte dependencia con el instanciador ya
que este ha de tener todas las dependencias antes, así garantiza que un bean está
completamente configurado antes de usarse. Si se inyecta por constructor es posible que no
sean necesarios métodos setter, con lo que la clase será más sencilla e inalterables sus
dependencias.
Inyección por setter
Sin embargo, la inyección por constructor tiene también sus inconvenientes. Si el bean tiene
muchas dependencias el constructor será complejo, si existen varios parámetros del mismo
tipo pues puede confundir su instanciación y acaba siendo más complejo cuando la clase
hereda. La configuración por setter puede realizar construcciones más complejas.
Spring permite ambas configuraciones, con lo que no restringe a una forma la inyección.
Ejemplo de Conexión
Continuando con la filosofía de anteriores ejemplos, veamos cómo se conectan beans
tomando como ejemplo de nuevo un escenario universitario. Al inicio del curso, los
responsables de hostelería han visto que este año habrá más alumnos, con lo que han decidido
contratar a más gente. Para el puesto en cuestión se valoraran distintas cualidades como saber
cocinar, limpiar, el trato con la gente… etc.
El código del interfaz trabajador sería el siguiente:
public interface Worker {
void work() throws WorkingException;
}
La siguiente clase define los candidatos camareros:
package es.uah.uahconnection.cafe.model;
public class Waiter implements Worker {
private int coffeesServedDaily = 1;
public Waiter() {
}
public Waiter(int coffeesServedDaily) {
this.coffeesServedDaily = coffeesServedDaily;
}
public void work() throws WorkingException {
System.out.println("el camarero está sirviendo " +
coffeesServedDaily + " cafés");
}
}
Veamos cuál sería la implementación más básica de camarero:
<bean id="joselito" class="es.uah.uahconnection.cafe.model.Waiter"/>
Esta es la definición más básica de un bean, tan sólo se le da un identificador y la clase a la que
pertenece el objeto. De los dos constructores que tienen los camareros, Joselito será creado
con el constructor sin parámetros. Tal y como vemos no es muy complicado que se te
considere como camarero, con servir un café al día vale. Pero no creo que Joselito consiga el
trabajo con tan poco esfuerzo.
Pepe viene con más entusiasmo, él asegura que es capaz de servir al menos 30 cafés al día.
Para ello hacemos uso del constructor con parámetro, en Spring el modo de definirlo es así:
<bean id="pepe"
class="es.uah.uahconnection.cafe.model.Waiter">
<constructor-arg value="30"/>
</bean>
Con la etiqueta <constructor-arg> se pasan los parámetros que el constructor precise siempre
que coincidan en número, tipo y posición así obtenemos la inyección mediante constructor.
Pepe es capaz de servir 30 cafés, pero hay competidores más preparados. Alberto es capaz de
poner el mismo número de cafés mientras limpia el local. Veamos como es el código del
camarero-limpiador.
public class WaiterDustman extends Waiter {
private Local local;
public WaiterDustman(int coffeesServedDaily, Restaurant restaurant) {
super(coffeesServedDaily);
this.local = restaurant;
}
@Override
public void work() throws WorkingException{
super.work();
local.clean();
}
}
Siendo el interfaz local y su implementación restaurante la siguiente:
interface Local {
void clean();
}
public class Restaurant implements Local {
public void clean() {
System.out.println("El ha sido limpiado");
}
}
La definición del bean Alberto que haremos en Spring le inyectaremos otro bean
“cafeteriaPolitecnica” el cual es de la clase restaurante.
<bean id="alberto" class="es.uah.uahconnection.cafe.model.WaiterDustman">
<constructor-arg value="25"/>
<constructor-arg ref="cafeteriaPolitecnica"/>
</bean>
Lo que Spring internamente ejecutaría sería algo similar a esto:
Local restaurant = new Restaurant();
Worker alberto = new WaiterDustman(coffeesServedDaily, restaurant);
Como hemos visto en anteriores ejemplos y hemos comentado al principio de este punto,
Spring permite configurar dependencias con los métodos setter como es el caso del cocinero y
su receta.
public class Kitchener implements Worker {
private Recipe recipe;//receta
private int diners;//comensales
public Kitchener() {
}
public void setRecipe(Recipe recipe) {
}
public void setDiners(int diners) {
this.diners = diners;
}
public void work() throws WorkingException {
System.out.println("Se dispone a cocinar " + recipe.getName());
recipe.develop();
}
}
La interfaz receta sería:
public interface Recipe {
void develop();
public String getName();
}
Y la clase paella valenciana sería la siguiente:
public class PaellaValenciana implements Recipe {
private static final String NAME = "Paella valenciana";
public void develop() {
fryTheMeat();
sauteVegetables();
pourWater();
addRice();
}
public String getName() {
return NAME;
}
public void fryTheMeat(){
System.out.println("La carne se está friendo");
}
public void sauteVegetables(){
System.out.println("las verduras se están rehogando");
}
public void pourWater(){
System.out.println("se vierte agua");
}
public void addRice(){
System.out.println("se añade el arroz");
}
}
Ernesto es un buen cocinero cuya especialidad es la paella valenciana receta de Arguiñano. La
definición de Ernesto así como de su paella valenciana sería la siguiente.
<bean id="paellaValencianaArguiñano"
class="es.uah.uahconnection.cafe.model.PaellaValenciana"/>
<bean id="ernesto" class="es.uah.uahconnection.cafe.model.Kitchener">
<property name="recipe" ref="paellaValencianaArguiñano"/>
<property name="diners" value="6"/>
</bean>
Como vemos en el código podemos asignar valores mediante el tag <property>. Esto sólo será
así si en la clase tenemos un método setter para tal propiedad. Para inyectar valores simples se
hará con el campo value mientras que para inyectar un bean definido en Spring se hará
mediante el campo ref. Spring sabrá si los valores simples son de tipo numérico, cadena o
booleano. Para definir a Ernesto se ha usado una inyección de bean y otra de valor simple, se
ha pasado la receta de paella que aprendió de Arguiñano y el número de comensales para el
que es capaz de cocinar.
Inyectar beans internos
Los beans internos, al igual que pasaría con las clases declaradas de manera interna dentro de
otra clase, son Beans definidos dentro del rango de bean. El siguiente aspirante al puesto de
trabajo es también cocinero, pero su receta de la paella valencia la guarda celosamente, no
quiere compartirla con el resto.
<bean id="alfredo" class="es.uah.uahconnection.cafe.model.Kitchener">
<property name="diners" value="5"/>
<property name="recipe">
<bean class="es.uah.uahconnection.cafe.model.PaellaValenciana"/>
</property>
</bean>
Si la clase cocinero pudiera configurarse por constructor también se podría crear un bean
interno de la siguiente manera:
<bean id="alfredo" class="es.uah.uahconnection.cafe.model.Kitchener">
<constructor-arg value="5"/>
<constructor-arg>
<bean class="es.uah.uahconnection.cafe.model.PaellaValenciana"/>
</constructor-arg>
</bean>
Conectar tipos múltiples
Hemos visto como en Spring se inyectan dependencias mediante los operadores value y ref.
Sin embargo se pueden inyectar tipos múltiples como List (cuando sea una lista), Set (una lista
sin duplicados), Map (clave y valor de cualquier tipo) o Properties (clave y valor de tipo
cadena).
Saber cocinar un plato está bien, pero lo útil sería poder acumular un gran número de recetas
para poder ser un buen Chef. La clase Chef refleja esto.
public class Chef implements Worker {
private Collection<Recipe> recipes;//también un Array o una lista
private int diners;//comensales
public Chef() {
}
public void setRecipe(Recipe recipe) {
}
public void setDiners(int diners) {
this.diners = diners;
}
public void work() throws WorkingException {
for (Recipe recipe : recipes) {
System.out.println("Se dispone a cocinar " + recipe.getName());
recipe.develop();
}
System.out.println("Todo ello para " + diners + " comensales");
}
}
Raimundo es un buen Chef, ha estudiado varias recetas. Veamos su definición.
<bean id="raimundo" class="es.uah.uahconnection.cafe.model.Chef">
<property name="recipes">
<list>
<ref bean="paellaValencianaArguiñano"/>
<ref bean="tortillaDePatatas"/>
<ref bean="pizzaItaliana"/>
</list>
</property>
<property name="diners" value="50"/>
</bean>
La clase de la propiedad recipes podría ser también una lista o incluso un array al igual que a la
hora de definirlo podríamos haberlo hecho con la etiqueta <set> en vez de <list>, esto
aseguraría que no hubiese repetidos.
El trato con la gente es algo muy valorado, ya que todo cliente estará más contento si alguien
le atiende de una manera amable. Borja Mari es un poco pedante, pero cae bien. Mientras
sirve cafés suele amenizar con frases amables. Borja Mari pertenece a la clase camarero
amable cuyo código es el siguiente:
public class KindWaiter extends Waiter {
private Properties kindSpeach;
public void setKindSpeach(Properties kindSpeach) {
this.kindSpeach = kindSpeach;
}
@Override
public void work() throws WorkingException {
super.work();
System.out.println("Mientras da una buena conversación a sus clientes:
");
for (Iterator it = kindSpeach.keySet().iterator(); it.hasNext();) {
String speachType = (String) it.next();
System.out.println("
- para " + speachType + " dice \"" +
kindSpeach.getProperty(speachType) + "\"");
}
}
}
Para poder definirlo en el contenedor de Spring incluiremos el siguiente código en el fichero
xml.
<bean id="borjaMari" class="es.uah.uahconnection.cafe.model.KindWaiter">
<property name="kindSpeach">
<props>
<prop key="buenosDias">Buenos días, que bonita mañana hace
hoy</prop>
<prop key="buenasTardes">Buenas y maravillosas tardes</prop>
<prop key="servir">Aquí está su café</prop>
<prop key="cuenta">Le traigo su cuenta enseguida</prop>
<prop key="despedirse">Muchas gracias y vuelva otra vez</prop>
</props>
</property>
</bean>
Hasta ahora todos nuestros aspirantes al puesto vacante para la cafetería de la universidad
han sido muy buenos, pero también se valoran dotes de liderazgo. El señor Antunez es un
buen coordinador y no sólo trabaja sino que sabe organizar a los demás. Para la definición de
la clase coordinador utilizaremos un mapa en el cual queden reflejados los nombres de los
trabajadores para que el señor Antunez pueda referirse por su nombre (la clave de la clase
Map) y el propio trabajador.
La clase Jefe refleja lo comentado:
public class Boos implements Worker {
private Map<String, Worker> employees;
public void work() throws WorkingException {
for (String name : employees.keySet()) {
System.out.println("Organiza el trabajo para " + name);
Worker worker = employees.get(name);
worker.work();
}
}
public void setEmployees(Map<String, Worker> employees) {
this.employees = employees;
}
}
Su definición en el fichero xml sería la siguiente
<bean id="antunez" class="es.uah.uahconnection.cafe.model.Boos">
<property name="employees">
<map>
<entry key="Alberto" value-ref="alberto"/>
<entry key="Alfredo" value-ref="alfredo"/>
<entry key="Ernesto" value-ref="ernesto"/>
<entry key="Joselito" value-ref="joselito"/>
<entry key="Pepe" value-ref="pepe"/>
<entry key="Raimundo" value-ref="raimundo"/>
<entry key="BorjaMari" value-ref="borjaMari"/>
</map>
</property>
</bean>
Tipos de conexión de beans
Existen dos vías para conectar dependencias de beans, la manual y la automática. La manual es
la que hemos ido viendo en los ejemplos que hemos hecho hasta ahora ya sea vía constructor
o vía setter.
La principal razón por la que existe la auto conexión es la reducción de código xml haciéndolo
más simple y más vistoso. Para ello tenemos cuatro tipos distintos de auto conexión, por
nombre, por tipo, por constructor y otra última que combina el constructor y el tipo.
Si tenemos definido un bean cuyo id en el contenedor tenga el mismo nombre que una
propiedad del bean que estamos definiendo podemos usar la auto conexión por nombre
(byName). La auto conexión por tipo nos será útil cuando haya un único bean del mismo tipo
que la propiedad a conectar, si hubiese más se lanzaría una excepción y si no hubiese la
propiedad tendría null.
También se le puede indicar al bean que intente auto conectarse por constructor intentando
que Spring haga corresponder los beans del contenedor con los constructores. Si hubiese
ambigüedades se lanzaría una excepción.
Por último existe la auto conexión con auto detección, esto sería una mezcla de la auto
conexión por tipo y por constructor.
Hay que comentar que también se puede conectar tipos null, gracias a la etiqueta <null/>, esto
es útil aquí en la auto conexión, ya que es posible que no queramos que determinadas
propiedades se conecten.
La autoconexión y la conexión especificada pueden perfectamente convivir. También hay que
decir que hay otro debate si es útil la auto-conexión o no.
Auto-conexión byName
Si el nombre que le damos a un bean coincide con el nombre de la propiedad, éste se autoconectará con la propiedad si lleva el campo autowire=”byName”. Como ejemplo podríamos
cambiar la definición de estudiante y carrera del ejemplo de la Inyección de dependencia:
<bean id="itig"
class="es.uah.uahdi.model.CarrerImp">
<property name="universityName" value="Universidad de Alcalá de
Henares"/>
<property name="name" value="Ingeniería Técnica en Informática de
Gestión"/>
</bean>
<bean id="juanito"
class="es.uah.uahdi.model.StudentImp">
<property name="name" value="Juan Aurelio Gonzalez"/>
<property name="carrer" ref="itig"/>
</bean>
Por el siguiente código:
<bean id="carrer"
class="es.uah.uahdi.model.CarrerImp">
<property name="universityName" value="Universidad de Alcalá de
Henares"/>
<property name="name" value="Ingeniería Técnica en Informática de
Gestión"/>
</bean>
<bean id="juanito"
class="es.uah.uahdi.model.StudentImp" autowire="byName">
<property name="name" value="Juan Aurelio Gonzalez"/>
</bean>
Cuando asignamos la auto-conexión por nombre estamos diciéndole a Spring que busque en el
contenedor beans no los mismo nombres que las propiedades. Las propiedades que definamos
de la manera convencional no se auto-conectarán. El ejemplo sólo podríamos auto-conectar la
misma carrera, si quisiésemos inyectar más beans de distinto tipo en otros lo tendríamos que
hacer sin auto-conexión.
Auto-conexión byType
La auto-conexión por tipo es similar a por nombre. Su funcionamiento es que Spring busca
beans del mismo tipo de la propiedad y las inyecta. En el caso de que Spring encuentre varias
se lanzará una excepción de tipo UnsatisfiedDepedencyException. Si en el ejemplo anterior
sólo podíamos auto-conectar aquellas propiedades cuyo nombre fuese el mismo que el del
bean y si queríamos conectar otras de distinto nombre tenía que ser con el método normal,
con la auto-conexión por tipo no podremos conectar ningún otro bean del mismo tipo. Es
decir, la propiedad que tenga autowire=”byType” obliga a que no haya más beans de la misma
clase en el contenedor.
<bean id=”exampleBean” class=”example.ExampleClass” autowire=”byType”/>
Auto-conexión constructor
La auto-conexión por constructor tiene las mismas limitaciones que la auto-conexión por tipo
pero sólo para las propiedades del constructor, Spring no intentará adivinar que bean autoconectar si hay más de uno del mismo tipo.
<bean id=”exampleBean” class=”example.ExampleClass” autowire=”constructor”/>
Auto-conexión autodetect
La conexión por auto-detección hace que Spring primero intente conectar mediante
constructor y luego mediante tipo.
<bean id=”exampleBean” class=”example.ExampleClass” autowire=”autodetect”/>
La auto-conexión default
Si en el tag de beans configuramos el campo default-autowire los beans que definamos en su
cuerpo se autoconectarán de esa forma. También se puede incluir explícitamente en el tag del
bean el valor default.
<beans default-autowire=”byType”>
…
</beans>
Si se especifica en el propio bean algo distinto se hará de esa manera.
La Auto-conexión no
Si se especifica en la propiedad del bean autowire=”no” no se hará autoconexión, incluso si en
el tag beans indiquemos lo contrario en el campo default-autowire, no se hará.
Configuración de la creación de los beans
A Spring se le pueden dar distintas directrices para alterar la simple creación del bean como
hasta ahora hemos hecho en los ejemplos. Las opciones serían las siguientes:



Inicializar un bean una vez ya creado y ejecutar código antes de su destrucción.
Crear beans desde métodos estáticos de fábrica en vez de constructores públicos.
Controlar el número de instancias de un bean. Una instancia por sesión, por petición,
por cada uso o por aplicación.
Delimitación de un bean
Si no se especifica nada, Spring instancia cada bean de forma única, por cada petición siempre
se entrega el mismo bean. Con el campo prototype del tag bean podemos alterar este
comportamiento.
Las delimitaciones de bean de Spring permiten declarar el límite bajo en el que se crean los
beans sin tener que codificar las reglas de limitación en la clase del bean.
Al igual que pasaba con bean, el término Singleton en Spring no es sinónimo del aplicado
tradicionalmente en java. Como veremos, a diferencia de éste, en Spring no son otorgados por
la obligatoriedad de la clase en sí, sino que Spring otorga este diseño de manera ajena a la
propia clase.
Típo de límite
Singleton
Prototype
Request
Session
Global-session
Limita a una única instancia por contenedor.
Es la opción predeterminada.
Permite que un bean sea instanciado
cualquier número de veces (se instanciará
cada vez que se use).
En Spring MVC es usado para que el bean se
instanciado una vez por petición HTTP
(request).
Al igual que request es usado en entorno web
y la instancia del bean durará lo mismo que
dure la sesión en el servidor.
Limita el bean a una sesión global en el
servidor en entornos web.
Dependiendo de las necesidades del sistema de información, unas veces será útil dejar la
configuración por defecto, es decir, la instanciación singleton y otras veces será útil tener
varias instancias con prototype. Con respecto al servidor web, más tarde veremos todas las
utilidades que podemos encontrar para la construcción de distintos beans.
Creación de beans con clases singleton
Esto es útil si se están usando clases de terceras partes cuyos métodos públicos sean estáticos.
Es decir, para la inclusión de clases tipo singleton dentro del contenedor de Spring. En Spring,
como ya hemos dicho, por defecto se crea una instancia de cada bean, pero no tiene por qué
ser de cada clase. Continuando con el anterior ejemplo, cambiemos la definición de la cafetería
de la universidad, ya que siempre va a ser la misma.
package es.uah.uahconnection.cafe.model;
public class Restaurant implements Local {
public void clean() {
System.out.println("El restaurante ha sido limpiado");
}
private static class RestaurantSingletonHolder {
static Restaurant instance = new Restaurant();
}
public static Restaurant getInstance() {
return RestaurantSingletonHolder.instance;
}
}
En el anterior ejemplo hemos definido una clase estática interna cuya función es devolver una
instancia de la clase restaurante, cumpliendo el patrón de diseño tradicional de singleton. Para
instanciar correctamente este bean en Spring le deberemos especificar en su definición el
campo factory-method el método de instanciación. El código xml sería el siguiente.
<bean id="cafeteriaPolitecnica"
class="es.uah.uahconnection.cafe.model.Restaurant"
factory-method="getInstance"/>
Uso de init-method y de destroy-method (inicializar y destruir)
Es posible que queramos ejecutar cierto código de inicialización o de destrucción al instanciar
un bean, ya sea para ponerlo en un estado en particular o para eliminar o limpiar ciertas
vicisitudes. Para ello (y para todo aquello que se nos pueda ocurrir), tenemos la posibilidad de
configurar dos campos en la declaración. Éstos son init-method y destroy-method. El primero
ejecutará el código del método al inicio y el segundo al final. Un ejemplo del código xml sería el
siguiente:
<bean id="exampleBean"
class="exampleClass"
init-method="initExample"
destroy-method="cleanUpExample"/>
También si tenemos varios beans a los cuales tenemos que inicializar y todos ellos tienen el
mismo nombre de método (usualmente init y clean) podríamos definirlo por defecto en el tag
de beans como muestra el código de ejemplo.
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd"
default-init-method="init"
default-destroy-method="clean">
Continuando con el ejemplo de la cafetería, algo muy importante para todo cocinero es
limpiarse las manos antes de cocinar y vestirse adecuadamente con el delantal. Cuando éste
acabe con su jornada laboral deberá quitarse el delantal. Así que, como queremos cocineros
limpios en la universidad modifiquemos el código del cocinero.
public class Kitchener implements Worker {
private Recipe recipe;//receta
private int diners;//comensales
public Kitchener() {
}
public void setRecipe(Recipe recipe) {
this.recipe = recipe;
}
public void setDiners(int diners) {
this.diners = diners;
}
public void work() throws WorkingException {
System.out.println("Se dispone a cocinar " + recipe.getName());
recipe.develop();
System.out.println("Para " + diners + " comensales");
}
public void init(){
System.out.println("El cocinero se pone el delantal");
System.out.println("El cocinero se lava las manos");
}
public void clean(){
System.out.println("El cocinero se quita el delantal");
}
}
Ahora la definición de cocinero sería tan simple como esta:
<bean id="alfredo" class="es.uah.uahconnection.cafe.model.Kitchener"
init-method="init" destroy-method="clean">
<property name="diners" value="5"/>
<property name="recipe">
<bean class="es.uah.uahconnection.cafe.model.PaellaValenciana"/>
</property>
</bean>
Como no sólo el cocinero manipula alimentos creemos que todos los chef deberían antes de
comenzar su jornada laboral limpiarse y ponerse vestimenta adecuada así como de la misma
forma, cuando terminen su jornada laboral cambiarse de ropa, con lo que todos los chef
implementarán el método init y también el método destroy.
Spring tiene una forma más útil de poder definir un método por defecto de inicio así como otro
de destrucción como ya hemos visto. El código perteneciente a esto sería:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd"
default-init-method="init"
default-destroy-method="clean">
Aún sigue teniendo Spring una forma más de hacer todo esto. Si la clase que definimos
implementa el interfaz InitializingBean y el DisposableBean. Utilizará los métodos
afterPropertiesSet y destroy, como ya comentamos al inicio de éste capítulo.
Vemos como sería el código del cocinero si implementase esto:
public class Kitchener implements Worker, InitializingBean, DisposableBean {
private Recipe recipe;//receta
private int diners;//comensales
public Kitchener() {
}
public void setRecipe(Recipe recipe) {
this.recipe = recipe;
}
public void setDiners(int diners) {
this.diners = diners;
}
public void work() throws WorkingException {
System.out.println("Se dispone a cocinar " + recipe.getName());
recipe.develop();
System.out.println("Para " + diners + " comensales");
}
public void init(){
System.out.println("El cocinero se pone el delantal");
System.out.println("El cocinero se lava las manos");
}
public void clean(){
System.out.println("El cocinero se quita el delantal");
}
public void afterPropertiesSet() throws Exception {
init();
}
public void destroy() throws Exception {
clean();
}
}
Como seguramente se haya percatado, esta opción liga el desarrollo de las clases de nuestro
sistema de información al framework de Spring, haciéndolas menos reutilizables. Es por ello
que aún que la opción esté y haga que nuestro xml de configuración sea más limpio esta
opción se descarte en muchas ocasiones.
Descargar