Configuration Properties En Spring Boot

22-04-2025

Por Jose Francisco Rodríguez Hernández

Cuando desarrollamos aplicaciones es interesante tener cierta flexibilidad y configuración posible. Una forma de lograr esto es a través de Configuration Properties. Esta funcionalidad nos permite externalizar la configuración y adaptar nuestra aplicación a distintos entornos sin necesidad de tocar el código.

Recientemente trabajando en un proyecto de Java con Spring Boot se nos presentó el problema de que debíamos sacar varias propiedades definidas en la configuración de nuestro proyecto y la manera que conocíamos hasta el momento era con la siguiente anotación:

public MyClass { @Value("${my.property}") String myProperty; ... }

Esta aproximación está bien si queremos utilizar pocas propiedades, pero en el escenario que nosotros nos encontrábamos, esta solución era inviable ya que necesitábamos bastantes propiedades. La duda que me surgió fue la siguiente:

¿Existe alguna otra manera de poder obtener las propiedades de la aplicación a través de un objeto o algo así? La respuesta es que sí, vamos a verlo.

Vamos a presuponer que tenemos el siguiente archivo YML de configuración en nuestro proyecto:

test: my-property: "aValue" another-property: "anotherValue" a-list-of-properties: - "value1" - "value2" - "value3" a-map-of-properties: key1: "value1" key2: "value2" key3: "value3" nested-properties: property: "aValue" nested-list: - "value1" - "value2" - "value3" nested-map: key1: "value1" key2: "value2" key3: "value3"

Imaginemos ahora que necesitamos todas las propiedades que caen dentro de la etiqueta “test”. Si fuéramos a obtenerlas a través del "@Value" sería un infierno, y es aquí donde entra en juego la anotación @ConfigurationProperties junto con una clase que represente la configuración que tenemos definido en el YML. Veamos un ejemplo:

@ConfigurationProperties(prefix = "test") public record TestProperties( String myProperty, String anotherProperty, List<String> aListOfProperties, Map<String, String> aMapOfProperties, NestedProperties nestedProperties ) {} public record NestedProperties( String property, List<String> nestedList, Map<String, String> nestedMap ) {}

A través de la propiedad prefix de la anotación podemos definir desde qué nivel queremos obtener las propiedades, para este ejemplo como queremos todo lo que está definido dentro de test pondremos “test” pero si quisiéramos por ejemplo las “nested-properties” el valor del prefix sería “test.nested-properties”. Los nombres de las propiedades deben ser iguales en el objeto a las definidas en el YML, más adelante cubriremos la posibilidad de tener nombres de variables distintos.

Vemos que en la clase TestProperties hemos cubierto distintos tipos de propiedades:

  • Simples: estas son las típicas propiedades que obtendríamos a través de la anotación @value. Tienen una key y un valor que puede ser un String, un valor numérico o un booleano.
  • Lista de Propiedades: estas propiedades tienen asignadas varios valores a una única key por lo que se transformaría en una lista de sus distintos valores.
  • Mapa: estas propiedades se corresponden con un mapa de valores.

La gran ventaja de acceder a las variables de configuración de esta manera está en que una vez definido el objeto que mapea las propiedades podemos inyectarlo en nuestras clases y acceder directamente a los valores. Además de que tenemos la posibilidad de anidar distintos niveles de indentación dentro del objeto que hayamos creado para mapear las propiedades.

Veamos un ejemplo:

@SpringBootTest @ActiveProfiles("test") public class TestPropertiesTest { @Autowired private TestProperties testProperties; @Test void should_load_basic_properties() { assertEquals("aValue", testProperties.myProperty()); assertEquals("anotherValue", testProperties.anotherProperty()); } @Test void should_load_a_list_of_properties() { assertEquals(3, testProperties.aListOfProperties().size()); assertEquals(List.of("value1", "value2", "value3"), testProperties.aListOfProperties()); } @Test void should_load_a_map_of_properties() { assertEquals(3, testProperties.aMapOfProperties().size()); assertEquals(Map.of("key1", "value1", "key2", "value2", "key3", "value3"), testProperties.aMapOfProperties()); } @Test void should_load_nested_properties() { final NestedProperties nestedProperties = testProperties.nestedProperties(); assertNotNull(nestedProperties); assertEquals("aValue", nestedProperties.property()); assertEquals(3, nestedProperties.nestedList().size()); assertEquals(List.of("key1","key2","key3"), nestedProperties.nestedMap().keySet().stream().toList()); } }

Esta es una manera limpia y sencilla de utilizar varias propiedades en nuestro proyecto sin tener que mapear cada una de ellas a mano.

Mapeo de propiedades con nombres distintos

Digamos que tenemos la siguiente propiedad dentro del mismo YML:

test: map-name-example: a-random-weird-name: "test"

Y ahora queremos mapear esta propiedad que tiene un nombre un poco extraño a uno más representativo dentro de nuestro proyecto y no podemos cambiarle el nombre a esta propiedad por el motivo que sea. Podemos mapearlo de la siguiente manera:

@ConfigurationProperties(prefix = "test.map-name-example") public class MapNameExampleProperties { private String aCoolNamedProperty; public String getaCoolNamedProperty() { return aCoolNamedProperty; } public void setARandomWeirdName(String aCoolNamedProperty) { this.aCoolNamedProperty = aCoolNamedProperty; } }

Debemos tener una función setter con el nombre de la propiedad definida en la configuración en la cual le asignemos el valor a una propiedad dentro de nuestro Objeto de propiedades y ya lo tendríamos.

Cabe destacar que se pueden utilizar tanto Clases como Records de Java para mapear las propiedades a Objetos dentro de nuestro proyecto pero se debe tener en cuenta que se necesitan getters y setters en el caso de que utilicemos Clases.

Esto sería todo, espero que os haya gustado y os dejo por aquí un enlace con el repositorio de GitHub con el código de ejemplo que he utilizado a lo largo del artículo.