Spring Framework

En este artículo te hago una pequeña introducción al framework de Spring, ¿te animas? vamos a ello :)


Spring es un framework que se enfoca en JavaEE.


¿Por qué se utiliza Spring?

Spring se empezó a utilizar como una aproximación mejorada de Java Enterprise Edition, para la creación de aplicaciones empresariales.


Conceptos importantes:


Principio de inversión de dependencias (DIP):

  • Los módulos de alto nivel no deben depender de los módulos de bajo nivel.

  • Abstracción sobre implementación.

  • Nos permite tener un código más desacoplado.

  • El patrón de IoC (Inversión de Control) es una aplicación de este principio.


Inversión de Control (IoC):

  • Estilo de programación donde un agente externo controla el flujo de la aplicación.


Inyección de dependencias (DI):

  • Subtipo de IoC

  • Objetivo tener un código fácil de mantener


Spring Core Container

  • Es un IoC Container

  • Gestiona el ciclo de vida de los objetos


Ventajas:

  • Inyección de dependencias (Favorece el bajo acoplamiento)

  • Flexibilidad (integración con otras herramientas)

  • Código ordenado

  • Uso de anotaciones

  • Estándares de programación (importante en proyectos grandes)

  • Desarrollo sencillo con POJOs (Un POJO es una clase sencilla que ni hereda ni implementa)

  • Minimiza el código repetitivo (boilerplate code)

  • AOP (Programación orientada a aspectos)


Que saber antes de continuar con el artículo:

  • Core Java SE

  • OOP

  • Maven

  • Patrones de diseño


Hablemos ahora del Core de Spring, en la siguiente imagen te muestro los componentes que forman el Core:




Como veis tenemos la parte de Test, luego la parte de Core, a continuación elementos como AOP, Aspects..., y por último métodos de acceso a datos y la parte web, de momento no vamos a entrar más en detalle.


Primero que todo, ¿qué es un bean?


En Spring, los objetos que forman la columna vertebral de la aplicación y que se administran mediante el contenedor Spring IoC se denominan beans. Un bean es un objeto que se crea una instancia, se ensambla y se administra de otro modo mediante un contenedor de IoC. Es decir, los servicios, repositorios, etc, de nuestra aplicación.


Inversion Of Control (IoC):


Invierte el flujo de control del programa externalizando en un agente (en este caso un framework) la construcción y el manejo de objetos.


De esta forma el contexto de spring definido en un XML se encarga de la creación de objetos, pasándole el nombre del bean por parámetro:

public class Main {
    public static void main () {
        Object obj = context.getBeans("bean-name");
    }
}


Inyección de dependencias (DI):


Delegar en otra clase la responsabilidad de la creación de los objetos, es decir, en vez de crearlas con el operador new, la inyectamos directamente en la clase, por lo que no habría que crear una instancia de ella.


NOTA: El @Autowired es mejor ponerlo encima del constructor y dejar la propiedad sin la anotación.


Con el Autowired, inyectamos la clase:

public class Bean {
    @Autowired
    InyectedClass inyectedClass;
}


Hay 2 formas fundamentales de crear inyección de dependencias en Spring.

  • Mediante constructor

  • Mediante setter


*La explicación del Autowired se verá en las anotaciones, más adelante*


Ejemplo mediante constructor:


Creamos la clase Informes por ejemplo que será nuestra dependencia.

public class Informes () {
    void method1();
    void method2();
}

Y en nuestra clase Empleado, hacemos lo siguiente:

public class Empleado {
    private Informe informe;

    public Empleado (Informe informe) {
        this.informe = informe;
    }
}

Ahora vamos al archivo XML application-context.xml a configurar la dependencia

<bean id="dependencia" class="package.Informes">

<bean id="clase" class="package.Empleado" >
 <constructor-arg ref="dependencia"></constructor-arg>
</bean>

Mediante el ref en constructor-arg le indicamos que inyecte la dependencia al crear el bean. Notar que no se ha usado en ningún momento el operador new.


Ejemplo mediante setter:


Teniendo las clases de antes como punto de partida.

public class Empleado {
 
 private Informes informes;

 public void setInformes(Informes informes) {
  this.informes = informes;
 }
}

Ahora vamos al archivo XML application-context.xml a configurar la dependencia

<bean id="dependencia" class="package.Informes">

<bean id="clase" class="package.Empleado" >
 <property name="informes" ref="clase"></property>
</bean>

El name de property debe ser el nombre del método setter sin el set y la primera letra en minúscula, ref se refiere al id del bean.


Inyección de campos:


Cuando un objeto necesite ciertos campos que puedan ser comunes con otros objetos.


Tomando como ejemplo nuestras clases anteriores, creamos los campos primero en nuestra clase:

public class Empleado {

 private String email;
 private String nombreEmpresa;

 setEmail();
 setNombreEmpresa();
}


Luego en el XML, especificando en el name de las propiedades los nombres de los métodos setter de la misma forma que antes, sin set y primera letra minúscula.:

<bean id="dependencia" class="package.Informes">

<bean id="clase" class="package.Empleado" >
 <property name="informes" ref="clase"></property>
 <property name="email" value="nombre@email.com"></property>
 <property name="nombreEmpresa" value="Nombre"></property>
</bean>


Luego en la clase principal despues de cargar el contexto a traves del XML, podemos obtener el bean con:

ApplicationContext contexto = new ClassPathXmlApplicationContext("app-context.xml")

Clase obj = contexto.getBean("id del bean");

Primero se carga el contexto desde el XML y cuando se ha hecho uso de él y ya no se va a utilizar se tiene que cerrar con:

contexto.close()

Ciclo de vida de un bean:


Qué tareas se realizan antes y después de la creación de un bean.


Esquema:


Existen dos métodos principales:


Init: Se ejecuta antes de que el bean esté listo para su uso.

  • Crear previamente otros beans para usarlo

  • Activación retardada de servicios

  • Abrir la conexión con BBDD, para que esté abierta antes de usarlo

Destroy: Se ejecuta luego de que el bean haya sido utilizado.

  • Liberar recursos

  • Cerrar conexiones de BBDD

  • Etc.


Ejemplo método init:

// Normalmente son de tipo void, pero no tiene porque ser asi.

public void miMetodoInicial() {
 // Tareas a ejecutar
}


Ejemplo método destroy:

public void miMetodoFinall() {
 // Tareas a ejecutar
}


Ahora en el XML de application-context.xml, dentro del bean donde hemos creado dichos métodos.

<bean id="dependencia" class="package.Informes">

<bean id="clase" class="package.Empleado" init-method="miMetodoInicial" destroy-method="miMetodoFinal">
 <property name="informes" ref="clase"></property>
 <property name="email" value="nombre@email.com"></property>
 <property name="nombreEmpresa" value="Nombre"></property>

</bean>

Java annotations


¿Que son?

  • Son anotaciones en forma de etiquetas que se añaden a nuestras clases, métodos o variables.

¿Para qué sirven?

  • Para añadir metadatos. Los metadatos son un conjunto de datos que describen la naturaleza del objeto.


Las anotaciones se pueden procesar en tiempo de compilación o ejecución.


Spring escanea en segundo plano todo nuestro código en busca de las anotaciones, pero hay que preparar primero el fichero xml de configuración, y ya Spring registra el bean, sin escribir nada más en el xml.


Lo primero es configurar el XML

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

Con esto Spring ya sabe qué tiene que escanear en el paquete que le hemos indicado las anotaciones.


Ahora creamos una clase a modo de ejemplo, con su anotación.

@Component("id del bean")
public class Empleado {
	...
}

@Component
public class Empleado {
...
}


Si dejamos el @Component sin id, coge el nombre de la clase como id, con la primera letra minúscula.


Y desde la clase principal obtenemos el bean:

Empleado empleado = contexto.getBean("id del bean", Empleado.class);

Todo muy sencillo.


Ahora veremos la anotación de Autowired.


Ejemplo de uso:


Creamos una interfaz:

public interface Informes {
 String getInforme();
}

Y la clase que implementa dicha interfaz.

public class InformeFinanciero implements Informes {
 
 public String getInforme() { ... }
}


Ahora supongamos que el Empleado necesita crear un informe financiero, pues inyectariamos la dependencia de la interfaz Informes en el constructor con Autowired, que lo que va a hacer es buscar en segundo plano alguna clase que implemente dicha interfaz e inyectarla, en este caso existe solo la clase InformeFinanciero.


Si existiera más de una clase que implementa dicha interfaz, más adelante veremos como Spring resuelve esto. De momento dejamos el ejemplo antes mencionado.




public class Empleado {
 private Informes informes;
 // Esto nos va a devolver la clase InformeFinanciero, dado que es la única que implementa la interfaz.
 @Autowired
 public Empleado (Informes informes) { this.informes = informes }
}


NOTA: A partir de la versión 4.3 de Spring Framework, si la clase solo tiene UN solo constructor la anotación @Autowired no es necesaria, ya que lo hace automáticamente.


También se puede utilizar Autowired con un método setter:



public class Empleado {
 private Informes informes;

 @Autowired
 public setInforme (Informes informes) { this.informes = informes }
}

Por último, podemos usar la anotación sólo en el campo de la clase, es decir, de la siguiente manera:

public class Empleado {

 @Autowired
 private Informes informes;
 
 public Empleado (Informes informes) { this.informes = informes }

}


En este último caso, lo que está sucediendo es lo que se denomina como Reflexión, es más, en este último caso se podría prescindir del constructor y no instanciar el objeto.


¿Qué ocurre cuando tengo varias implementaciones y quiero utilizar el Autowired?

Para ello utilizaremos la anotación @Qualifier.


Supongamos que tenemos la interfaz Informes y estas 3 clases.

@Component
public class Impl1 implements Informes { }

@Component
public class Impl2 implements Informes { }

@Component
public class Impl3 implements Informes { }


Al usar ahora el autowired, ahora nos saldrá un error del tipo expected single matching bean but found 3


En nuestra clase donde usamos el Autowired hacemos lo siguiente:

@Autowired
@Qualifier("id del bean")
private Informes informes;


Y así cogerá el que le hayamos dicho.


Ahora veremos el uso de @Scope, por defecto Spring crea todos sus beans como Singleton, es decir, que solo existirá una única instancia en memoria de dicho objeto y lo reutilizará, si quisiéramos que no sea así y que trabaje con el patrón de prototype, que nos generará objetos diferentes en memoria, lo único que debemos hacer es esto en el bean que queremos que sea así:

@Component
@Scope("prototype")
public class BeanDeSpring { ... }

Como vemos, es muy sencillo.


Ejecución de código antes de la creación del bean y después de su uso mediante anotaciones: como vimos anteriormente se puede hacer con xml, pero ahora usaremos anotaciones.


@PostConstruct y @PreDestroy


@PostConstruct: ejecutar antes de que el bean esté listo para su uso.

@PreDestroy: ejecutar después de que el bean haya sido usado.


NOTA: No se puede trabajar con prototype y estas anotaciones. Porque con el patrón prototype spring no controla todo el ciclo de vida del bean.


Ejemplo de uso:

@PostConstruct
public void method() { ... }

@PreDestroy
public void method() { ... }

No deben recibir ningún tipo de argumentos, y pueden tener el modificador de acceso que queramos, normalmente son void.


Configuración de Spring sin el archivo XML:


@Configuration


Esto nos permite configurar Spring a través de Java, prescindiendo del archivo XML.


Creamos la clase y añadimos @Configuration y @ComponentScan para que escanee los beans que existen en el paquete correspondiente.

@Configuration
@ComponentScan("package")
public class ClaseConfiguration { ... }

Ahora en la clase principal para leer la configuración:

AnnotationConfigApplicationContext contexto = new AnnotationConfigApplicationContext(ClaseConfiguration.class);

Ahora vamos a hacer uso de la etiqueta @Bean, para definir beans en la clase de configuración, como vemos en la siguiente imagen, a modo de ejemplo.



Esto sería útil para prescindir de la etiqueta @Component en la clase que queremos que sea el bean.



@PropertySource y @Value para inyectar valores de propiedades desde un archivo externo.


Creamos un fichero con el nombre y la extensión que sea, tenemos que escribir parejas de valores:


...

email=ejemplo@ejemplo.com

telefono=123456789


Lo guardamos dentro de la carpeta src.


Ahora en la clase de configuración.

@PropertySource("classpath:nombrefichero")
public class Config { ... }

En el bean que queramos usarlo:

public class Bean { 
 @Value("${email}")
 private String email;
 @Value("${telefono}")
 private String telefonoDelBean;
}

Con @Value estamos indicando el nombre de la propiedad del fichero que creamos antes, de esta forma sustituira ese campo por el valor del fichero.


Y hemos llegado al final, espero que te haya gustado este repaso por el mundo de Spring Framework, nos vemos en la próxima :)






107 vistas

©2020 por Juanma Perez.