El contenedor Spring
Introducción
Este capítulo presenta un uso simplificado de Spring, para que tengamos una visión general, sin perdernos en los detalles. Posteriormente, veremos aclaraciones sobre las partes que es necesario conocer para afrontar una aplicación más compleja. El ejemplo que lo ilustra ya permite experimentar con una gran parte de los problemas relacionados con el uso de este framework.
Origen
Vamos a bordar los principales componentes de Spring.
Spring es un framework que simplifica la programación. Se compone de un núcleo, Spring Core, que permite una administración sencilla de instancias de clase en memoria y librerías de clases que utilizan este núcleo.
A estas clases se las llama beans Spring. El framework proporciona un conjunto de beans preprogramados que cubren un espectro muy amplio de casos de uso, que encontramos cuando codificamos una aplicación compleja.
Son ampliables y fáciles de usar.
El núcleo del framework permite cargar un conjunto de singletons durante el arranque y facilita su acceso inyectando automáticamente su referencia (ubicación en memoria) en los objetos que los usan.
Spring permite tener un control muy fino sobre la gestión de objetos en memoria.
Spring interactúa con muchos frameworks y otros productos. El principal interés de Spring es la instanciación y la disponibilidad automatizada de beans. Estos objetos pueden ser de dos tipos: singletons, como ya hemos mencionado, así como objetos «duplicados» llamados «prototipos».
A diferencia de lo que sucede con el objeto Prototype, el objeto Singleton es un objeto compartido del que se crea una única instancia en un mismo contenedor Spring y cuyo uso es compartido. Spring gestiona internamente una lista de singletons instanciados. Si se debe...
Los módulos fundamentales
Los beans especializados que se muestran a continuación generalmente están presentes desde la versión 0.9 de Spring y han recibido mejoras a lo largo de las versiones. Se utilizan sobre todo cuando queremos extender Spring. Los verá principalmente en los frameworks. Cuando usamos anotaciones, dejamos de ver estos objetos, pero Spring los utiliza internamente.
1. Composición de un bean
Veremos más en detalle cómo funciona un bean en la programación orientada a aspectos con Spring dedicado a AOP.
Para simplificar, un bean se puede considerar como un proxy que extiende un objeto Java. El proxy permite:
-
capturar las llamadas a los métodos del objeto para añadir comportamientos,
-
añadir nuevos métodos,
-
gestionar vínculos a los objetos a los que se hace referencia en el objeto principal,
-
posibilidad de asignar valor a las propiedades del objeto de diferentes maneras: cadenas, archivos (a través de una factory),
-
gestionar mensajes en los bundles (a través del contexto),
-
gestionar eventos entre objetos: creación, destrucción o eventos de usuario.
El bean está orientado a datos (POJO) o procesamientos, intentando separar estos aspectos en beans especializados.
Por lo tanto, contiene:
-
la clase de implementación real del bean,
-
elementos de configuración conductual del bean, singleton/prototype,
-
beans relacionados con la inyección de dependencias, etc.,
-
valores de propiedad que se establecerán durante la construcción.
Cuando queremos usar un bean Spring, añadimos un miembro de clase e indicamos a Spring que queremos utilizarlo escribiendo la variable con la interfaz correspondiente al Bean. Spring tiene diferentes estrategias para encontrar el Bean adecuado que se va a inyectar.
Un bean se identifica por su nombre y su identificador. Los identificadores pueden tener alias, pero este uso es bastante confuso. Varios beans pueden tener el mismo identificador, por lo que se indicará a Spring cuál debe tener prioridad (@Primary).
2. El singleton y el prototipo
Un singleton, que es el tipo predeterminado de beans, solo tiene una instancia de implementación dentro de un contexto, mientras que el prototipo puede tener varias.
3. Los objetos fundamentales del paquete core
Este paquete gestiona la inyección de dependencias. Utiliza un determinado número...
Configuración de los beans
Como se ha mencionado, la configuración de los beans Spring puede ser implícita o explícita. En modo implícito, Spring reconoce qué clases Java debe añadir a la lista de beans a partir del marcado en las candidatas. En modo explícito, le decimos a Spring el nombre y la ubicación exacta de los beans. El modo explícito es más seguro, pero se tarda más en configurar. Por este motivo rara vez se usa. Los beans se pueden configurar mediante archivos XML, anotaciones o directamente en Java.
Posibilidad de declarar beans
Tipo |
Modo |
Uso |
Archivos XML |
Explícito |
Beanes personalizados externamente para variación por entorno. |
Código Java |
Implícito y explícito |
Beans de configuraciones estáticas. |
Anotaciones |
Implícito |
Beans que experimentan pocos cambios estructurales. |
Lambda |
Implícito |
Sin proxy (abordaremos este aspecto en detalle). |
Por lo tanto, por este medio podemos declarar un árbol de objetos que contenga objetos singleton y objetos prototype. También veremos que es posible tener una configuración diferente para la «run» y para las pruebas que, por ejemplo, tienen una configuración dedicada.
La configuración por XML presenta dificultades relacionadas con el uso de la configuración en formato texto. Spring debe hacer conversiones del texto al tipo de destino. Para indicar un número, lo metemos en una cadena, que posteriormente se convierte. Para indicar el nombre de una variable o argumento, también usamos texto. Los problemas solo son visibles en tiempo de ejecución, mientras que, si se configura en Java, los problemas se revelan directamente durante la fase de compilación.
Por lo tanto, primero usaremos la configuración en Java. Los ejemplos muestran todos los tipos de configuración para presentarlos.
1. Configuración mediante un archivo XML
Las aplicaciones sencillas o antiguas se configuran directamente en uno o más archivos XML, organizados de forma jerárquica. Por lo general, tenemos un archivo XML principal por contenedor Spring, junto con archivos XML periféricos.
En el siguiente ejemplo, se muestra cómo declarar un bean miServicio que coincida con la clase de implementación miPaquete.MiServicioImpl.
<Beans>
<Bean...
Uso de beans: inyección para setters y constructores
La aplicación dispone del bean cuando este se declara en la configuración. Entonces, podemos inyectarlo como miembro de nuestra clase.
Spring solo puede inyectar dependencias en un bean de Spring.
Los expertos dirán que, mediante la configuración de la JVM, es posible inyectar los beans donde quiera modificando el cargador de clases, pero, en un contexto de uso normal, nos limitaremos a los beans Spring.
Por lo tanto, el primer bean de nuestra aplicación se instanciará obligatoriamente por medio de una factory Spring que cargará el contexto y aplicará la configuración a ese bean. Este es el punto de partida de Spring en la aplicación.
Spring crea el objeto con el constructor predeterminado y, a continuación, llama a los setters para inyectar las referencias de los objetos que se van a inyectar. Si el constructor predeterminado no está disponible, Spring utiliza un constructor con argumentos, que también rellena las dependencias. Esta posibilidad se usa para las API externas a Spring que no tienen constructores predeterminados, como los pools de conexiones, para los que debemos pasar el datasource como argumento constructor.
Un constructor que contiene beans como argumentos permite hacer el equivalente de la anotación @Autowired en los miembros del bean. Por otro lado, es necesario tener un número limitado de argumentos en el constructor para mantener la legibilidad.
La anotación @Autowired se coloca en la variable o, preferiblemente, en el setter de la variable. Es posible especificar que la resolución se puede diferir al inicio a través del argumento required=false:
@Autowired(required=false)
Esto permite indicar a Spring que el bean que se va a mapear puede no estar disponible durante el inicio. Por ejemplo, si durante la instanciación (carga en la memoria) de un bean se necesita un elemento que no está disponible al inicio, es posible posponer su instanciación al momento en el que se utilice por primera vez. Un bean puede necesitar que se inicie la base de datos o que ya se haya producido una conexión a ella, por ejemplo, antes de que se pueda utilizar.
En caso de que no necesitemos utilizar la carga diferida, también es posible utilizar la anotación @Inject del JSR-330.
1. Asignación mediante el constructor en XML
El elemento...
Control del ciclo de vida: construcción y destrucción
Spring permite interceptar la creación y descripción de los objetos Spring.
No es posible utilizar los beans inyectados directamente en el constructor porque, en este preciso momento, Spring aún no ha instanciado los objetos contenidos en los miembros de la clase. Del mismo modo, la llamada al método finally no es sencilla porque es el garbage collector quien lo llama y el finally está deprecated (JEP 421). Spring da la posibilidad de marcar métodos para que se llamen durante la creación y destrucción del objeto. Hay varios mecanismos.
He aquí el más reciente.
Anotación |
Momento de la llamada |
@PostConstruct |
Después del constructor, con los beans inicializados. |
@PreDestroy |
Antes de la destrucción. |
También es posible utilizar aspectos de la programación orientada a aspectos (AOP) para realizar acciones durante la creación y destrucción del objeto.
Antes de estas anotaciones, usamos métodos parametrizados en la configuración con los argumentos init-method y destroy-method, como en el siguiente ejemplo:
<bean id="helloWorld"
class="com.tutorialspoint.HelloWorld"
init-method="init" destroy-method="destroy">
<property name="mensaje" value="Hola"/> ...
Ejemplo que ilustra los mappings estándares
Este ejemplo describe, paso a paso, la creación de un proyecto sencillo. Está presente en los ejemplos que se pueden descargar en la página Información. En primer momento, este capítulo puede parecer un poco complejo para los principiantes, pero se puede utilizar para experimentar.
1. El Proyecto Maven
-
Cree un proyecto vacío de Maven con el siguiente arquetipo:
mvn archetype:generate -DgroupId=fr.eni.editions
-DartifactId=mappingApp -DarchetypeArtifactId=maven-archetype-quickstart
-DinteractiveMode=false
-
O use el Spring Initalizr para crear un proyecto vacío (https://start.spring.io/).
-
Importe el archivo Maven a Eclipse o IntelliJ IDEA (o a su herramienta de desarrollo favorita).
-
Para separar los archivos de configuración de los archivos de código, cree los directorios de origen:
-
src/main/resources
-
src/test/resources
Pondremos en estos directorios los archivos de configuración de Spring y el sistema de registro.
2. Archivo de configuración de Spring
Crearemos un archivo Spring llamado applicationContext.xml y lo colocaremos en el directorio src/main/resources/Spring/.
<?xml version="1.0" encoding="UTF-8"?>
<beans default-lazy-init="true"
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/jdbc
http://www.springframework.org/schema/jdbc/spring-jdbc-4.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.1.xsd"> ...
Spring Expression Language
Es un lenguaje de expresión ampliamente utilizado que permite variabilizar un argumento que se fijaría en una cadena, lo que es particularmente útil con argumentos de anotación.
Su sintaxis es similar a Jakarta Expression Language (Unified EL), pero ofrece posibilidades adicionales, como la invocación de métodos en un bean o en un objeto fuera de Spring.
Es posible utilizar SpEL de forma autónoma usando algunas clases de la infraestructura del núcleo, como el analizador, pero, en general, su uso es transparente cuando codificamos con Spring. SpEL está muy cerca de las especificaciones JavaBeans.
1. Uso de ExpressionParser
ExpressionParser se utiliza para evaluar una expresión.
Rara vez lo usamos directamente, pero Spring lo utiliza muy a menudo con las cadenas que le enviamos.
ExpressionParser parser = new SpelExpressionParser ();
Expression exp = parser.parseExpression ( "'Hola a'.concat
(' todos')");
String message = (String) exp.getValue ();
Por ejemplo, aquí concatenamos las cadenas «hola a » y «todos».
Se puede acceder a los miembros y métodos de los objetos a través de una cadena de llamadas, utilizando «.».
Por ejemplo:
"'Hola a todos'.bytes.length"
invoca ’getBytes().length’.
El método getValue tiene la firma:
public <T>...
Servidores J2EE, Java EE y Jakarta EE
1. Aspectos generales
Spring fue diseñado originalmente para evitar el uso de los EJB, que originalmente eran inmaduros. En sus primeras versiones, Spring permitía una gestión simplificada de los EJB. Incluso hoy en día, los proyectos continúan usando Spring 3 con servidores Java JEE 6, o incluso Spring 2 asociado con un servidor Java JEE 5, generalmente con un framework casero. Algunas veces se evita la migración si las modificaciones de código son menores. Encontramos en Spring 2 y 3 casi todo lo que hay en Spring 4, excepto las anotaciones. De hecho, en la medida de lo posible, Spring ha intentado llevar las evoluciones de la versión actual en las versiones anteriores. En todas sus versiones, Spring funciona muy bien en la mayoría de los servidores J2EE, Java EE y Jakarta EE (siempre que utilice la versión que corresponda al servidor empleado).
En la práctica, los «problemas» llegan con bastante rapidez. Para empezar, están las implementaciones «propietarias» de las JVM. Una JVM de IBM no tiene los mismos errores o fallos que una JVM de Oracle. Esto significa que el código no es realmente portable. También es necesario tener en cuenta el ritmo de las versiones correctivas y las dificultades de hacer que los parches estén disponibles en los diferentes entornos.
Luego están las partes personalizadas: WebSphere, WebLogic y jBoss. Estas proponen una parte común desde un punto de vista de las especificaciones, pero cada una tendrá sus sutilezas para implementarlas y, por lo tanto, será necesario conocerlas. Además, también ofrecen partes diferenciadoras que facilitarán enormemente la vida de los desarrolladores, pero los vincularán permanentemente con un proveedor de servidores. Su objetivo es vender un paquete con máquinas, licencias, etc. Estos puentes, generalmente, permiten llamar al código «Legacy» desde el código Java.
IBM ofrece un puente con sus mainframes que permiten utilizar IMS, CISC, etc., junto con Java. WebLogic ofrece un puente con Tuxedo y workflows basados en MDB (Message Driven Bean). JBoss propone innovaciones, JBoss Rules, Hibernate o pocas interconexiones propietarias. JBoss se utiliza mucho en contenedores docker con Kubernetes y OpenShift.
En función del cliente...
Puntos clave
-
El framework es configurable en Java o mediante archivos XML.
-
Preferiblemente, usaremos la configuración de Java para nuevos proyectos.
-
Los beans se inyectan gracias a los constructores o por los setters.
-
Podemos mejorar el sistema de log predeterminado.
-
Es muy fácil hacer pruebas con Spring.
-
Es posible utilizar conjuntamente Spring y EJB.