Propriétés avec Spring et Spring Boot

1. Vue d'ensemble

Ce tutoriel montrera comment configurer et utiliser des propriétés dans Spring via la configuration Java et @PropertySource.

Nous verrons également comment les propriétés fonctionnent dans Spring Boot.

2. Enregistrez un fichier de propriétés via des annotations

Spring 3.1 introduit également la nouvelle annotation @PropertySource en tant que mécanisme pratique pour ajouter des sources de propriété à l'environnement.

Nous pouvons utiliser cette annotation en conjonction avec l' annotation @Configuration :

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

Un autre moyen très utile d'enregistrer un nouveau fichier de propriétés consiste à utiliser un espace réservé, qui nous permet de sélectionner dynamiquement le bon fichier au moment de l'exécution :

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

2.1. Définition de plusieurs emplacements de propriété

L' annotation @PropertySource est répétable selon les conventions Java 8. Par conséquent, si nous utilisons Java 8 ou supérieur, nous pouvons utiliser cette annotation pour définir plusieurs emplacements de propriété:

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

Bien sûr, nous pouvons également utiliser l' annotation @PropertySources et spécifier un tableau de @PropertySource . Cela fonctionne dans toutes les versions Java prises en charge, pas seulement dans Java 8 ou supérieur:

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

Dans les deux cas, il convient de noter qu'en cas de collision de nom de propriété, la dernière lecture de la source est prioritaire.

3. Utilisation / Injection des propriétés

L'injection d'une propriété avec l' annotation @Value est simple:

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

Nous pouvons également spécifier une valeur par défaut pour la propriété:

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

Le nouveau PropertySourcesPlaceholderConfigurer ajouté dans Spring 3.1 permet de résoudre les espaces réservés $ {…} dans les valeurs de propriété de définition de bean et les annotations @Value .

Enfin, nous pouvons obtenir la valeur d'une propriété à l'aide de l' API Environment :

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

4. Propriétés avec Spring Boot

Avant d'entrer dans des options de configuration plus avancées pour les propriétés, passons un peu de temps à examiner la prise en charge des nouvelles propriétés dans Spring Boot.

De manière générale, ce nouveau support implique moins de configuration par rapport à Spring standard , ce qui est bien sûr l'un des principaux objectifs de Boot.

4.1. application.properties: le fichier de propriétés par défaut

Boot applique sa convention typique sur l'approche de configuration aux fichiers de propriétés. Cela signifie que nous pouvons simplement mettre un fichier application.properties dans notre répertoire src / main / resources , et il sera détecté automatiquement . Nous pouvons ensuite y injecter toutes les propriétés chargées normalement.

Ainsi, en utilisant ce fichier par défaut, nous n'avons pas besoin d'enregistrer explicitement une PropertySource ou même de fournir un chemin vers un fichier de propriétés.

Nous pouvons également configurer un fichier différent lors de l'exécution si nécessaire, en utilisant une propriété d'environnement:

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

Depuis Spring Boot 2.3, nous pouvons également spécifier des emplacements de caractères génériques pour les fichiers de configuration .

Par exemple, nous pouvons définir la propriété spring.config.location sur config / * / :

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

De cette façon, Spring Boot recherchera les fichiers de configuration correspondant au modèle de répertoire config / * / en dehors de notre fichier jar. Cela est pratique lorsque nous avons plusieurs sources de propriétés de configuration.

Depuis la version 2.4.0 , Spring Boot prend en charge l'utilisation de fichiers de propriétés multi-documents , de la même manière que YAML le fait par conception:

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

Notez que pour les fichiers de propriétés, la notation à trois tirets est précédée d'un caractère de commentaire ( # ).

4.2. Fichier de propriétés spécifiques à l'environnement

Si nous devons cibler différents environnements, il existe un mécanisme intégré pour cela dans Boot.

Nous pouvons simplement définir un fichier application-environment.properties dans le répertoire src / main / resources , puis définir un profil Spring avec le même nom d'environnement.

Par exemple, si nous définissons un environnement de « mise en scène », cela signifie que nous allons devoir définir une mise en scène profil puis application-staging.properties .

Ce fichier env sera chargé et prévaudra sur le fichier de propriétés par défaut. Notez que le fichier par défaut sera toujours chargé, c'est juste que lorsqu'il y a une collision de propriétés, le fichier de propriétés spécifique à l'environnement est prioritaire.

4.3. Fichier de propriétés spécifiques au test

Nous pouvons également avoir besoin d'utiliser des valeurs de propriété différentes lorsque notre application est en cours de test.

Spring Boot handles this for us by looking in our src/test/resources directory during a test run. Again, default properties will still be injectable as normal but will be overridden by these if there is a collision.

4.4. The @TestPropertySource Annotation

If we need more granular control over test properties, then we can use the @TestPropertySource annotation.

This allows us to set test properties for a specific test context, taking precedence over the default property sources:

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

If we don't want to use a file, we can specify names and values directly:

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

We can also achieve a similar effect using the properties argument of the @SpringBootTest annotation:

@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. Hierarchical Properties

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.

Comme toujours, l'intégralité du code supportant l'article est disponible sur GitHub.