Hartanah dengan Spring and Spring Boot

1. Gambaran keseluruhan

Tutorial ini akan menunjukkan cara mengatur dan menggunakan sifat pada musim bunga melalui konfigurasi Java dan @PropertySource.

Kami juga akan melihat bagaimana hartanah berfungsi di Spring Boot.

2. Daftarkan Fail Properties melalui Anotasi

Spring 3.1 juga memperkenalkan anotasi @PropertySource baru sebagai mekanisme yang sesuai untuk menambahkan sumber harta tanah ke persekitaran.

Kami boleh menggunakan anotasi ini bersama dengan anotasi @Configuration :

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

Kaedah lain yang sangat berguna untuk mendaftarkan fail sifat baru adalah menggunakan placeholder, yang membolehkan kami memilih fail yang betul secara dinamik pada waktu runtime :

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

2.1. Mendefinisikan Pelbagai Lokasi Harta

The @PropertySource anotasi digunakan berulang mengikut Java 8 konvensyen. Oleh itu, jika kita menggunakan Java 8 atau lebih tinggi, kita dapat menggunakan anotasi ini untuk menentukan beberapa lokasi harta tanah:

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

Sudah tentu, kita juga boleh menggunakan anotasi @PropertySources dan menentukan pelbagai @PropertySource . Ini berfungsi dalam versi Java yang disokong, bukan hanya di Java 8 atau lebih tinggi:

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

Dalam kedua-dua kes, perlu diperhatikan bahawa sekiranya berlaku perlanggaran nama harta tanah, sumber terakhir yang dibaca diutamakan.

3. Menggunakan / Menyuntikkan Sifat

Menyuntik harta tanah dengan penjelasan @Value adalah mudah:

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

Kami juga dapat menentukan nilai lalai untuk harta tanah:

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

PropertySourcesPlaceholderConfigurer yang baru ditambahkan pada Spring 3.1 menyelesaikan $ {…} placeholder dalam nilai harta definisi kacang dan anotasi @Value .

Akhirnya, kita dapat memperoleh nilai harta tanah dengan menggunakan API Lingkungan :

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

4. Sifat Dengan Spring Boot

Sebelum kita pergi ke pilihan konfigurasi yang lebih maju untuk hartanah, mari luangkan masa untuk melihat sokongan hartanah baru di Spring Boot.

Secara umum, sokongan baru ini melibatkan konfigurasi yang lebih sedikit berbanding Spring biasa , yang tentunya merupakan salah satu tujuan utama Boot.

4.1. application.properties: Fail Harta Lalai

Boot menggunakan konvensyen khasnya mengenai pendekatan konfigurasi ke fail harta tanah. Ini bermaksud bahawa kita hanya dapat meletakkan file application.properties di direktori src / main / resources kami , dan ia akan dikesan secara automatik . Kita kemudian dapat menyuntikkan sifat yang dimuat dari itu seperti biasa.

Oleh itu, dengan menggunakan fail lalai ini, kita tidak perlu mendaftarkan PropertySource secara eksplisit atau bahkan memberikan jalan ke fail harta tanah.

Kami juga dapat mengkonfigurasi fail yang berbeza pada waktu berjalan jika perlu, menggunakan harta persekitaran:

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

Pada Spring Boot 2.3, kami juga dapat menentukan lokasi wildcard untuk fail konfigurasi .

Sebagai contoh, kita boleh menetapkan properti spring.config.location ke config / * / :

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

Dengan cara ini, Spring Boot akan mencari fail konfigurasi yang sepadan dengan corak konfigurasi / * / direktori di luar fail balang kami. Ini sangat berguna apabila kita mempunyai banyak sumber sifat konfigurasi.

Sejak versi 2.4.0 , Spring Boot menyokong penggunaan fail sifat pelbagai dokumen , sama seperti yang dibuat oleh YAML dengan reka bentuk:

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

Perhatikan bahawa untuk fail sifat, notasi tiga titik didahului oleh watak komen ( # ).

4.2. Fail Hartanah Khusus Alam Sekitar

Sekiranya kita perlu menargetkan persekitaran yang berbeza, ada mekanisme bawaan untuk itu di Boot.

Kita hanya dapat menentukan fail application-environment.properties dalam direktori src / main / resources , dan kemudian menetapkan profil Spring dengan nama persekitaran yang sama.

Sebagai contoh, jika kita menentukan persekitaran "pementasan", itu bermaksud kita harus menentukan profil pementasan dan kemudian aplikasi-staging.properties .

Fail env ini akan dimuat dan akan diutamakan daripada fail harta lalai. Perhatikan bahawa fail lalai masih akan dimuat, hanya apabila berlaku perlanggaran harta tanah, fail harta tanah khusus diutamakan.

4.3. Fail Sifat Khusus Ujian

Kami mungkin juga mempunyai syarat untuk menggunakan nilai harta yang berlainan ketika aplikasi kami dalam ujian.

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.

Seperti biasa, keseluruhan kod yang menyokong artikel ini terdapat di GitHub.