Spring Boot: configuración de propiedades

En este tutorial, nos sumergiremos en la configuración de las propiedades de Spring Boot a través de valores y archivos de propiedades.

Introducción

En este artículo, nos sumergiremos en Configuración de las propiedades de Spring Boot.

Spring permite a los desarrolladores configurar una gran cantidad de propiedades para sus proyectos. Spring Boot, además de permitir a los desarrolladores comenzar con un proyecto desde cero de manera mucho más fácil y rápida que Spring, también facilita mucho la configuración de propiedades para sus aplicaciones.

Hay varias formas de configurar un proyecto de Spring:

  • Basado en Java
  • Basado en XML
  • Basado en propiedades

La configuración de propiedades basada en Java y XML era una forma clásica de configurar aplicaciones Spring antes de que Spring Boot nos presentara un archivo application.properties.

Esta adición nos permite configurar externamente la aplicación y acceder fácilmente a las propiedades definidas en el archivo.

De forma predeterminada, el archivo application.properties se puede usar para almacenar pares de propiedades, aunque también puede definir cualquier número de archivos de propiedades adicionales.

Para registrar un archivo de propiedades, puede anotar una clase @Configuration con la anotación adicional @PropertySource:

1
2
3
4
5
@Configuration
@PropertySource("classpath:custom.properties")
public class ConfigClass {
// Configuration
}

Con este método, puede registrar cualquier cantidad de archivos .properties adicionales:

1
2
3
4
5
6
@Configuration
@PropertySource("classpath:custom.properties")
@PropertySource("classpath:another.properties")
public class ConfigClass {
// Configuration
}

Inyección de propiedades Spring Boot

Configuración de la aplicación

La forma más fácil de comenzar con un proyecto básico es usar Spring Initializr. Seleccione su versión preferida de Spring Boot, agregue la dependencia Web y genere como un proyecto Maven:

Spring Initializr

Si abre el proyecto, notará que se mantiene un archivo application.properties en la ruta src/main/resources.

Este es el archivo predeterminado en el que se basa Spring para cargar las propiedades. Podemos escribir nuestras propiedades personalizadas o específicas de Spring como pares clave-valor aquí:

1
2
message.default.welcome=Welcome...
message.default.goodbye=Goodbye...

En lugar del archivo properties, también podemos usar un archivo .yml y definir las mismas propiedades que:

1
2
3
4
message:
  default:
    welcome: Welcome...
    goodbye: Goodbye...

Esto funciona debido al jar SerpienteYaml presente en el classpath. Los archivos YAML son más concisos y admiten mapas, listas, etc.

Depende de usted y su equipo qué tipo usar. Usaremos el tipo .properties en este tutorial.

Inyectar propiedades usando @Value

Veamos cómo podemos usar estas propiedades en una API REST simple:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
@RestController
public class GreetController {

    @Value("${message.default.welcome}")
    private String welcomeMessage;

    @Value("${message.default.goodbye}")
    private String goodBye;

    @RequestMapping("/welcome")
    public String welcome() {
        return welcomeMessage;
    }

    @RequestMapping("/bye")
    public String bye() {
        return goodBye;
    }
}

Esto es bastante sencillo. Usando la anotación @Value, podemos inyectar los valores del archivo application.properties en campos de clase en el bean administrado por Spring GreetController.

Luego tenemos un par de extremos REST que simplemente devuelven estos valores:

Result

El uso de @Value le permite establecer un valor predeterminado si el solicitado, por cualquier motivo, no está disponible:

1
2
@Value("${message.default.welcome:SomeDefaultValue}")
private String welcomeMessage;

Si el valor message.default.welcome no está presente, el valor se establecerá como SomeDefaultValue.

Si desea leer más sobre la @anotación de valor, ¡tenemos un artículo detallado sobre eso!

Inyectando propiedades usando @ConfigurationProperties {#inyectando propiedades usando propiedades de configuración}

Si nuestras propiedades tienen algún contexto común como el mismo prefijo, podemos usar la anotación @ConfigurationProperties que asignará estas propiedades a los objetos de Java:

1
2
3
4
5
6
7
8
9
@Configuration
@ConfigurationProperties(prefix = "message.default")
public class MessageProperties {

    private String welcome;
    private String goodbye;

    // Getters and Setters
}
  • @Configuration le dirá a Spring que cree un bean de esta clase.
  • @ConfigurationProperties inicializará los campos con los nombres de propiedad correspondientes.

Ahora podemos usar este bean en otros beans administrados por Spring:

1
2
@Autowired
MessageProperties messageProperties;

Sustitución de propiedades

Naturalmente, a medida que nuestro entorno de aplicaciones se expande y cambia (desarrollo, control de calidad, producción, etc.), algunas de nuestras propiedades también cambiarán. Estos pueden interferir entre sí si no los segregamos de alguna manera.

Esto lo logramos manteniendo diferentes archivos u obteniendo los valores de las propiedades a través de variables de entorno.

Uso de perfiles Spring

La forma más común de escribir propiedades "cambiantes" es almacenarlas en diferentes archivos. Estos archivos son específicos del entorno y nuestra aplicación puede cargarlos en función de las variables del entorno.

Spring Boot proporciona una manera muy elegante de manejar esto.

Todo lo que tenemos que hacer es seguir una convención de nomenclatura: application-<environment>.properties para nuestros archivos de propiedades:

  • aplicación-desarrollo.propiedades
  • aplicación-qa.properties
  • aplicación-producción.propiedades, etc.

Para notificar a Spring qué archivos usar, debemos establecer una variable de entorno: spring.profiles.active.

Dicho esto, si el valor de spring.profiles.active es dev, por ejemplo, Spring boot cargará el archivo application-dev.properties y lo mismo.

Nota: application.properties siempre se carga, independientemente del valor de spring.profiles.active. Si hay el mismo valor-clave presente tanto en application.properties como en application-<environment>.properties, el último anulará al primero.

Por lo general, escribimos todas las propiedades comunes de cada entorno en application.properties y anulamos las propiedades específicas del entorno usando application-<environment>.properties específico del perfil.

Veamos esto creando un application-dev.properties:

1
message.default.welcome = Welcome to DEV environment...

Hay pocas formas de configurar la variable spring.profiles.active.

Si estamos ejecutando la aplicación a través de Eclipse, podemos establecer esto en los argumentos de VM:

Eclipse VM Arguments

Podemos configurarlo en las variables de entorno del sistema operativo, como en Windows:

windows env

Comencemos nuestra aplicación y en los registros, puede ver que se está cargando el perfil dev:

spring profiles active dev logs

Revisemos nuestros dos extremos REST anteriores:

spring profiles active dev

Como podemos ver, el valor message.default.welcome vino del archivo application-dev.properties y la propiedad message.default.goodbye vino de application.properties.

Podemos tener múltiples valores en spring.profiles.active como dev,qa:

spring profiles active dev/qa

Cualquier clave duplicada sería anulada por el último perfil, siendo en el caso anterior qa.

También podemos pasar spring.profiles.active como un argumento de línea de comando como:

1
java -jar -Dspring.profiles.active=dev greeting-service-0.0.1-SNAPSHOT.jar

Creación de propiedades de aplicación desde la ubicación de compilación {#creación de propiedades de aplicación desde la ubicación de compilación}

También podemos anular las propiedades internas creando un archivo application.properties en el mismo nivel desde donde se ejecuta .jar. El contexto de Spring anulará las propiedades usando este archivo recién creado.

Esta es una excelente manera de distribuir su aplicación a otros, quienes pueden anular ciertas propiedades en función de su entorno, como las configuraciones de la base de datos, por ejemplo.

También hay otras formas de externalizar sus propiedades, como las variables de entorno del sistema operativo, los argumentos de la línea de comandos, etc. El orden en que Spring lo considera se puede encontrar [aquí](https://docs.spring.io/spring-boot /docs/current/reference/html/boot-features-external-config.html).

Externalización de propiedades mediante el servidor de configuración en la nube {#externalización de propiedades mediante el servidor de configuración en la nube}

Muchas de las aplicaciones creadas hoy en día se basan en la arquitectura de microservicios. Estas aplicaciones no solo se implementan por separado, sino que podrían tener varias instancias de sí mismas (según la carga) y el recuento total podría superar fácilmente las 100.

Administrar propiedades en este estilo arquitectónico particular a través de métodos convencionales requiere demasiado esfuerzo. Además, para cambiar una propiedad, tenemos que volver a compilar la aplicación e implementarla o, en el mejor de los casos, reiniciar la aplicación. Esto requiere tiempo de inactividad, lo que anula todo el propósito de los microservicios.

Otro problema con el enfoque tradicional, especialmente si las propiedades se externalizaron a través de archivos o variables de entorno, es que no hay trazabilidad. Siempre se toman las últimas y no sabemos cuáles eran las propiedades antes o quién las cambió.

Configuración de la nube de primavera proporciona una manera centralizada, externalizada, segura y fácil de almacenar y servir configuraciones para aplicaciones para diferentes entornos:

spring config server diagram

En resumen, tenemos un Servidor de configuración que se ejecuta como una aplicación separada que se conecta a un repositorio de Git.

Cuando iniciamos una nueva aplicación (Cliente de configuración), obtiene todas las propiedades necesarias del Servidor de configuración. No importa si la aplicación existía cuando configuramos el servidor o no.

Crear un servidor de configuración

Como siempre, comenzamos usando Spring Initializr.

Seleccione su versión preferida de Spring Boot, agregue la dependencia del servidor de configuración y genere como un proyecto de Maven:

spring config server

Al anotar nuestra clase principal con @EnableConfigServer, la marcamos como un servidor de configuración:

1
2
3
4
5
6
7
8
@SpringBootApplication
@EnableConfigServer
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

Y ahora, tenemos que configurar algunas cosas en el archivo application.properties:

1
2
3
server.port = 8888
spring.cloud.config.server.git.uri = https://github.com/dhananjay12/spring-cloud-config
spring.cloud.config.server.git.searchPaths = app-properties

Aquí, definimos el puerto en el que se ejecutaría el servidor de configuración. Luego especificamos la URL de Git que necesita enlazar para las propiedades.

Nota: De forma predeterminada, Spring busca los archivos de propiedades en la raíz. Si tenemos que especificar una carpeta en particular, podemos proporcionar la ubicación a través de searchPaths.

Así es como se ve el repositorio de Git:

greeting service cloud

Podemos iniciar el servidor de configuración ahora. Si desea verificar la configuración de Spring Config Server, siguiendo la convención: http://localhost:8888/<application-name>/<spring-profiles> nos mostrará toda la información necesaria.

En nuestro caso sería - http://localhost:8888/saludo-servicio-nube/predeterminado:

greeting service cloud

Creación de un cliente de configuración

Vamos a crear el mismo servicio de saludo pero con un par de dependencias adicionales:

greeting service cloud

Aquí, creamos el servicio greeting-service-cloud con las dependencias Web, Config Client y Actuator.

Tiene las mismas asignaciones REST que antes, con la adición de la anotación @RefreshScope. Esta anotación permite que el bean se actualice dinámicamente en tiempo de ejecución:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
@RestController
@RefreshScope
public class GreetController {

    @Value("${message.default.welcome}")
    private String welcomeMessage;

    @Value("${message.default.goodbye}")
    private String goodBye;

    @RequestMapping("/welcome")
    public String welcome() {
        return welcomeMessage;
    }
    @RequestMapping("/bye")
    public String bye() {
        return goodBye;
    }
}

Además de application.properties, ahora tenemos que crear bootstrap.properties, que se carga antes de application.properties.

Por lo general, lo utiliza Spring Config Client para obtener propiedades del Spring Config Server:

1
2
spring.application.name = greeting-service-cloud
spring.cloud.config.uri = http://localhost:8888

Aquí, primero establecemos el nombre de la aplicación. Spring Config Server buscará este nombre de archivo en el repositorio de Git y entregará su contenido.

También debemos mencionar dónde se ejecuta el servidor de configuración especificándolo en spring.cloud.config.uri.

Comencemos este servicio y echemos un vistazo a los registros:

greeting service cloud logs

Tenga en cuenta que primero obtuvo las propiedades del Spring Config Server.

Nota: Si no se puede acceder al servidor de configuración, la aplicación no se iniciará.

Ahora revisemos nuestros puntos finales REST:

greeting service cloud apis

Así que externalizamos nuestras propiedades y tenemos una buena trazabilidad de ellas en nuestro repositorio de Git. Algunos puntos importantes que vale la pena señalar:

  • Podemos usar spring-profiles-active aquí también. Si esta variable se establece en el entorno de Config Client para, por ejemplo. dev, se pasará al servidor de configuración mientras se solicitan las propiedades. El servidor de configuración luego buscará greeting-service-cloud-dev.properties en el repositorio de Git y se lo entregará al cliente.
  • Si hay una application.properties presente en el repositorio de Git, se servirá a todos los clientes además de otros archivos.
  • Si el cliente de configuración solicita propiedades, por ejemplo, diga perfil dev, el servidor de configuración devolverá application.properties, application-dev.properties y greeting-service-cloud-dev.properties. Las propiedades comunes serán anuladas por la última.

Actualizar propiedades sin reiniciar

De forma predeterminada, los valores de configuración de los archivos de propiedades están listos o se obtienen al iniciar la aplicación y no de nuevo. Si hay que hacer algunos cambios, todavía tenemos que reiniciar la aplicación.

Para solucionar esto agregamos la dependencia Solenoide a nuestra aplicación. Proporciona algunos [puntos finales] (https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-endpoints.html) listos para la producción que pueden brindar información sobre nuestra aplicación que puede utilizarse para fines administrativos.

Tenemos que habilitar estos puntos finales manualmente especificando management.endpoints.web.exposure.include = * en las propiedades de la aplicación.

Agreguemos esto al repositorio de Git y reiniciemos la aplicación. Podemos verificar muchos detalles de nuestra aplicación visitando puntos finales como http://localhost:8080/actuador/env, http://localhost:8080/actuador/ asignaciones, etc.

El que nos interesa es /actuator/refresh. Podemos obligar a un bean a actualizar su configuración (es decir, a extraer la configuración nuevamente del servidor de configuración) anotando el bean con @RefreshScope.

Nota: si se actualiza un bean, la próxima vez que se acceda al bean (es decir, se ejecute un método) se creará una nueva instancia.

Esto se puede activar enviando una solicitud HTTP POST vacía al punto final de actualización del cliente: http://<host:port>/actuator/refresh.

Cambiemos el valor de uno en el repositorio de Git a otro:

1
2
3
4
message.default.welcome = Welcome from cloud config server changed...
message.default.goodbye = Goodbye...

management.endpoints.web.exposure.include = *

Ahora activemos el punto final de actualización:

1
curl localhost:8080/actuator/refresh -d {} -H "Content-Type: application/json"

Verifique el punto final /welcome:

greeting service cloud refresh

Entonces, pudimos actualizar la propiedad de una aplicación en ejecución sin reiniciarla.

Conclusión

En este artículo, hemos cubierto cómo configurar propiedades en nuestra aplicación Spring Boot.

En primer lugar, hemos discutido formas simples de inyectar propiedades a nuestra aplicación y luego cambiar/anular estas propiedades en función de diferentes entornos.

En segundo lugar, hemos cubierto cómo obtener propiedades de Spring Config Server y cómo actualizar las propiedades sin reconstruir o reiniciar.

Como siempre, el código de los ejemplos usados ​​en este artículo se puede encontrar en Github.