martes, 24 de septiembre de 2013

Spring: Introducción + Inyección de dependencias

Recorriendo Spring

Es un framework que tiene mucha vida en el mundo JAVA desde hace mucho tiempo, nació en primera instancia como un JAR que proporcionaba soluciones mágicas a programadores en años cuando compilar y trabajar en java requería de mucho tiempo y paciencia por lo limitado del hardware con el que se trabajaba.

Mas Historia,.... un loco durante algunos años  escribió sobre como seria programar de una manera mas facil en J2EE, mediante el desacoplamiento  ,la reutilizacion de codigo y por supuesto un codigo mucho mas limpio. Este muchacho se llama Rod Johnson quien lógicamente ante esta serie de análisis construyo spring  para hacer la vida de todos mas facil.


Nacio con el nombre de interface en correspondencia a su empresa propietaria interface 21, evoluciono mucho con el tiempo hasta convertirse en spring source y la búsqueda de inversores en años venideros convirtió a sus creadores en millonarios por la compra de un gigante de la virtualizacion como vmware. 


Cual es el  secreto de su exito ?


  la comunidad de programadores que hoy impulsan la utilización de este framework en el mundo java y por supuesto las virtudes de este framework que puede ser utilizado en todo tipo de desarrollos, aqui es donde va la aclaración si bien spring cuenta con funcionalidades fuertes muy dedicadas al desarrollo web, su core puede ser utilizado en aplicacion swing sin problema, es mas en la actualidad es utilizado en desarrollo mobile sin problema.


Por que elegir spring ?

Por un lado la capacidad de poder realizar un desarrollo por completo, dado que simplemente hoy proporciona soluciones funcionales para toda necesidad que se pueda tener en nuestro desarrollo.

Flexible: si quiero pongo lo que quiera de spring y tengo la posibilidad  integración con otros framework muy populares sin problema. Si quiero utilizo spring en su todo, o si quiero solo el core o alguno de sus  subproyecto potentes. Es decir la herramienta no nos obliga a utilizarlo en su magnitud sino que podemos utilizar lo que nos guste. Si utilizo su soporte a base de datos, no estoy obligado a utilizar su MVC y viceversa. 


Altamente Cohesivo : Se dedica a lo que tiene que hacer de manera dedicada,  es decir los subproyectos como  JDBC es especifico para dicha función, si quiero sobre webservice o MVC también lo son.  Es atómico en sus funciones, convirtiéndolo en reutilizable en desarrollo en cualquier fase. Con esto puedo insertar spring en nuevos desarrollos o en mantenimientos que requieran de nuevos desarrollo sin problema.  


Débilmente acoplado:  no me fuerza a utilizar el framework, sino que puedo  utilizar sus  proyectos como lo necesite.


Lo primero que hay que dejar claro es que no es un framework de desarrollo web. Efectivamente, tiene módulos para desarrollo web como Spring MVC y Spring Web Flow que están entre los mejores, pero, por encima de eso, Spring es un framework para el desarrollo de aplicaciones empresariales Java, sean web o no. La idea es que Spring se encargue de las labores de infraestructura para que los desarrolladores se centren en resolver los problemas del dominio.


Entre algunas de sus virtudes nos proporciona:



  1. Soporte Core:
    1. Configuración de aplicaciones: normalmente una aplicación empresarial está compuesta por diferentes módulos que interaccionan entre sí. Spring nos da herramientas para conectar estos módulos y poder intercambiarlos de forma sencilla. En Spring, las partes que componen una aplicación son simples POJOs y veremos como siempre se nos recomienda trabajar con interfaces cuando sea posible, para facilitar el reemplazo de módulos.
    2. Integración empresarial: las aplicaciones empresariales requieren recursos y servicios como seguridad, transaccionalidad, mensajería, acceso remoto, caché, etc… Spring nos ayuda a integrar todo esto de forma que impacte lo menos posible en el código, manteniéndolo lo más limpio posible. También hace más sencillo el desarrollo de Web Services o el trabajo con tareas programadas.
    3. Testing: soporte para test unitarios y test de integración.
    4. Acceso a datos: simplifica la capa de acceso a base de datos y proporciona integración con las principales tecnologías como JDBC, Hibernate, JPA, Ibatis…
  2. Soporte Web: se integra de forma muy sencilla con frameworks de desarrollo web como JSF o Struts y, además, nos proporciona los suyos propios: Spring MVC Spring Web Flow.

Spring basa toda su filosofía en esos tres principios 

Triángulo de Spring


La idea es componer nuestra aplicación a base de objetos simples o POJOs. Normalmente éstos objetos no dependen mucho de la tecnología que utilicemos y eso es una buena práctica porque:
  • El código se centra en resolver lógica de negocio y no en integración. Esto permite que si cambiamos de tecnologías nuestra lógica no se vea afectada. Por ejemplo, si tenemos un módulo que hace uso de JDBC para acceder a los datos y mañana queremos cambiar a Hibernate, el impacto sobre nuestros POJOs debería ser mínimo.
  • Los programadores son más productivos, ya que invierten su tiempo en resolver los problemas del dominio y no en pegarse con diferentes tecnologías.
  • Es más sencillo realizar pruebas sobre estos objetos (tanto unitarias como de integración).
Para mantener nuestro objetos lo más simples posibles, Spring se apoya en los tres principios que hemos visto:
  • Inyección de Dependencias (o Inversión de Control): un objeto no se encarga de instanciar a sus colaboradores si no que éstos son inyectados por el framework. El famoso principio de Hollywood, "no me llames, nosotros te llamaremos" , es decir la posibilidad de una bolsa de objetos definida mediante diccionario xml de spring, para la utilización en toda nuestra aplicación, mediante la utilización de interfaces.  Si solo se conoce las dependencias vía interfaz, al cambiar la implementación de la dependencia el objeto dependiente no se entera que se hizo.   De esta forma, el objeto no depende de la tecnología con la que estén implementados y además es más fácil sustituirlos por tests.
  • Programación Orientada a Aspectos: llamamos aspectos a funcionalidades que son transversales a las aplicaciones (por ejemplo la seguridad o la transaccionalidad). Al trabajar con AOP estamos centralizando el código que implementa estas funcionalidades y configurando Spring para que las ejecute donde queramos, en lugar de tener que hacerlo en cada método que haga uso de ellas.

  • Abstracción de Servicios de Empresa: Spring se encarga de hacer transparente muchas tareas que hay que realizar al trabajar con servicios utilizados habitualmente en aplicaciones empresariales y que suelen resultar muy repetitivas. Por ejemplo al acceder a bases de datos mediante JDBC, él se encargará de gestionar las conexiones, unificar excepciones, etc…
Ahora con todo esto es mucho mas facil entender para que me sirve spring, y la respuesta seria simple para todo por su fortaleza, por un lado tendría:

  • Por medio de la abstracción lograría tener resuelto las tareas comunes como la conexión para la base de datos para mi backend, gestión de excepciones del mismo, la seguridad.  
  • Por medio de la programación orientada a aspecto tendría trazabilidad y código limpio en la ejecución de triguer sobre acciones que se realicen en mi negocio. 
  • Con la inyección de dependencias obtendría un modelo basado en contratos sin importar las implementacion obteniendo un desacoplamiento absoluto y un core alta mente escalable.

ahora todo es lindo bonito y barato por el momento pero la realidad es que este modelo se encuentra apoyado en el corazón spring por medio de su contenedor  beans o contenedor de inyección de dependencias.

Para construir nuestra aplicación, Spring necesita dos cosas: 

  • Las clases de la aplicación (POJOs, en adelante los llamaremos beans
  • Una configuración que podremos proporcionarle mediante ficheros XML, anotaciones o código Java
Con estas dos entradas, Spring levantará un contexto de aplicación(ApplicationContext). Este contexto es un interfaz cuya implementación se encargará de instanciar de nuestros beans, configurarlos y conectarlos:

Contenedor Spring

Entendiendo un poco mas de spring.
Tengamos claro que trabaja con Beans y su contenedor.

¿Qué es un bean?

La wikipedia define bean como un componente de software que tiene la particularidad de ser reutilizable.

En java cumplen varios criterios:
  1. Implementación en serie.
  2. Tener todos sus atributos privados (private).
  3. Tener métodos set() y get() públicos de los atributos privados que nos interese.
  4. Tener un constructor público por defecto
A diferencia de los bean convencionales que representan una clase, la particularidad de los beans de Spring es que son objetos creados y manejados por el contenedor Spring


Que es el contenedor de beans de Spring ?

El contenedor se encuentra en el núcleo del marco de trabajo de Spring y utiliza inyección de dependencias para gestionar los componentes que forman la aplicación. Se encarga de varias tareas, como crear, conectar y alojar los objetos definidos por los beans. Además hace de dispensador proporcionando beans por peticion. El contenedor carga las definiciones de beans escritas en archivos XML estructurados de forma ordenada.

Tipos de contenedor de Spring:

  • Fabrica de beans (bean factory): contenedor sencillo con soporte básico de inyeccion de dependencias.
  • Contexto de aplicacion (aplication context): es una implementacion de la bean factory que proporciona opciones avanzadas como por ejemplo: medios para resolver mensajes de texto e internalizacion, publicación de beans registrados como receptores o formas genéricas de abrir recursos de archivo.


 Curso de vida de la bean en Spring

Otra diferencia de los beans de Spring es que a éstos se añade un ciclo nuevo para que la bean sepa cual es su contexto de aplicación. Podemos ordenar las fases de la vida de un bean de la siguiente forma:


  1. Instanciación
  2. Inyección de las propiedades
  3. Nombre del bean
  4. Nombre de la fábrica
  5. Postprocesado (pre inicializacion)
  6. Inicialización
  7. Postprocesado (post inicialización)
  8. Bean listo para uso
  9. Destrucción
Esta un poco pesado de entender estos pasos por lo que vamos a intentar explicar esto de una manera mas practica 

Inicialización

Durante la inicialización existen dos pasos bien diferenciados:
  • Carga de definiciones de beans: se parsean los ficheros XML, se carga su definición en el BeanFactory del contexto de aplicación (indexados por el id que les hayamos dado) y por último se invocan losBeanFactoryPostProcessor. Éstos pueden modificar la definición de los beans tras haber sido cargada pero antes de que se instancien. Reasultan útiles en muchas situaciones, uno de los más utilizados es elPropertyPlaceholderConfigurer que sustituye variables en los ficheros de configuración por valores de ficheros .properties. Para crear los nuestros propios basta con implementar el interfaz BeanFactoryPostProcessor. No hay que confundirlos con los BeanPostProcessor, veremos estos a continuación.
  • Creación e inicialización de beans: por defecto, para cada bean definido y en el orden adecuado, Spring crea una instancia de éste e inyecta sus dependencias (por constructor y por setters). Tras esto, cada bean pasa por una serie de BeanPostProcessor donde puede realizarse configuración adicional. Después de pasar por los BeanPostProcessor, el bean estará preparado para su uso. Existen dos tipos de  BeanPostProcessor:
    • Inicializadores: realizan inicialización adicional a la inyección de dependencias. A partir de Spring 2.5, la forma común de definirlos es utilizando la notación @PostConstruct. Podemos anotar tantos métodos como queramos dentro de un mismo bean pero no se garantiza en orden en el que serán ejecutados.
    • El resto: configuración adicional a la inicialización, pueden ejecutarse antes o después de ésta. Un ejemplo común es la anotación @Required que indica que una inyección de dependencia es obligatoria.
Es posible implementar el interfaz BeanPostProcessor para crear los nuestros propios, sin embargo no es algo muy común (aunque resulta una opción muy potente).
Con esto termina la fase de inicialización y ya tenemos los beans preparados para su utilizarlos.
Formas de crear un bean en Spring

Bean simples: sin atributos
Bean con inyección por constructor: pasando los atributos por constructor
Bean con referencias de objeto de constructores: cuando pasamos un bean como atributo del constructor de otro.
Bean con inyección de propiedades: cuando en vez del método constructor utilizamos setters de atributos.
  • Con valores simples: Enteros, reales, Cadenas…
  • Con valores complejos:
  • Por referencia de otro objeto: pasando un bean id al método set.
  • Colecciones de datos: listas, arrays, maps…
  • Con valor nulo: cuando necesitamos pasar un valor nulo.

Ahora esto parece simple pero como trasladarlo a la practica ? para esto realizaremos un ejemplo practico con acceso a datos que permite una visión mas real de los beneficios de la inyección

Entendiendo Spring en la Practica.

Problemática: En el Hospital del Conocimiento se requiere listar todos los pacientes de una habitación.

La arquitectura que usaremos sera una arquitectura tradicional basada en capas apoyada en la utilizacion de contratos o interfaces:
  • Services Layer: Expondrá mi negocio a un front-End
  • Data Acces Layer: Donde accederemos a la Base de datos.
Dado que el caso de estudio no es como trabajar con base de datos, utilizaremos el soporte de spring para trabajar con base de datos embebidas con JDBC. 

Para obtención de conocimiento, mirar un poco aqui.  

Para nuestro ejemplo utilizaremos HSQL que es una base de dato embebida muy utilizada en tiempos de desarrollo por desarrolladores java, si quiere jugar un poco mira aqui que de seguro encontraras mucha información.

En primera instancia vamos crear un proyecto Maven para los que por supuesto lo podremos realizar con:

  • Maven por linea de comando (en el blog podremos encontrar algunas entradas a este tema).
  • Con M2ECLIPSE que tiene que estar instalado en nuestro eclipse
  • SPRING TOOLS SUITE que es la herramienta que biene con todo instalado para trabajar con spring y por supuesto entre algunas cosas M2ECLIPSE.


Dado que el caso de estudio es dar una mirada a la inyección de dependencias en esta ocasión con un proyecto de consola nos va alcanzar.

Paso 1 : Crear nuestro proyecto.






Paso 2: Definir nuestras dependencias  y trabajar con nuestro pom.




Primero definimos las propiedades de nuestro archivo para indicar versión de las dependencias.


Luego cargamos las dependencias de nuestro proyecto que en nuestro caso no serán muchas 

Core y Beans: librerías necesarias para trabajar con  spring. La dependencia beans es necesaria dado que es la que nos brinda la posibilidad de trabajar con el contenedor, core como todos ya sabemos es el corazon de spring.

jdbc: utilizaremos el soporte para trabajar con base de datos embebidas con jdbc,

HSQLDB: dependencia necesaria para trabajar con HSQL. 


Nota: como puede verse realizamos una modificación para que la versión del jar la tome del archivo de propiedades que previamente cargamos.

finalmente nuestro archivo pom.xml queda asi 



<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.hostipal</groupId>
  <artifactId>hospital_01</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <properties>
   <org.springframework.version>3.0.7.RELEASE</org.springframework.version>
  </properties>
  <dependencies>
  <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${org.springframework.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>${org.springframework.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>${org.springframework.version}</version>
        </dependency>
        
        <dependency>
      <groupId>org.hsqldb</groupId>
      <artifactId>hsqldb</artifactId>
      <version>2.0.0</version>
  </dependency>
  </dependencies>
</project>


Paso 3: Trabajar en la estructura de nuestro proyecto acorde a la arquitectura propuesta.




Pero comencemos por las entidades  o bien  por los famosos POJOS


com.hospital.Patient.java: a nivel código no encontramos nada raro en esta clase solo la definición de la estructura de datos de un paciente y sus correspondientes gett y sett.


package com.hospital.modelo;

public class Patient {
    
    private Integer id;
    private String name;
    private String lastName;
    private Integer roomId;

    public Patient(String name, String lastName) {
        this.name = name;
        this.lastName = lastName;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public Integer getRoomId() {
        return roomId;
    }

    public void setRoomId(Integer roomId) {
        this.roomId = roomId;
    }
}



com.hospital.Room.java: para esta clase una habitación tiene un identificador y solo un campo para la cantidad maxima de pacientes y sus gett y sett correspondientes. 

package com.hospital.modelo;

public class Room {
    
    private Integer id;
    private Integer maxPatients;

    public Room(Integer maxPatients) {
        this.maxPatients = maxPatients;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public Integer getMaxPatients() {
        return maxPatients;
    }

    public void setMaxPatients(Integer maxPatients) {
        this.maxPatients = maxPatients;
    }

}


Entendiendo Data Acces Objetc Layer.

com.hospital.dao.PatientDao.java: es una interfaz que limitara a la clase que lo implemente en sus metodos y en este caso solo tiene un método definido que recibe un numero de habitación y retorna una lista de pacientes.


package com.hospital.dao;

import java.util.List;

import com.hospital.modelo.Patient;

public interface PatientDao {
    
    List findByRoom(Integer roomId);
    
}


com.hospital.dao.PatientDaojdbc.java:  es la implementacion del contrato que antes describimos  y que por supuesto es el como mediante código devolverá la lista de pacientes, en este caso utilizaremos jdbc para implementar una solución pero la realidad es que podria ser hibernate, ebatis o cualquier forma de acceso a base de datos.

a nivel código la clase recibe un datasource por contructor y el metodo lo consume para realizar la operacion de consulta de los pacientes del modelo de datos y los retorna en lista.

 
import com.hospital.modelo.Patient;

public class PatientDaoJdbc implements PatientDao {

    private DataSource dataSource;

    public PatientDaoJdbc(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    public List findByRoom(Integer roomId) {

        List patients = new ArrayList();

        PreparedStatement query = null;

        String queryString = "SELECT ID, NAME, LASTNAME FROM T_PATIENT WHERE ROOMID=?";

        try {

            Connection connection = dataSource.getConnection();

            query = connection.prepareStatement(queryString);
            query.setInt(1, roomId);

            ResultSet rs = query.executeQuery();
            while (rs.next()) {
                Integer id = rs.getInt("ID");
                String name = rs.getString("NAME");
                String lastName = rs.getString("LASTNAME");
                Patient patient = new Patient(name,lastName);
                patient.setId(id);
                patient.setRoomId(roomId);
                patients.add(patient);
            }

        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            try {
                if (query != null) {
                    query.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

        return patients;

    }

}


Nota: lo lógico es que alguien externo deberá proporcionarme el datasource que no es otra cosa que la conexión al modelo de datos y aqui es donde comienza la magia de entender una arquitectura acoplada y desacoplada. Por que lamentablemente en una dependencia acoplada de seguro la capa superior me brindara que consumir, lo que me genera grandes problemáticas a la hora de que en un futuro el medio de datos cambie, por que de seguro deberá modificar mi capa de servicio en este escenario. 
Pero en una dependencia desacoplada debería tener un orquestador que en este caso es spring que me proporcione dicha información sin afectar a la capa de servicio, lo que hace escalable mi desarrollo y adaptable al cambio del medio de datos sin dolores de cabeza.

Entendiendo Services Layer.

No hay mucho que decir sera quien expondrá la funcionalidad a quien la quiera consumir y en nuestro caso para esta ocacion sera solo una consola, pero podría ser cualquier cosa, una aplicación web , una aplicacion swing.

com.hospital.servicio.PatientServic.java:  es nuestro contrato para las clases de implementacion del servicio y que a nivel código tiene un solo metodo que es buscar los pacientes, recibe un código de habitación y retorna una lista de pacientes.


package com.hospital.servicio;

import java.util.List;

import com.hospital.modelo.Patient;

public interface PatientService {
    
    public List findByRoom(Integer roomId);
    
}

com.hospital.servicio.PatientServiceImpl.java:  la clase utiliza la interfaz dao antes comentada para consumir un metodo para buscar los un codigo de habitación y retornar la lista de pacientes, pero hay un tema recibe un dao por contructor, lo que me hace pensar que esa referencia la voy a recibir de algún lado, pero por otro lado nunca esta mandando a la capa dao el datasource para que pueda trabajar, y no veo que el loco instancie nunca ninguna clase, se acabo la mentira este código no funciona y me comi todo el tutorial al pe..!!   


package com.hospital.servicio;

import java.util.List;

import com.hospital.dao.PatientDao;
import com.hospital.modelo.Patient;
import com.hospital.servicio.PatientService;

public class PatientServiceImpl implements PatientService{
    
    private PatientDao patientDao;

    public PatientServiceImpl(PatientDao patientDao) {
        this.patientDao = patientDao;
    }

    public List findByRoom(Integer roomId) {
        return patientDao.findByRoom(roomId);
    }
    
}

Nota: Aquí comienza la parte buena del fenómeno inyección de dependencias que de arranque es ni mas ni menos contar con una bolsa de objetos que puedan ser utilizables en toda mi aplicación y que permita comunicación con los componentes que conforman mi aplicación.

por que nadie instancia a nadie ? 
por sping
por que nadie le pasa el datasource para que la pobre capa dao pueda trabajar? 
por que lo hara spring.


Entendiendo spring bean configuration

Sin esto spring no tiene referencia de los beans de la aplicación,  es un archivo xml de configuracion donde definimos los bean para ser consumidos.

vamos a indicarle a Spring cómo ensamblar mediante un fichero XML que llamaremos application-config.xml (normalmente tendremos varios ficheros para las diferentes capas de la aplicación pero de momento utilizaremos sólo uno):


<?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:jdbc="http://www.springframework.org/schema/jdbc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
       http://www.springframework.org/schema/jdbc     http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd">
    <bean id="patientService" class="com.hospital.servicio.PatientServiceImpl">
        <constructor-arg ref="patientDao"/>
    </bean>
    <bean id="patientDao" class="com.hospital.dao.PatientDaoJdbc">
        <constructor-arg ref="dataSource"/>
    </bean>
    <jdbc:embedded-database id="dataSource" type="HSQL">
        <jdbc:script location="classpath:schema.sql"/>
        <jdbc:script location="classpath:test-data.sql"/>
    </jdbc:embedded-database>
</beans>



Entendiendo ApplicationContext

com.hospital.hospital.java:  la clase que consumira la capa servicio para obtener la lista de paciente acorde al numero de aplicacion que le indique.


package com.hospital;

import java.util.List;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.hospital.modelo.Patient;
import com.hospital.servicio.PatientService;

public class Hospital {

    public static void main(String [] args)
    {
       
        ApplicationContext context = new ClassPathXmlApplicationContext("application-config.xml");
        PatientService patientService = (PatientService) context.getBean("patientService");

        List patients = patientService.findByRoom(1);
        System.out.println("Pacientes en la habitacion 1: ");
        for(Patient patient : patients) {
            System.out.println("-->"+patient.getId()+","+patient.getName()+" "+patient.getLastName());
        }

    }

}

Nota: Como dijimos antes, ApplicationContext es un interfaz que tiene diferentes implementaciones, para el ejemplo utilizaremosClassPathXmlApplicationContext, que buscará los ficheros de configuración xml en el classpath de la aplicación:

solicita el bean de pacientes 
 PatientService patientService = (PatientService) context.getBean("patientService");

obtuvo la referencia de la instancia para poder ejecutar los metodos correspondientes
 List patients = patientService.findByRoom(1);

finalmente recorre la lista de pacientes que obtuvo.
        System.out.println("Pacientes en la habitacion 1: ");
        for(Patient patient : patients) {
            System.out.println("-->"+patient.getId()+","+patient.getName()+" "+patient.getLastName());

        }

Resultado 




Entendiendo la inyección de dependencias


¿Qué está ocurriendo en el contexto? Podemos ver que Spring se está encargando de gestionar el ciclo de vida de los objetos que componen nuestra aplicación: los instancia, los inicializa, los inyecta donde hacen falta en el orden correcto y los registra con el id que le indiquemos en el fichero de configuración para que podamos acceder a ellos. Además, el ApplicationContext actúa como un contenedor que encapsula la implementación de éstos. 

Por ahora vamos a centrarnos en cómo se definen los beans. Como hemos visto antes, normalmente esta configuración se detalla en un fichero XML dentro de la etiqueta beans, que, además, contendrá una serie de referencias a los espacios de nombres y esquemas que vamos a usar (normalmente esto lo generan los IDE, no es necesario saberlo de memoria). 

El bean más sencillo que podemos definir sería algo parecido a esto:

<bean id="service" class="example.ServiceImpl"/>

Por dentro Spring hará uso de reflexión para traducir esa definición a:
ServiceImpl service = new ServiceImpl();

Y, además, registrará en el ApplicationContext esa instancia al identificador “service”


Inyección por contructor

A continuación vamos a inyectar una dependencia a nuestro servicio:


<bean id="service" class="example.ServiceImpl">
 <constructor-arg ref="dao" />
</bean>
<bean id="dao" class="example.DaoImpl"/>

Resultado de la reflexión:
DaoImpl dao = new DaoImpl();
ServiceImpl service = new ServiceImpl(dao);


Inyeccion por Setter

A continuación vamos a inyectar una dependencia a nuestro servicio:

<bean id="service" class="example.ServiceImpl">
 <property name="dao" ref="dao" />
</bean>
<bean id="dao" class="example.DaoImpl"/>

Resultado de la reflexión:

DaoImpl dao = new DaoImpl();
ServiceImpl service = new ServiceImpl();
service.setDao(dao);

Hasta ahora hemos visto como inyectar otros beans, pero también es posible inyectar valores escalares:

<bean id="person" class="example.Person">
 <property name="name" value="John" />
 <property name="age" value="23" />
</bean>

Resultado de la reflexión:

Person person = new Person();
person.setName("John");
Integer age = 23;
person.setAge(age);

Existe cierta controversia entre cuál de las dos es más adecuada, en cualquier caso, Spring soporta ambas y se pueden utilizar de forma conjunta. La recomendación general es utilizar inyección por constructor en aquellas propiedades que sean obligatorias y por setter en las que sean opcionales.

Entendiendo finalmente el archivo de configuración de nuestro ejemplo

Es muy recomendable separar los beans de lógica de negocio de los de infraestructura para que no exista acoplamiento. En el ejemplo del hospital, el bean “dataSource” es un candidato idóneo para ser definido en otro fichero al que podríamos llamar “test-infrastructure-config.xml”  pero con los fines de educacion lo puse en un solo archivo de configuracion.

como podrán ver definimos un bean para el datasource que es el que me proporciona acceso a la base de datos embebida.
<jdbc:embedded-database id="dataSource" type="HSQL">
        <jdbc:script location="classpath:schema.sql"/>
        <jdbc:script location="classpath:test-data.sql"/>
    </jdbc:embedded-database>

luego definimos el bean patientDao que hace referencia a la implementacion dao que utiliza jdbc para pegarle a la base HSQL a quien le inyectamos el datasource, con lo que logramos desacoplar el tema del datasource que ya comentamos que alguien debía proporcionarlo y no esta definido en la capa de servicio.

<bean id="patientDao" class="com.hospital.dao.PatientDaoJdbc">
        <constructor-arg ref="dataSource"/>
    </bean>
Para finalizar definimos el bean para la implementacion del servicio a quien le inyectamos nuestro bean dao logrando la conectividad entre las capas de nuestra aplicacion.
<bean id="patientService" class="com.hospital.servicio.PatientServiceImpl">
        <constructor-arg ref="patientDao"/>
    </bean>



 Para finalizar pongo a disposición el código fuente del ejemplo, recuerden por favor que para levantarlo desde su entorno deben importar como proyecto maven.

Proyecto Ejemplo        

domingo, 8 de septiembre de 2013

SVN Trunk, Tags ,Branches y Buenas Practicas

Subversion (SVN) es una herramienta de control de versiones open source basada en un repositorio cuyo funcionamiento se asemeja enormemente al de un sistema de ficheros.
Utiliza el concepto de revisión para guardar los cambios producidos en el repositorio. Entre dos revisiones sólo guarda el conjunto de modificaciones (delta), optimizando así al máximo el uso de espacio en disco.
SVN permite al usuario crear, copiar y borrar carpetas con la misma flexibilidad con la que lo haría si estuviese en su disco duro local. Dada su flexibilidad, es necesaria la aplicación de buenas prácticas para llevar a cabo una correcta gestión de las versiones del software generado. El objetivo de este artículo es guiar al desarrollador para que sea capaz de tomar la mejor decisión en cada etapa del ciclo de vida de su proyecto.
Es importante recalcar que Subversion es una herramienta de Gestión de Versiones, y no de Gestión de la Configuración.


La estructura TTB se ha convertido en el estándar de facto en los repositorios SVN. TTB son las iniciales de las tres carpetas que compondrán el primer nivel de directorios del repositorio: Trunk, Tags y Branches. Cada carpeta tiene su funcionalidad específica, pero Subversion, al igual que un disco duro, las tratará por igual y no limitará las operaciones a realizar sobre ellos, por tanto conocer y aplicar las buenas prácticas ayudará a los usuarios a darles un uso correcto.
A continuación se listan las funcionalidades que se le debería dar a cada rama del repositorio:
  • Trunk: Rama de desarrollo principal.
  • Tags: Rama de gestión de versiones. Reservado para versiones cerradas, por tanto no se desarrollará sobre esta rama.
  • Branches: Rama con evoluciones paralelas al Trunk.
         
TTB, La Estructura Habitual Subversion  

Operaciones Habituales con Subversion

A continuación se presentan las operaciones más habituales con las que nos encontramos trabajando con Subversion.

Trabajo en Equipo: Se refiere a la situación en la que al menos dos personas modifican el código.

Por qué: Mientras otras herramientas obligan a bloquear zonas del repositorio cuando se estén realizando cambios en ellas, Subversion permite la modificación paralela de código del repositorio, de modo que varias personas pueden trabajar de forma simultánea sobre cualquier parte del código sin crear interferencias. En el caso de que dos desarrolladores modificasen el mismo elemento a la vez, Subversion integrará los cambios de forma automática, obligando al usuario a hacerlo de forma manual sólo en casos en los que el conocimiento humano es el único que puede asegurar la correcta integración.
Cuándo: Antes de hacer cualquier modificación en su entorno local, los desarrolladores deben asegurarse de estar trabajando con la última versión del software del repositorio. Lo mismo sucederá al finalizar un desarrollo: antes de persistir los cambios en el repositorio de Subversion se deberá asegurar que no se está interfiriendo con un desarrollo paralelo que ya haya sido guardado en el repositorio. Para esto se utilizará el mecanismo de sincronización de Subversion.
Buenas prácticas: Existen tres formas de sincronizar el código del entorno local con el del repositorio:
  • El comando Checkout descargará al entorno local una copia fiel del código del repositorio. Útil paracomenzar a desarrollar sobre proyectos nuevos.
  • El comando Update descargará al entorno local únicamente las modificaciones que hayan tenido lugar desde la última sincronización. Sólo se podrá hacer esta operación si se dispone ya de una versión local del código del repositorio.
  • El comando Commit actualizará el contenido del repositorio con los cambios del entorno local. Subversion sólo permitirá esta operación si no existen conflictos con el código ya existente en el repositorio. Es decir, no permitirá hacer Commit si otro miembro del equipo ha modificado el mismo elemento de forma paralela desde la última sincronización de código.
Para favorecer el trabajo en equipo, se recomienda la orientación del desarrollo a tareas de resolución a corto plazo. Aunque la gestión de las tareas se puede llevar en una hoja de cálculo o por correo electrónico, también existen herramientas específicas para la gestión de tareas en proyectos de desarrollo de software como Atlassian Jira, o Bugzilla. El funcionamiento de estas herramientas queda fuera del alcance de este artículo.
La dinámica habitual de trabajo deberá ser la siguiente:
  1. Antes de comenzar con la resolución de una tarea, se deberá asegurar la sincronización con el repositorio, bien con un Update o bien con un Checkout dependiendo de si se dispone previamente del código en el entorno local o no.
  2. Una vez resuelta la tarea, se deberá hacer otro Update para traer al entorno local los cambios que hayan podido ser realizados en paralelo al desarrollo actual. Subversion sabrá integrar los cambios del repositorio con los del entorno local en la mayoría de los casos, pero existirán situaciones que requieran de intervención humana para la integración. Estos casos, se deberán resolver de forma manual procurando mantener las modificaciones propias y las realizadas por los otros desarrolladores en paralelo.
  3. Finalmente se deberá hacer el Commit para hacer público al resto del equipo el código desarrollado. El alcance del Commit deberá limitarse al código relevante a la resolución de la tarea, y no mezclar desarrollos de distintas tareas en un mismo Commit.

Cierre de Versión (Creación de Tag)


Por qué: En ciertos momentos del ciclo de vida de un proyecto software puede ser conveniente el cierre de una versión para continuar con su evolución en el ámbito de la versión siguiente. Este cierre de versión nos permitirá volver a versiones anteriores en situaciones que lo requieran. Un ejemplo puede ser la necesidad de arreglar un bug tras una entrega, donde se deberá partir de la versión entregada en lugar de la versión actual en evolución, la cual podría encontrarse en una situación inestable. A este proceso de guardar una “foto” del estado del software en un momento dado también se le conoce como “congelar una versión”.
En lenguaje Subversion, el cierre de versión se denomina crear un Tag de la versión desarrollada. Esto implica llevar una copia de la versión a cerrar a la rama de gestión de versiones. Subversion maneja copias baratas para esto, es decir, sólo guarda una referencia a la rama y revisión que se desea copiar, lo que significa que el coste tanto en tiempo como en espacio en disco es bajo y constante (no es dependiente ni del número, ni del tamaño de los ficheros que componen la versión).
Cuándo: Dado el bajo coste de la creación de Tags para los cierres de versión, se recomienda que se realicen con cada hito del desarrollo del proyecto.
Buenas prácticas: Es importante no modificar nunca un Tag tras su creación, ya que se estaría perdiendo la referencia a la versión que en su momento se decidió congelar. Subversion no impide esta modificación, así que es responsabilidad de los desarrolladores el seguir esta buena práctica.
Una vez creado el Tag, se debe utilizar la rama donde se desarrolló la versión cerrada (bien el Trunk o bien un Branch) para la evolución hacia la siguiente versión.

Ramificación del Código (Creación de Branch)


Por qué: Existen situaciones en las que el ciclo de vida de un proyecto implica una evolución paralela de su código. Subversion habilita entornos disjuntos para estos desarrollos mediante la creación de Branches. Las modificaciones realizadas en los entornos paralelos pueden ser fusionadas en cualquier momento mediante la Fusión de Cambios que se explicará en el siguiente punto. Igual que en el caso de creación de Tags, Subversion también maneja copias baratas en las ramificaciones por lo que el coste es bajo.
Cuándo: La necesidad de bifurcar la evolución de un código puede surgir por diferentes motivos. El más habitual es la necesidad de seguir evolucionando un software al mismo tiempo que se corrigen los bugs que puedan surgir de la última versión puesta en producción. En este caso se necesitaría un branch evolutivo y otro correctivo. Otra situación puede ser la necesidad de realizar un gran número de modificaciones que durante su desarrollo obligarían a dejar el repositorio en un estado inestable, en cuyo caso se crearía un branch inestable hasta finalizar todas las modificaciones; u otras situaciones como que del proyecto surjan dos evoluciones de naturaleza distinta y por tanto no sea conveniente desarrollarlas de forma conjunta.
Buenas prácticas: Pese a ser un proceso de bajo coste como la creación de Tags, la ramificación del código debe realizarse sólo en casos en los que no exista alternativa a la creación de una nueva rama de desarrollo. La razón de esto es que a medida que crece el número de ramas, se hace más compleja la gestión de las versiones en desarrollo y puede llevar a los desarrolladores a perder la dirección inicial del proyecto.
Salvo en los casos en los que la ramificación de código sea con el objetivo de crear un proyecto nuevo a partir del código inicial, se deben considerar los Branches como ramas de desarrollo de vida limitada, es decir, tendrán un tiempo de vida tras el cual se deberá dejar de trabajar sobre ellos, bien por un Cierre de Versión o bien por la Fusión de Cambios a la rama de la que se hizo la ramificación.

Fusión de Cambios


Por qué: En muchos casos tras una ramificación, los cambios realizados en una rama se deben aplicar a algún desarrollo paralelo. Subversion facilita este proceso mediante el comando Merge, que aplica todos los cambios producidos entre dos revisiones en una rama a otra rama cualquiera del repositorio. En el caso de una bifurcación para la resolución de un bug en una rama correctiva de forma paralela al desarrollo de la siguiente versión en una rama evolutiva, deberán fusionarse los cambios realizados en la rama correctiva con los cambios que hayan surgido simultáneamente en la rama evolutiva.
Cuándo: Siempre tras la finalización de un desarrollo paralelo que afecte a alguna rama paralela.
Buenas prácticas: Se deberá hacer uso del comando Merge, ya que la aplicación manual de los cambios es tanto susceptible de error humano como mucho más costosa en tiempo. La alternativa a Merge es la aplicación de un Patch que tendría la misma función, pero está limitada a modificaciones en ficheros, mientras que Merge tiene en cuenta modificaciones tanto de ficheros como de directorios.

Ciclo de Vida con Subversion

A continuación se explicará paso a paso la transición de estados de un posible software siendo desarrollado usando Subversion como herramienta de control de versiones.
Ciclo de Vida con SubversionSe partirá de un código inicial que se evolucionará hasta cerrar una primera versión. Esta versión se llevará a producción y a partir de ahí se empezará a trabajar sobre la siguiente versión.
Para mostrar distintos escenarios de ramificaciones de código, se supondrá que se contrata un servicio de mantenimiento evolutivo del producto entregado, que tendrá que desarrollarse en paralelo a la evolución de la siguiente versión del producto y que además se detectará un pequeño bug en el producto que requerirá una corrección urgente, y que por tanto no podrá esperar a resolverse en una nueva versión del producto.
  • Rev. 10: Se supone un estado inicial con el código fuente de partida dado de alta en el Trunk. El resto del repositorio queda vacío.
  • Rev. 20: Evolución de la versión 1.0.0 del SW mediante Trabajo en Equipo sobre el Trunk.
  • Rev. 30: Cierre de Versión 1.0.0 del SW. Implica la creación de un Tag y el paso al desarrollo de la versión 2.0.0 en el Trunk. Suponemos la puesta en producción de la versión 1.0.0.
  • Rev. 40: Suponemos la contratación de un mantenimiento evolutivo sobre la versión puesta en producción. Esta evolución debe ser paralela al desarrollo de la versión 2.x del producto en el Trunk, por lo que necesitará una Ramificación del Código implicando la creación de un Branch para la versión 1.x.
  • Rev. 50: Suponemos la detección de un bug en la versión en producción. Para resolverlo se deberá partir del código puesto en producción, por tanto se deberá recuperar el Tag de la versión 1.0.0. Para no perder la referencia a esta versión tras la realización de cambios se deberá hacer unaRamificación del código para resolver el bug en un Branch y realizar los cambios en dicha rama, con el objetivo de crear la versión 1.0.1.
  • Revs. 60–80: Trabajo en Equipo paralelo en el Trunk para la versión 2.x, en un Branch evolutivo para la versión 1.x y en un Branch correctiva para la versión 1.0.x.
  • Rev. 90: Cierre de Versión 1.0.1 del SW. Se crea el Tag de la versión 1.0.1.
  • Revs. 100-110: Debido a que tanto el desarrollo de la versión 1.x en el Branch como el de la versión 2.x en el Trunk han partido de la versión 1.0.0 que contenía el bug resuelto en la versión 1.0.1 será altamente probable la existencia del mismo error en estas ramas. Por tanto, se deberá hacer unaFusión de Cambios de las modificaciones realizadas desde la versión 1.0.0 a la versión 1.0.1 con el estado actual del Trunk Branch.