Tips Spring : [BOOT] propiedades de configuración personalizadas. La anotación @ConfigurationProperties.

logo spring

Los parámetros de configuración de Spring Boot y las librerías compatibles se definen en el fichero /src/main/resources/application.properties (o application.yml para los que prefieran el formato YAML). Los disponibles se detallan en esta página.

Además, seguimos contando con la posibilidad de crear nuestros propios ficheros de configuración con los mecanismos estándar de Spring. A ellos dedico este extenso artículo. Con todo, voy a indicar de manera muy resumida los pasos para hacerlo:

  • Añadir el fichero al classpath dentro de /src/main/resources, siempre y cuando vaya a ser empaquetado en la aplicación.
song=ride like the wind
  • Importarlo en una clase de configuración (clase marcada con @Configuration o @SpringBootApplication) con @PropertySource. Admite una lista de ficheros ubicados en el classpath y \ o en una ruta absoluta en el sistema de archivos (prefijo file:).
@SpringBootApplication
@PropertySource("classpath:custom.properties")
public class SpringBootPropertiesApplication {
  • Inyectamos las propiedades de forma individual con @Value.
@SpringBootTest
class CustomPropertiesTests {

    @Value("${song}")
    private String song;

    @Test
    void testSongFromValue() {
        assertEquals(SONG_NAME, song);
    }

}
@Autowired
 private Environment environment;

@Test
void testSongFromEnvironment() {
    String song = environment.getProperty("song");

    assertEquals(SONG_NAME, song);
}

Podemos escribir nuestros propios parámetros en el application.properties y leerlos con las dos estrategias que acabo de explicar. No obstante, la razón de ser de este pequeño artículo es que Spring Boot permite modelar en clases esos parámetros para abstraer su uso con métodos getters. De este modo, no necesitamos conocer sus nombres o declararlos en constantes.

Vamos a crear una clase que modele un grupo de propiedades de configuración. Cada propiedad se corresponderá con un atributo y contará con sus métodos get y set. La marcamos con @ConfigurationProperties y en ella declaramos, opcionalmente, el prefijo de las propiedades. Lo razonable es agrupar las propiedades que se utilizan para configurar cierta librería o módulo de nuestra aplicación siguiendo una jerarquía de nombres, tal y como hace Spring Boot (spring.datasource, spring.mvc, etcétera).

Si queremos leer la siguiente propiedad…

custom.song = ride like the wind

…esta es la clase que la recoge.

package com.danielme.springboot;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties("custom")
public class CustomProperties {

    private String song;

    public String getSong() {
        return song;
    }

    public void setSong(String song) {
        this.song = song;
    }
}

Las clases @ConfigurationProperties tienen un superpoder: sus atributos pueden validarse con las anotaciones de Bean Validation. Esto precisa añadir la dependencia spring-boot-starter-validation al pom.

<dependency>
   <groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-validation</artifactId>
</dependency>

Ahora marcamos CustomProperties con @Validated y aplicamos las restricciones de validación al atributo.

package com.danielme.springboot;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;

import javax.validation.constraints.NotBlank;

@Validated
@ConfigurationProperties("custom")
public class CustomProperties {

    @NotBlank
    private String song;

    public String getSong() {
        return song;
    }

    public void setSong(String song) {
        this.song = song;
    }
}

La anotación @NotBlank impone que el valor exista y no sea una cadena de espacios en blanco. Si no se satisface esta restricción, se lanzará una excepción que aborta el inicio de Spring. Esto nos da cierta seguridad sobre la validez la configuración.

***************************
APPLICATION FAILED TO START
***************************
Description:
Binding to target org.springframework.boot.context.properties.bind.BindException: Failed to bind properties under 'custom' to com.danielme.springboot.CustomProperties failed:

    Property: custom.song
    Value: "null"
    Reason: must not be blank

Action:

Update your application's configuration

Queda un paso. Hay que declarar la clase CustomProperties en la configuración de Spring con @EnableConfigurationProperties. Como alternativa, es posible activar el escaneo automático de todas las clases con propiedades con @ConfigurationPropertiesScan, anotación disponible a partir de Spring Boot 2.2 (octubre 2019). Por comodidad, he optado por la segunda opción.

@SpringBootApplication
@PropertySource("classpath:custom.properties")
@ConfigurationPropertiesScan
public class SpringBootPropertiesApplication {

Ahora leemos las propiedades inyectando CustomProperties y llamando a sus getters.

@Autowired
private CustomProperties customProperties;

@Test
void testConfigurationPropertiesClass() {
    assertEquals(SONG_NAME, customProperties.getSong());
}

También se pueden recoger los valores de cualquier fichero de configuración distinto del application.properties. Tomemos como ejemplo este fichero denominado servers.properties.

email=email_server
ftp=ftp_server
@Configuration
@Validated
@ConfigurationProperties
@PropertySource("classpath:servers.properties")
public class ServersProperties {

    @NotBlank
    private String email;
    @NotBlank
    private String ftp;

He aprovechado la clase anterior de tipo @ConfigurationProperties para importar el fichero de propiedades que modela. No es obligatorio hacerlo ahí y podemos seguir realizando la importación en cualquier clase de configuración. Sin embargo, me parece el lugar más natural para hacerlo.

Un último consejo. Si añadimos al pom la siguiente dependencia:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <optional>true</optional>
</dependency>

Al compilarse el proyecto se creará el fichero /target/classes/META-INF/spring-configuration-metadata.json que detalla las propiedades personalizadas.

{
  "groups": [
    {
      "name": "custom",
      "type": "com.danielme.springboot.CustomProperties",
      "sourceType": "com.danielme.springboot.CustomProperties"
    }
  ],
  "properties": [
    {
      "name": "custom.song",
      "type": "java.lang.String",
      "sourceType": "com.danielme.springboot.CustomProperties"
    }
  ],
  "hints": []
}

Gracias a esta información, los IDE como IntelliJ Ultimate pueden ofrecer autocompletado en el fichero de configuración, entre otras funcionalidades. Toda ayuda que haga nuestro trabajo más cómodo es bien recibida.

Código de ejemplo

El código de ejemplo se encuentra en GitHub. Para más información sobre cómo utilizar GitHub, consultar este artículo.

Deja una respuesta

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Salir /  Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Salir /  Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Salir /  Cambiar )

Conectando a %s

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.