====== Spring ======
[[http://www.springsource.org/spring-framework|Spring Framework]] es uno de los frameworks más usados en Java. Una pequeña historia sobre Spring la podemos encontrar en [[wpes>Spring_Framework|Spring Framework]].[[http://www.springsource.org|Spring]] consta de más de 20 proyectos opensource distintos aunque nosotros sólo vamos a usar [[http://www.springsource.org/spring-framework|Spring Framework]] que en adelante llamaremos simplemente Spring.
Spring contiene gran cantidad de funcionalidad pero nosotros únicamente vamos a usar su capacidad de realizar [[patrones:di|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 [[patrones:di|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 [[patrones:di|Inyección de Dependencias]] debemos explicar cómo realizar las siguientes 3 tareas:
* [[#Preparando Spring]]: Cómo inicializar Spring para que pueda funcionar.
* [[#Asignado Beans]]: Cómo indicarle a Spring que queremos que inyecte otro objeto en alguna propiedad de nuestro objeto .
* [[#Definiendo Beans]]: Cómo decirle a Spring los objetos que pueden ser inyectados.
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]]
* [[#Crear un objeto con el contexto de Spring desde Java]]
==== 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.
Como se puede ver sólo hemos definido un tag raiz llamado '''' 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 [[wpes>Espacio_de_nombres_XML|Espacio de nombres XML]] y en [[http://www.w3schools.com/xml/xml_namespaces.asp|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 org.springframework.context.ApplicationContext|ApplicationContext creando un objeto org.springframework.context.support.ClassPathXmlApplicationContext|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:
* Mediante el fichero de configuración ''applicationContext.xml''
* [[#Mediante las anotaciones]]
* [[#Mediante código]]
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 '''' en el ''applicationContext.xml''.
==== 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 org.springframework.context.ApplicationContext|ApplicationContext.
Si queremos asignar un bean a una variable o propiedad debemos llamar al método org.springframework.beans.factory.BeanFactory#getBean(java.lang.Class)|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 (( Realmente sí que la puede hacer usando AOP, pero es complejo de configurar )) 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 org.springframework.beans.factory.BeanFactory#getBean(java.lang.Class)|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]]
* [[#Mediante anotaciones]]
==== En el fichero applicationContext.xml ====
El tag '''' permite definir un bean que podrá ser usado por Spring. El atributo ''class'' indica de FQCN del bean.
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:
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.
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''.
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
@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 ''''.
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.