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.
Para poder trabajar con el contenedor de Inyección de Dependencias debemos explicar cómo realizar las siguientes 3 tareas:
Spring necesita dos pasos para poder ser usado:
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.
<?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.
applicationContext.xml
pero aquí solo vamos a ver lo mínimo que necesitamos en nuestro proyecto para hacerlo funcionar y sin entrar en detalles.
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.
Hay tres formas de asignar un Bean a una propiedad/variable/método en Spring:
applicationContext.xml
En este tutorial únicamente vamos a explicar las dos últimas.
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
.
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/>
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
.
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.
Spring permite dos formas de definir los beans
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
.
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.
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:
@Autowired
SessionFactory sessionFactory;
Y deberemos eliminar la línea en la que llamábamos a HibernateUtil
.
factory-method
no deben de ponerse los paréntesis.
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
.
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
@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.
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>