Свойства с Spring и Spring Boot

1. Общ преглед

Този урок ще покаже как да настроите и използвате свойства през Spring чрез Java конфигурация и @PropertySource.

Ще видим и как работят свойствата в Spring Boot.

2. Регистрирайте файл със свойства чрез анотации

Spring 3.1 също представя новата анотация @PropertySource като удобен механизъм за добавяне на източници на свойства към околната среда.

Можем да използваме тази анотация заедно с анотацията @Configuration :

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

Друг много полезен начин за регистриране на нов файл с свойства е използването на заместител, който ни позволява да избираме динамично правилния файл по време на изпълнение :

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

2.1. Определяне на множество местоположения на имоти

В @PropertySource анотацията се повтаря според Java 8 конвенции. Следователно, ако използваме Java 8 или по-нова, можем да използваме тази анотация, за да дефинираме множество местоположения на свойства:

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

Разбира се, можем да използваме и анотацията @PropertySources и да посочим масив от @PropertySource . Това работи във всяка поддържана версия на Java, не само в Java 8 или по-нова версия:

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

И в двата случая си струва да се отбележи, че в случай на сблъсък на име на свойство, предимство има последният прочетен източник.

3. Използване / инжектиране на свойства

Инжектирането на свойство с анотацията @Value е лесно:

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

Можем също да посочим стойност по подразбиране за свойството:

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

Новият PropertySourcesPlaceholderConfigurer добавят през пролетта на 3.1 решителност $ {...} контейнери в определението боб стойността на имотите и @Value анотации .

И накрая, можем да получим стойността на свойство, използвайки API на околната среда :

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

4. Свойства с Spring Boot

Преди да влезем в по-разширени опции за конфигуриране на свойства, нека прекараме известно време в разглеждане на новата поддръжка на свойства в Spring Boot.

Най-общо казано, тази нова поддръжка включва по-малко конфигурация в сравнение със стандартната Spring , което разбира се е една от основните цели на Boot.

4.1. application.properties: Файлът на свойствата по подразбиране

Boot прилага типичната си конвенция по отношение на конфигурационния подход към файловете със свойства. Това означава, че можем просто да поставим файл application.properties в нашата директория src / main / resources и той ще бъде автоматично открит . След това можем да инжектираме всички заредени свойства от него, както обикновено.

Така че, като използваме този файл по подразбиране, не е нужно да регистрираме изрично PropertySource или дори да предоставяме път към файл със свойства.

Също така можем да конфигурираме различен файл по време на изпълнение, ако е необходимо, като използваме свойство на околната среда:

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

От Spring Boot 2.3, ние също можем да посочим заместващи места за конфигурационни файлове .

Например, можем да зададем свойството spring.config.location на config / * / :

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

По този начин Spring Boot ще търси конфигурационни файлове, съответстващи на шаблона на директория config / * / извън нашия jar файл. Това е полезно, когато имаме множество източници на конфигурационни свойства.

От версия 2.4.0 , Пролет Boot опори, използващи свойства мулти-документни файлове , подобно както YAML прави по проект:

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

Имайте предвид, че за файловете със свойства нотацията с три черти се предшества от знак за коментар ( # ).

4.2. Файл със свойства на специфични за околната среда

Ако трябва да се насочим към различни среди, има вграден механизъм за това в Boot.

Можем просто да дефинираме файл application-environment.properties в директорията src / main / resources и след това да зададем Spring профил със същото име на среда.

Например, ако дефинираме среда за „подреждане“, това означава, че ще трябва да дефинираме подготвителен профил и след това application-staging.properties .

Този env файл ще бъде зареден и ще има предимство пред файла със свойства по подразбиране. Имайте предвид, че файлът по подразбиране все още ще бъде зареден, просто когато има сблъсък на свойство, файлът на свойствата, специфичен за околната среда, има предимство.

4.3. Файл със специфични за теста свойства

Може също да имаме изискване да използваме различни стойности на свойствата, когато приложението ни е в процес на тестване.

Spring Boot се справя с това, като погледнем в нашата директория src / test / resources по време на пробно изпълнение . Отново, свойствата по подразбиране ще продължат да се инжектират както обикновено, но ще бъдат заменени от тях, ако има сблъсък.

4.4. В @TestPropertySource анотацията

Ако се нуждаем от по-подробен контрол върху тестовите свойства, тогава можем да използваме анотацията @TestPropertySource .

Това ни позволява да зададем тестови свойства за конкретен тестов контекст, като имаме предимство пред източниците на свойства по подразбиране:

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

Ако не искаме да използваме файл, можем директно да посочим имена и стойности:

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

Също така можем да постигнем подобен ефект, като използваме аргумента свойства на анотацията @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. Йерархични свойства

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.

Както винаги, целият код, подкрепящ статията, е достъпен в GitHub.