Tabla de Contenidos

Spring

Spring Framework es uno de los frameworks más usados en Java. Una pequeña historia sobre Spring la podemos encontrar en Spring Framework.Spring consta de más de 20 proyectos opensource distintos aunque nosotros sólo vamos a usar Spring Framework que en adelante llamaremos simplemente Spring.

Spring contiene gran cantidad de funcionalidad pero nosotros únicamente vamos a usar su capacidad de realizar Inyección de Dependencias y su framework para desarrollo Web llamado Spring MVC (el cual veremos en el siguiente tema).

¿Pero realmente para qué queremos usar Spring?

En el ejemplo anterior vimos cómo el código de las clases DAO quedaba acoplado a la clase HibernateUtil, ya que ejecutaban el código:

SessionFactory sessionFactory=HibernateUtil.getSessionFactory();

Ésto no es nada conveniente ya que en un futuro podríamos querar cambiar nuestra clase HibernateUtil por cualquier otra. Gracias a Spring podemos desacoplar esta dependencia.

También veíamos cómo al crear un objeto DAO en el código se quedaba programada la implementación que íbamos a usar ya que se ejecutaba el siguiente código:

ProfesorDAO profesorDAO=new ProfesorDAOImplHibernate();

Ésto no permite que se pueda cambiar fácilmente de una implementación a otra. Gracias a Spring podremos configurar muy fácilmente qué implementación usar sin modificar el código.

La Inyección de Dependencias no es un concepto que se aprenda de la noche a la mañana así que recomiendo leer la abundante documentación que hay sobre ello en Internet.

Para poder trabajar con el contenedor de Inyección de Dependencias debemos explicar cómo realizar las siguientes 3 tareas:

En la nomenclatura de Spring a los objetos se le llaman Beans

Preparando Spring

Spring necesita dos pasos para poder ser usado:

Crear un fichero de configuración

Spring necesita de un fichero xml donde configurar el framework. Dicho fichero suele llamarse applicationContext.xml y se suele colocar en el paquete raíz.

El siguiente fichero applicationContext.xml contiene lo mínimo para empezar a configurar Spring.

1 | applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       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.1.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
">
 
 
</beans>

Como se puede ver sólo hemos definido un tag raiz llamado <beans> y dos namespaces. A este fichero vacío le iremos añadiendo diversos tags para configurar Spring según nuestras necesidades.

Spring posee muchísimas opciones de configuración en el fichero applicationContext.xml pero aquí solo vamos a ver lo mínimo que necesitamos en nuestro proyecto para hacerlo funcionar y sin entrar en detalles.
Se puede encontrar más información sobre los namespaces de XML en Espacio de nombres XML y en W3Schools.XML Namespaces

Crear un objeto con el contexto de Spring desde Java

Junto con el fichero applicationContext.xml necesitamos inicializar Spring desde Java. Para ello deberemos ejecutar la siguiente línea de código al inicio de nuestra aplicación.

ApplicationContext context =new ClassPathXmlApplicationContext("classpath:applicationContext.xml");

Deberemos crear una nueva instancia de ApplicationContext creando un objeto ClassPathXmlApplicationContext.

Al constructor la estamos pasando el nombre del fichero de configuración de Spring pero delante le añadimos el texto classpath: para indicar a Spring que debe buscar el fichero en los paquetes java y no en el sistema de ficheros.

Asignado Beans

Hay tres formas de asignar un Bean a una propiedad/variable/método en Spring:

En este tutorial únicamente vamos a explicar las dos últimas.

Mediante las anotaciones

La anotación @Autowired indica a Spring que debe buscar un Bean y asignárselo a la propiedad a la que está asociada la anotación.

@Autowired
ProfesorDAO profesorDAO;

Con este código Spring buscará un Bean que implemente el interfaz ProfesorDAO y se lo asignará a la propiedad profesorDAO.

En caso de que Spring no encuentre ningún Bean que se pueda asignar a la propiedad se producirá la excepción:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [ejemplo04.persistencia.dao.ProfesorDAO] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

Desgraciadamente, con sólo poner esa anotación no es suficiente para que Spring le asigne un Bean. Necesitamos decirle que debe tener en cuenta dichas anotaciones. Para ello deberemos incluir en tag <context:annotation-config/> en el applicationContext.xml.

<context:annotation-config/>

Mediante código

Ya hemos visto en el apartado anterior cómo en el método main se debe inicializar el contexto de spring creando el objeto context del tipo ApplicationContext.

Si queremos asignar un bean a una variable o propiedad debemos llamar al método getBean(java.lang.Class) sobre la variable que contiene el contexto de Spring. Al método deberemos pasarle el tipo del Bean que queremos obtener.

ProfesorController profesorController=context.getBean(ProfesorController.class);

Con esta línea Spring buscará un Bean que sea compatible con la clase ProfesorController y se lo asignará a la variable profesorController.

¿No es un poco estúpida la línea anterior? No sería mucho mas fácil crear directamente el objeto con un new.
ProfesorController profesorController=new ProfesorController();

Desgracidamente Spring no hace magia 1) y necesitamos que Spring cree los objetos para que luego le podamos inyectar las dependencias. Si él no los crea no sabrá nada de ellos y no hará nada sobre ellos.

Así que como mínimo deberemos llamar una vez a getBean(java.lang.Class) para que cree el primer Bean gestionado por Spring y a partir de él se vayan inyectando el resto de objetos de la aplicación.De esa forma todos los objetos que se creen estarán gestionados por Spring.

Definiendo Beans

Spring permite dos formas de definir los beans

En el fichero applicationContext.xml

El tag <bean> permite definir un bean que podrá ser usado por Spring. El atributo class indica de FQCN del bean.

<bean class="ejemplo01.persistencia.dao.impl.ProfesorDAOImplHibernate" />
<bean class="ejemplo01.persistencia.dao.impl.UsuarioDAOImplHibernate" />    

En nuestro caso se han definido 2 beans correspondientes a las 2 clases ProfesorDAOImplHibernate y UsuarioDAOImplHibernate.

¿Qué significa realmente ésto? Que ahora mediante la anotación @Autowired sobre una propiedad, Spring automáticamente asignará a la propiedad una instancia del bean.

@Autowired
ProfesorDAO profesorDAO;

Cuando Spring vea que la propiedad profesorDAO es @Autowired buscará algún bean que se pueda asignar a esa propiedad. Como hemos definido el bean ProfesorDAOImplHibernate, entonces se le asignará una instancia de ProfesorDAOImplHibernate a la propiedad profesorDAO.

Lo mismo pasará con UsuarioDAO.

@Autowired    
UsuarioDAO usuarioDAO;

A la propiedad usuarioDAO se le asignará una instancia de UsuarioDAOImplHibernate.

Si hubiera más de un bean definido que se puede asignar a una misma propiedad se producirá una excepción.

Por ejemplo, si definimos dos beans que implementan el mismo interfaz:

<bean class="ejemplo01.persistencia.dao.impl.ProfesorDAOImplHibernate" />
<bean class="ejemplo01.persistencia.dao.impl.ProfesorDAOImplJDBC" />    

Al ejecutar el código Spring produciría la siguiente excepción:

org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [ejemplo04.persistencia.dao.ProfesorDAO] is defined: expected single matching bean but found 2: [ejemplo04.persistencia.dao.impl.ProfesorDAOImplHibernate#0, ejemplo04.persistencia.dao.impl.ProfesorDAOImplJDBC#0]

ya que no sabría si usar la implementación de Hibernate o de JDBC.

Métodos Factory

En muchos ocasiones nos encontraremos que, para crear el Bean Spring no debe crear la clase directamente sino llamar a algún método de otra clase para que cree dicho bean. Para ello Spring dispone del atributo factory-method que se llamará para crear el bean.

<bean class="com.fpmislata.persistencia.hibernate.HibernateUtil" factory-method="getSessionFactory"  />

En este caso le estamos indicando a Spring que llamando al método getSessionFactory() de la clase HibernateUtil se obtendrá un Bean de la clase SessionFactory que podrá ser asignado a cualquier propiedad que lo necesite.

Ahora en la clase GenericDAOImplHibernate obtendremos la referencia a SessionFactory mediante la línea:

| GenericDAOImplHibernate
@Autowired
SessionFactory sessionFactory;

Y deberemos eliminar la línea en la que llamábamos a HibernateUtil.

Recuerda que al poner el nombre del método en el atributo factory-method no deben de ponerse los paréntesis.
También quiero recalcar que al usar el atributo factory-method, el bean que estamos definiendo no es el indicado en el atributo class mediante su FQCN sino el Bean que se obtiene al llamar al método definido en factory-method.

Mediante anotaciones

Cualquier clase de Java la podemos anotar con la anotación @Component Éso indicará a Spring que dicho Bean puede ser asignado a una propiedad mediante @Autowired.

En nuestro ejemplo, los controladores se han anotado con dicha propiedad

1
@Component
public class ProfesorController {
 
    @Autowired
    ProfesorDAO profesorDAO;
 
    public void guardar(Profesor profesor) throws BussinessException {
 
            profesorDAO.saveOrUpdate(profesor);
    }
 
}

Vemos en la línea 1 la anotación @Component que indica que la clase ProfesorController es un Bean de Spring y que puede ser asignado a otras clases.

Desgraciadamente con sólo poner esa anotación no es suficiente para que Spring encuentre el Bean. Necesitamos decirle el paquete donde se encuentran las clases anotadas con @Component. Para ello necesitamos añadir en el fichero applicationContext.xml el tag <context:component-scan>.

<context:component-scan base-package="ejemplo01"/>

El atributo base-package contiene el paquete a partir del cual buscará clases anotadas con @Component. Spring busca tanto en ese paquete como en los subpaquetes de forma recursiva.

applicationContext.xml

En este último apartado mostraremos el fichero applicationContext.xml con todas las opciones que se han ido explicando.

<?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.1.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
">
 
    <!--Permite usar entre otras la anotación @Autowired -->
    <context:annotation-config/>
    <!--Permite usar entre otras las anotaciones @Component y @Controller -->
    <context:component-scan base-package="ejemplo01"/>
 
 
    <bean class="com.fpmislata.persistencia.hibernate.HibernateUtil" factory-method="getSessionFactory"  />    
    <bean class="ejemplo01.persistencia.dao.impl.ProfesorDAOImplHibernate" />
    <bean class="ejemplo01.persistencia.dao.impl.UsuarioDAOImplHibernate" />  
 
</beans>
1)
Realmente sí que la puede hacer usando AOP, pero es complejo de configurar