Propiedades con Spring y Spring Boot

1. Información general

Este tutorial mostrará cómo configurar y usar propiedades en Spring a través de la configuración de Java y @PropertySource.

También veremos cómo funcionan las propiedades en Spring Boot.

2. Registre un archivo de propiedades mediante anotaciones

Spring 3.1 también presenta la nueva anotación @PropertySource como un mecanismo conveniente para agregar fuentes de propiedades al entorno.

Podemos usar esta anotación junto con la anotación @Configuration :

@Configuration @PropertySource("classpath:foo.properties") public class PropertiesWithJavaConfig { //... }

Otra forma muy útil de registrar un nuevo archivo de propiedades es usar un marcador de posición, que nos permite seleccionar dinámicamente el archivo correcto en tiempo de ejecución :

@PropertySource({ "classpath:persistence-${envTarget:mysql}.properties" }) ...

2.1. Definición de ubicaciones de propiedades múltiples

La anotación @PropertySource es repetible de acuerdo con las convenciones de Java 8. Por lo tanto, si usamos Java 8 o superior, podemos usar esta anotación para definir múltiples ubicaciones de propiedades:

@PropertySource("classpath:foo.properties") @PropertySource("classpath:bar.properties") public class PropertiesWithJavaConfig { //... }

Por supuesto, también podemos usar la anotación @PropertySources y especificar una matriz de @PropertySource . Esto funciona en cualquier versión de Java compatible, no solo en Java 8 o superior:

@PropertySources({ @PropertySource("classpath:foo.properties"), @PropertySource("classpath:bar.properties") }) public class PropertiesWithJavaConfig { //... }

En cualquier caso, vale la pena señalar que en caso de una colisión de nombres de propiedad, la última fuente leída tiene prioridad.

3. Uso / inyección de propiedades

Inyectar una propiedad con la anotación @Value es sencillo:

@Value( "${jdbc.url}" ) private String jdbcUrl;

También podemos especificar un valor predeterminado para la propiedad:

@Value( "${jdbc.url:aDefaultUrl}" ) private String jdbcUrl;

El nuevo PropertySourcesPlaceholderConfigurer agregado en Spring 3.1 resuelve $ {…} marcadores de posición dentro de los valores de propiedad de definición de frijol y anotaciones @Value .

Finalmente, podemos obtener el valor de una propiedad usando la API de entorno :

@Autowired private Environment env; ... dataSource.setUrl(env.getProperty("jdbc.url"));

4. Propiedades con Spring Boot

Antes de entrar en opciones de configuración más avanzadas para las propiedades, dediquemos un tiempo a analizar el soporte de nuevas propiedades en Spring Boot.

En términos generales, este nuevo soporte implica menos configuración en comparación con Spring estándar , que es, por supuesto, uno de los principales objetivos de Boot.

4.1. application.properties: el archivo de propiedades predeterminado

Boot aplica su convención típica sobre el enfoque de configuración a los archivos de propiedades. Esto significa que simplemente podemos poner un archivo application.properties en nuestro directorio src / main / resources , y se detectará automáticamente . Luego podemos inyectar cualquier propiedad cargada de él como de costumbre.

Entonces, al usar este archivo predeterminado, no tenemos que registrar explícitamente un PropertySource o incluso proporcionar una ruta a un archivo de propiedad.

También podemos configurar un archivo diferente en tiempo de ejecución si es necesario, usando una propiedad de entorno:

java -jar app.jar --spring.config.location=classpath:/another-location.properties

A partir de Spring Boot 2.3, también podemos especificar ubicaciones de comodines para los archivos de configuración .

Por ejemplo, podemos establecer la propiedad spring.config.location en config / * / :

java -jar app.jar --spring.config.location=config/*/

De esta manera, Spring Boot buscará archivos de configuración que coincidan con el patrón de directorio config / * / fuera de nuestro archivo jar. Esto resulta útil cuando tenemos múltiples fuentes de propiedades de configuración.

Desde la versión 2.4.0 , Spring Boot admite el uso de archivos de propiedades de múltiples documentos , de manera similar a como lo hace YAML por diseño:

baeldung.customProperty=defaultValue #--- baeldung.customProperty=overriddenValue

Tenga en cuenta que para los archivos de propiedades, la notación de tres guiones está precedida por un carácter de comentario ( # ).

4.2. Archivo de propiedades específicas del entorno

Si necesitamos apuntar a diferentes entornos, hay un mecanismo incorporado para eso en Boot.

Simplemente podemos definir un archivo application-environment.properties en el directorio src / main / resources , y luego establecer un perfil de Spring con el mismo nombre de entorno.

Por ejemplo, si definimos un entorno de “preparación”, eso significa que tendremos que definir un perfil de preparación y luego application-staging.properties .

Este archivo env se cargará y tendrá prioridad sobre el archivo de propiedades predeterminado. Tenga en cuenta que el archivo predeterminado aún se cargará, es solo que cuando hay una colisión de propiedades, el archivo de propiedades específico del entorno tiene prioridad.

4.3. Archivo de propiedades específicas de la prueba

También podríamos tener el requisito de usar diferentes valores de propiedad cuando nuestra aplicación esté bajo prueba.

Spring Boot maneja esto por nosotros buscando en nuestro directorio src / test / resources durante una ejecución de prueba . Nuevamente, las propiedades predeterminadas seguirán siendo inyectables de forma normal, pero serán anuladas por estas si hay una colisión.

4.4. La anotación @TestPropertySource

Si necesitamos un control más granular sobre las propiedades de la prueba, entonces podemos usar la anotación @TestPropertySource .

Esto nos permite establecer propiedades de prueba para un contexto de prueba específico, teniendo prioridad sobre las fuentes de propiedades predeterminadas:

@RunWith(SpringRunner.class) @TestPropertySource("/foo.properties") public class FilePropertyInjectionUnitTest { @Value("${foo}") private String foo; @Test public void whenFilePropertyProvided_thenProperlyInjected() { assertThat(foo).isEqualTo("bar"); } }

Si no queremos usar un archivo, podemos especificar nombres y valores directamente:

@RunWith(SpringRunner.class) @TestPropertySource(properties = {"foo=bar"}) public class PropertyInjectionUnitTest { @Value("${foo}") private String foo; @Test public void whenPropertyProvided_thenProperlyInjected() { assertThat(foo).isEqualTo("bar"); } }

También podemos lograr un efecto similar usando el argumento de propiedades de la anotación @SpringBootTest :

@RunWith(SpringRunner.class) @SpringBootTest( properties = {"foo=bar"}, classes = SpringBootPropertiesTestApplication.class) public class SpringBootPropertyInjectionIntegrationTest { @Value("${foo}") private String foo; @Test public void whenSpringBootPropertyProvided_thenProperlyInjected() { assertThat(foo).isEqualTo("bar"); } }

4.5. Propiedades jerárquicas

If we have properties that are grouped together, we can make use of the @ConfigurationProperties annotation, which will map these property hierarchies into Java objects graphs.

Let's take some properties used to configure a database connection:

database.url=jdbc:postgresql:/localhost:5432/instance database.username=foo database.password=bar

And then let's use the annotation to map them to a database object:

@ConfigurationProperties(prefix = "database") public class Database { String url; String username; String password; // standard getters and setters }

Spring Boot applies it's convention over configuration approach again, automatically mapping between property names and their corresponding fields. All that we need to supply is the property prefix.

If you want to dig deeper into configuration properties, have a look at our in-depth article.

4.6. Alternative: YAML Files

Spring also supports YAML files.

All the same naming rules apply for test-specific, environment-specific, and default property files. The only difference is the file extension and a dependency on the SnakeYAML library being on our classpath.

YAML is particularly good for hierarchical property storage; the following property file:

database.url=jdbc:postgresql:/localhost:5432/instance database.username=foo database.password=bar secret: foo

is synonymous with the following YAML file:

database: url: jdbc:postgresql:/localhost:5432/instance username: foo password: bar secret: foo

It's also worth mentioning that YAML files do not support the @PropertySource annotation, so if we need to use this annotation, it would constrain us to using a properties file.

Another remarkable point is that in version 2.4.0 Spring Boot changed the way in which properties are loaded from multi-document YAML files. Previously, the order in which they were added was based on the profile activation order. With the new version, however, the framework follows the same ordering rules that we indicated earlier for .properties files; properties declared lower in the file will simply override those higher up.

Additionally, in this version profiles can no longer be activated from profile-specific documents, making the outcome clearer and more predictable.

4.7. Importing Additional Configuration Files

Prior to version 2.4.0, Spring Boot allowed including additional configuration files using the spring.config.location and spring.config.additional-location properties, but they had certain limitations. For instance, they had to be defined before starting the application (as environment or system properties, or using command-line arguments) as they were used early in the process.

In the mentioned version, we can use the spring.config.import property within the application.properties or application.yml file to easily include additional files. This property supports some interesting features:

  • adding several files or directories
  • the files can be loaded either from the classpath or from an external directory
  • indicating if the startup process should fail if a file is not found, or if it's an optional file
  • importing extensionless files

Let's see a valid example:

spring.config.import=classpath:additional-application.properties, classpath:additional-application[.yml], optional:file:./external.properties, classpath:additional-application-properties/

Note: here we formatted this property using line breaks just for clarity.

Spring will treat imports as a new document inserted immediately below the import declaration.

4.8. Properties From Command Line Arguments

Besides using files, we can pass properties directly on the command line:

java -jar app.jar --property="value"

We can also do this via system properties, which are provided before the -jar command rather than after it:

java -Dproperty.name="value" -jar app.jar

4.9. Properties From Environment Variables

Spring Boot will also detect environment variables, treating them as properties:

export name=value java -jar app.jar 

4.10. Randomization of Property Values

If we don't want determinist property values, we can use RandomValuePropertySource to randomize the values of properties:

random.number=${random.int} random.long=${random.long} random.uuid=${random.uuid}

4.11. Additional Types of Property Sources

Spring Boot supports a multitude of property sources, implementing a well-thought-out ordering to allow sensible overriding. It's worth consulting the official documentation, which goes further than the scope of this article.

5. Configuration Using Raw Beans — the PropertySourcesPlaceholderConfigurer

Besides the convenient methods of getting properties into Spring, we can also define and regiter the property configuration bean manually.

Working with the PropertySourcesPlaceholderConfigurer gives us full control over the configuration, with the downside of being more verbose and most of the time, unnecessary.

Let's see how we can define this bean using Java configuration:

@Bean public static PropertySourcesPlaceholderConfigurer properties(){ PropertySourcesPlaceholderConfigurer pspc = new PropertySourcesPlaceholderConfigurer(); Resource[] resources = new ClassPathResource[ ] { new ClassPathResource( "foo.properties" ) }; pspc.setLocations( resources ); pspc.setIgnoreUnresolvablePlaceholders( true ); return pspc; }

6. Properties in Parent-Child Contexts

This question comes up again and again: What happens when our web application has a parent and a child context? The parent context may have some common core functionality and beans, and then one (or multiple) child contexts, maybe containing servlet-specific beans.

In that case, what's the best way to define properties files and include them in these contexts? And how to best retrieve these properties from Spring?

We'll give a simple breakdown.

If the file is defined in the Parent context:

  • @Value works in Child context: YES
  • @Value works in Parent context: YES
  • environment.getProperty in Child context: YES
  • environment.getProperty in Parent context: YES

If the file is defined in the Child context:

  • @Value works in Child context: YES
  • @Value works in Parent context: NO
  • environment.getProperty in Child context: YES
  • environment.getProperty in Parent context: NO

7. Conclusion

This article showed several examples of working with properties and properties files in Spring.

Como siempre, el código completo que respalda el artículo está disponible en GitHub.