Spring framework. Desarrollo JEE ágil.

J2EE (que recientemente cambió su nombre a JEE) es una plataforma madura y soportada por las principales empresas de tecnología. Desarrollar aplicaciones empresariales usando las especificaciones JEE asegura en gran medida: escalabilidad, manejo transaccional robusto, e independencia de plataforma tanto de hardware como de sistema operativo. Sin embargo, JEE también tiene ciertas debilidades, las cuales se pueden evitar a través de la utilización del framework Spring.

Debilidades de JEE
Toda esa robustez que JEE nos provee, también genera un costo tanto en complejidad como en desempeño. Una aplicación JEE diseñada de acuerdo a los lineamientos de los Java BluePrints, requiere la utilización de numerosos y complejos patrones de diseño. Adicionalmente, las múltiples capas generadas pueden impactar el desempeño de las aplicaciones. Un ejemplo de esto es la aplicación de referencia “Java Pet Store”, que cuenta con un gran overhead y como resultado ofrece un desempeño degradado.

Mi intención no es eliminar el uso de patrones, si no más bien ser pragmático, aprovechar la robustez de JEE sin hacerlo tan complicado, y poder aplicar métodos ágiles como TestDriven Development (TDD). En fin, mi intención es usar otros enfoques de desarrollo.

Por otro lado, está el tema de los EJBs. Es común asociar de inmediato JEE con EJBs. Esto en cierta medida es correcto. pero JEE no es sólo EJBs. Muchos de nosotros hemos sufrido, perdón, usado Entity Beans en algún proyecto, lo que después de la pesadilla nos lleva a buscar alternativas. Los Entity Beans, por ser orientados a componentes, no se prestan a modelar relaciones entre ellos de manera adecuada desde el punto de vista de negocio. Para manejar la lógica de negocio, JEE propone la utilización de Session Beans en sus dos modalidades: con estado y sin estado. En la mayoría de los casos, esto implica mezclar código de negocio con APIs de EJBs. Adicionalmente, es complicado probar este código antes de que el EJB “viva” en un EJB Container. Se requiere utilizar herramientas adicionales como simuladores de contenedores.

Una Alternativa
Entre las alternativas disponibles para suplir las debilidades de JEE en persistencia, están Hibernate, iBatis, JDO, entre otros, incluso JDBC directo. Hibernate es un framework que permite modelar el dominio de la aplicación y mapear ese modelo de objetos Java a tablas de una base de datos relacional. Las clases Java que Hibernate necesita para hacer esto, son sencillamente clases planas Java, conocidas como POJOs (Plain Old Java Objects).

Y entonces, ¿por qué no usar POJOs para programar la lógica de negocio también? ¿Por que no usarlos para acceder otras fuentes de información, como directorios LDAP? Esto simplificaría el desarrollo e incluso permitiría probar el código desde la línea de comandos, facilitando esta labor y permitiendo que la cobertura de las pruebas sea mayor, lo que en el futuro evita sorpresas con el usuario.

El inconveniente con utilizar POJOs para todo es que nos puede incitar a programar código sumamente acoplado. Sin embargo, esto si seguimos el principio de diseño que dice “programa hacia una interfaz, no hacia una implementación”. Esto significa que debemos generar dependencias hacia interfaces en lugar de hacia clases concretas. Por ejemplo:

public interface DAO {
Cliente getClienteById(String id);
}

public class DaoImpl implements Dao {
public Cliente getClienteById(String id) { // Código necesario para obtener los datos del cliente. }
}

public interface ClienteService {
boolean isClienteMoroso(String idCliente);
}

public class ClienteServiceImpl implements ClienteService {
private Dao dao;
public Dao getDao() {
return this.dao }
public void setDao(Dao dao) {
this.dao = dao
}
public boolean isClienteMoroso(String idCliente) {
Cliente cliente = this.getDao.getClienteById(idCliente);
return cliente.getSaldo > 0;
}
}

En este ejemplo definimos dos interfaces. La primera, como su nombre lo indica, sigue el patrón de diseño DAO (Data Access Object). Este patrón se utiliza para encapsular lógica de acceso a datos, evitando así mezclarla con la lógica de negocio. La clase DaoImpl es una implementación de la interfaz Dao, así que contiene en el código necesario para acceder los datos, dependiendo de la tecnología que se esté utilizando.

La segunda interfaz (ClienteService) define un servicio de negocio, en este caso para saber si un cliente es moroso. La clase ClienteServiceImpl implementa esa interfaz y, como pueden observar, contiene una micro-lógica de negocio para determinar si un cliente es moroso. Observen que esta clase usa un DAO para acceder a la base de datos, solo que en vez de hacer la referencia a una clase concreta, se referencia a la interfaz. De esta manera se desacoplan las implementaciones.

Así que podemos tener múltiples DAOs y cada uno puede usar un mecanismo diferente para acceder la base de datos. Como mencionábamos anteriormente, uno podría utilizar Hibernate, otro JDBC, e incluso otro pudiera basarse en archivos de texto plano en caso de no haber una base de datos relacional. Este mecanismo se puede aplicar no sólo a las dependencias que tengan los servicios de negocio con DAOs, sino también de dependencias de servicios entre sí.

Con el uso de interfaces hemos resuelto el asunto del acoplamiento y la dependencia a clases concretas. Sin embargo, surge un nuevo inconveniente, y es que ahora requerimos de algún mecanismo para que los servicios de negocio puedan obtener la referencia a la clase concreta que implemente la interfaz DAO y así poder acceder a los datos. Si observan de nuevo, el código del servicio sólo contiene la lógica de negocio, no contiene código que “inicialice” o cree una instancia del DAO que necesita. Para resolver esto típicamente se usa el patrón de diseño Factory en sus múltiples variaciones. Pero al hacer esto, de nuevo podemos mezclar código de la lógica de negocio con código que bien puede ser de infraestructura de la aplicación. Adicionalmente, el patrón Factory por su naturaleza implica un cuello de botella, ya que el acceso a este debe de ser de manera sincronizada para evitar “colisiones” con otros hilos de la aplicación. Otro inconveniente de utilizar este patrón es que entonces es necesario crear una factoría para casi todo; es decir, una factoría para DAOs, otra para servicios, y así para todos aquellos objetos cuya instancia necesitemos definir.

Mejor Usemos Spring
Una alternativa a la problemática mencionada, y el principal interés de este artículo es hablarles del framework de desarrollo JSE/JEE Spring. Y es que aunque en este articulo empecé a hablar de desarrollo JEE, Spring es aplicable a desarrollos JSE, sin ningún problema.

Spring básicamente nos provee de un contenedor de objetos. En sentido un poco mas contextual, es un contenedor de beans. Como todo contenedor, el de Spring se encarga del ciclo de vida del bean, de su creación y en algunas implementaciones de su destrucción. Este contenedor es conocido como un Application Context. El ApplicationContext en sí, es una factoría de beans, porque en base a cierta definición, el contenedor se encarga de la creación de los beans.

Antes de seguir hablándoles del Application Context, quiero hablarles de un principio elemental en el cual Spring se basa. Este principio que es considerado un patrón de diseño por muchos, se llama Inversion Of Control (IoC) o Dependency Injection (DI). Martin Fowler explica de manera muy concisa la diferencia entre ambos conceptos, yo prefiero identificar a la “magia” de Spring como DependencyInjection.

¿Cómo Funciona la Inyección de Dependencias?
En Spring la DI funciona de una manera muy simple. En un archivo de texto plano se escriben etiquetas XML de definición de los beans que vivirán en el ApplicationContext. En este archivo, además de la definición de los beans, se definen las dependencias de los beans. Esto le permite a Spring hacer las instancias de los beans e inyectar las dependencias para que al momento de usarlos, no obtengamos un bonito NullPointerException.

Veamos el ejemplo de la definición de un bean en XML:


< !DOCTYPE beans PUBLIC “-//SPRING//DTD BEAN//EN” “http://www.springframework.org/dtd/spring-beans.dtd” >
< beans >
< bean id=”dao” class=”org.springhispano.persistence.dao.impl.DaoImpl” >
< /bean >
< bean id=”clienteService” class=”org.springhispano.services.impl.ClienteServiceImpl” >
< property name=”dao” >
< ref local=”dao” / >
< /property >
< /bean >
< /beans >

Con la anterior definición, Spring creará en su contenedor dos beans, uno para la clase de implementación del DAO y otro para el servicio de negocio. A la clase del servicio de negocio le inyectará la referencia del DAO concreto. Esto lo logra invocando el método setter de la propiedad DAO en el bean clienteServicio. Aquí es donde la inyección de dependencias trabaja; en nuestra aplicación le delegamos a Spring la creación de los objetos, o dicho de una manera más correcta, invertimos el control de la creación de objetos a Spring. Con esto se hace innecesario la programación de factorías de objetos y sobre todo la inclusión de código de infraestructura en las clases que programemos.

Algunas de las principales características de Spring, son:
• Contenedor IoC. Es el encargado de la configuración y manejo de los beans que viven en el contenedor. Es el corazón de Spring.
• Soporte para Programación Orientada a Aspectos (AOP). Spring provee un mecanismo basado en Proxies para implementar AOP en nuestras aplicaciones, aunque también podemos usar el poder de AspectJ.
• Acceso a datos simplificado. Spring brinda un enfoque consistente para acceso a datos, ya sea JDBC o a través de algunos frameworks para acceso a datos, como Hibernate, iBatis, JDO, TopLink, etc. Asimismo nos proporciona una jerarquía de excepciones que encapsulan las excepciones lanzadas por los diversos frameworks de acceso a datos, lo que permite en algún momento poder intercambiar de framework sin afectar el código ya escrito.
• Manejo transaccional. Spring provee una abstracción para trabajar con recursos transaccionales, ya sea JTA, transacciones JDBC, Hibernate, o algún otro API.
• Modulo Web: Spring provee un framework MVC basado en peticiones (request based).

Entre los beneficios que podemos obtener con su utilización están:
• Mejor diseño orientado a objetos en las aplicaciones.
• Mayor enfoque en el desarrollo de la lógica de negocio.
• Menor código de infraestructura necesario.
• Facilidad para realizar pruebas unitarias del código.
• Independencia del servidor de aplicaciones.
• No es necesario usar APIs de Spring para ponerlo a funcionar. Aunque en algunos casos seria una lastima si no usáramos las abstracciones que Spring nos provee con algunas de sus clases (TemplatesClasses, SupportClasses).

Aunque Spring provee funcionalidad para manejar las diversas capas de una aplicación (front end, lógica de negocio, acceso a datos), no es necesario usarlo para todo. Spring nos brinda la flexibilidad de utilizarlo solamente en la capa o capas que queramos.
Los invito a que se familiaricen con Spring, es un excelente framework. Aprovecho para invitar a los interesados y a los usuarios de Spring a que visiten el Spring User Group (SUG) springhispano.org, que tiene como finalidad ayudar de manera desinteresada a la adopción de Spring, así como el intercambio de experiencias y dar a conocer en dónde se está utilizando Spring.

Referencias • Martin Fowler. Inversion of Control Containers and the Dependency Injection pattern. www.martinfowler.com/articles/injection.html
• Rod Johnson, Juerger Holler, et. al. Java Development with the Spring Framework. Wiley Publishing, Inc., 2005

Acerca del autor
Domingo Suárez se desempeña como Gerente de Sistemas de Vigilancia en Bursatec. Es egresado de IPN-UPIICSA, lugar donde fundó el grupo de usuarios JavaUp.org. Así mismo es cofundador del Grupo de usuarios SpringHispano.org. Desarrolla software usando en su mayoría software libre y metodologías ágiles. domix@springhispano.org