Herramientas de usuario

Herramientas del sitio


unidades:08_spring:02_spring
no way to compare when less than two revisions

Diferencias

Muestra las diferencias entre dos versiones de la página.


unidades:08_spring:02_spring [2023/04/07 21:26] (actual) – creado - editor externo 127.0.0.1
Línea 1: Línea 1:
 +====== 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:
 +<code java>
 +SessionFactory sessionFactory=HibernateUtil.getSessionFactory();
 +</code>
 +É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:
 +<code java>
 +ProfesorDAO profesorDAO=new ProfesorDAOImplHibernate();
 +</code>
 +É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.
 +
 +<note important>
 +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.
 +</note> 
 +
 +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.
 +
 +<note tip>En la nomenclatura de Spring a los objetos se le llaman Beans</note>
 +===== 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.
 +<code xml 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>
 +</code>
 +
 +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.
 +
 +<note tip>
 +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.
 +</note>
 +
 +<note tip>
 +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]]
 +</note>
 +
 +==== 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.
 +<code java>
 +ApplicationContext context =new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
 +</code>
 +
 +Deberemos crear una nueva instancia de <javadoc s31>org.springframework.context.ApplicationContext|ApplicationContext</javadoc> creando un objeto <javadoc s31>org.springframework.context.support.ClassPathXmlApplicationContext|ClassPathXmlApplicationContext</javadoc>
 +
 +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.
 +<code java>
 +@Autowired
 +ProfesorDAO profesorDAO;
 +</code>
 +Con este código Spring buscará un Bean que implemente el interfaz ''ProfesorDAO'' y se lo asignará a la propiedad ''profesorDAO''
 +
 +<note tip>
 +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)}
 +</note>
 +
 +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''.
 +
 +<code xml>
 +<context:annotation-config/>
 +</code>
 +
 +==== 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 <javadoc s31>org.springframework.context.ApplicationContext|ApplicationContext</javadoc>.
 +
 +Si queremos asignar un bean a una variable o propiedad debemos llamar al método <javadoc s31>org.springframework.beans.factory.BeanFactory#getBean(java.lang.Class)|getBean(java.lang.Class)</javadoc> sobre la variable que contiene el contexto de Spring. Al método deberemos pasarle el tipo del Bean que queremos obtener.
 +
 +<code java>
 +ProfesorController profesorController=context.getBean(ProfesorController.class);
 +</code>
 +Con esta línea Spring buscará un Bean que sea compatible con la clase ''ProfesorController'' y se lo asignará a la variable ''profesorController''.
 +
 +<note tip>
 +¿No es un poco estúpida la línea anterior? No sería mucho mas fácil crear directamente el objeto con un ''new''.
 +<code java>
 +ProfesorController profesorController=new ProfesorController();
 +</code>
 +
 +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  <javadoc s31>org.springframework.beans.factory.BeanFactory#getBean(java.lang.Class)|getBean(java.lang.Class)</javadoc> 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.
 +
 +</note>
 +
 +===== Definiendo Beans =====
 +Spring permite dos formas de definir los beans
 +  * [[#En el fichero applicationContext.xml]]
 +  * [[#Mediante anotaciones]]
 +
 +
 +==== 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.
 +
 +<code xml>
 +<bean class="ejemplo01.persistencia.dao.impl.ProfesorDAOImplHibernate" />
 +<bean class="ejemplo01.persistencia.dao.impl.UsuarioDAOImplHibernate" />    
 +</code>
 +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.
 +<code java>
 +@Autowired
 +ProfesorDAO profesorDAO;
 +</code>
 +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''.
 +<code java>
 +@Autowired    
 +UsuarioDAO usuarioDAO;
 +</code>
 +A la propiedad ''usuarioDAO'' se le asignará una instancia de ''UsuarioDAOImplHibernate''.
 +
 +<note important>
 +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:
 +<code xml>
 +<bean class="ejemplo01.persistencia.dao.impl.ProfesorDAOImplHibernate" />
 +<bean class="ejemplo01.persistencia.dao.impl.ProfesorDAOImplJDBC" />    
 +</code>
 +
 +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.
 +
 +</note>
 +
 +=== 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.
 +
 +<code xml>
 +<bean class="com.fpmislata.persistencia.hibernate.HibernateUtil" factory-method="getSessionFactory"  />
 +</code>
 +
 +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:
 +<code java | GenericDAOImplHibernate>
 +@Autowired
 +SessionFactory sessionFactory;
 +</code>
 +Y deberemos eliminar la línea en la que llamábamos a ''HibernateUtil''.
 +
 +<note tip>
 +Recuerda que al poner el nombre del método en el atributo ''factory-method'' no deben de ponerse los paréntesis.
 +</note>
 +
 +<note important>
 +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''.
 +</note>
 +==== 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
 +<code java 1>
 +@Component
 +public class ProfesorController {
 +
 +    @Autowired
 +    ProfesorDAO profesorDAO;
 +    
 +    public void guardar(Profesor profesor) throws BussinessException {
 +
 +            profesorDAO.saveOrUpdate(profesor);
 +    }
 +    
 +}
 +</code>
 +
 +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>''.
 +
 +<code xml>
 +<context:component-scan base-package="ejemplo01"/>
 +</code>
 +
 +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.
 +
 +<code 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
 +">
 +
 +    <!--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>
 +</code>
 +
  
unidades/08_spring/02_spring.txt · Última modificación: 2023/04/07 21:26 por 127.0.0.1