Learn to use @PropertySource, @Value and @ConfigurationProperties annotations to register property files and inject property values into a Spring boot application’s configuration.
1. Register Properties Files with @PropertySource
The @PropertySource annotation is used to register the property files in a Spring application.
1.1. Spring Boot Automatically Loads application.properties
By default, Spring Boot automatically loads the application.properties whenever it starts up. We can access the properties defined in application.properties using @Value annotation.
Let us assume that we have the following application.properties file.
application.name=Demo App
If we have to access this property in a Spring @Component, we can use the @Value annotation.
@Component
//This is optional as Spring boot loads application.properties by default
@PropertySource("classpath:application.properties")
public class AppProperties {
@Value("${application.name}")
private String appName;
}
In addition to application.properties, Spring boot automatically loads the profile-specific property file. For example, if the active profile is dev
then Spring boot will load the application-dev.properties file along with application.properties file.
If there are any conflicts between values in the two files, then the profile-specific file wins. Ideally, we should specify the default values in application.properties and override them with profile-specific values in application-dev.properties file.
1.2. Loading Custom Properties Files
If we want to change which file Spring Boot reads by default then we can use the spring.config.name
property.
export SPRING_CONFIG_NAME=foo
Now when we run the spring boot application, it will load all the properties from foo.properties file.
If we have a different properties file or multiple properties files, then we can explicitly use the @PropertySources
annotation to specify those property files. Specifying application.properties is optional in this case, too.
@Component
@PropertySources({
@PropertySource("classpath:jms.properties"),
@PropertySource("classpath:datasource.properties")
})
public class AppProperties {
//...
}
1.3. Duplicate Property Resolution
This is important to note that if there are two or more properties with the same name then the property value will be chosen from the last occurrence of the property file.
Duplicate property values do not raise any exceptions.
2. Inject Property Values with @Value
The @Value is used at the field or method/constructor parameter level to initialize the field with a default value expression populated from the property file.
- SpEL (Spring Expression Language) expressions can be used to inject values using
#{systemProperties.myProp}
syntax. - Property values can be injected using
${my.app.myProp}
style property placeholders.
Also, we can assign a default value to a property key. This helps in preventing the exception when the property key is missing or not found in the properties file.
@Component
@PropertySources({
@PropertySource("classpath:jms.properties"),
@PropertySource("classpath:datasource.properties")
})
public class AppProperties {
@Value("${application.name:My App}")
private String appName;
@Value("${spring.datasource.url}")
private String datasourceUrl;
}
To insert a list of values in a String array or List, we can use the following example. For reference, the property name and value are:
app.environments=local,dev,test,prod
Injecting such values into String array is supported, by default.
@Value("${app.environments}")
private String[] environments;
To inject these values into a List, we need to use the SpEL syntax.
@Value("#{'${app.environments}'.split(',')}")
private List<String> environmentsList;
3. Bind Fields to Property Values with @ConfigurationProperties
The @ConfigurationProperties
is used to bind the member fields in a bean with the property values defined in a properties file. Binding is either performed by calling setters on the annotated class or, if @ConstructorBinding
is in use, by binding to the constructor parameters.
Note that contrary to @Value
, SpEL expressions are not evaluated since property values are externalized.
For example, suppose we have the following properties in application.properties
file.
spring.datasource.url=jdbc:h2:file:C:/temp/test
spring.datasource.username=sa
spring.datasource.password=
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.dialect=org.hibernate.dialect.H2Dialect
To bind these properties in class fields, we need to create fields with the exactly same name as the property name. Also, we must mention the prefix
, if any.
@Data
@Component
@ConfigurationProperties(prefix = "spring.datasource")
public class DatasourceProps {
private String url;
private String username;
private String password;
private String driverClassName;
private String dialect;
}
If the above properties are part of a separate file datasource.properties, then we can use @PropertySource
to specify the property file name.
@Data
@Component
@PropertySource("classpath:datasource.properties")
@ConfigurationProperties(prefix = "spring.datasource")
public class DatasourceProps {
//fields
}
4. Validate Property Values
Start with importing spring-boot-starter-validation
module in the project. This module imports the hibernate-validator project that implements the JSR-303 specification.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
For validating the field-property bindings, we can use @Validated
annotation. It is a variant of JSR-303’s @Valid
, supporting the specification of validation groups.
In addition to @Validated, we need to apply specific constraints on the fields using the javax.validation.constraints annotations. If any of these validations fail, then the application would fail to start with an IllegalStateException.
@Data
@Component
@Validated
public class AppProperties {
@NotEmpty
@Value("${application.name}")
private String appName;
@NotEmpty
@Value("${spring.datasource.url}")
private String datasourceUrl;
}
5. Include Additional Configuration Files
To include additional property files, we can use the spring.config.import
property within the application.properties
or application.yml
file. Imports are processed as they are discovered, and are treated as additional documents inserted immediately below the one that declares the import.
For example, we can have the following import statement in application.properties file.
application.name=Demo App
spring.config.import=optional:file:./dev.properties
The above import will try to search and import the dev.properties file in the current working directory. If the file is found then its values will take precedence over the file that triggered the import. If the file is not found then no error is reported.
Note that the position of spring.config.import
statement in the existing property file does not matter. It will always produce the same result, as discussed above.
If we specify multiple locations then all the locations will be processed in the order that they are defined, with later imports taking precedence. We can also specify a directory containing multiple property files.
spring.config.import=classpath:datasource.properties,
classpath:mysql-properties.yml,
optional:file:./cloud-deployment.properties,
classpath:test-properties/
If a directory is imported then loaded files are sorted alphabetically. If we need a different order, then we should list each location as a separate import.
The spring.config.import
property can be set using the server startup arguments as well:
$ java -jar myproject.jar --spring.config.import=\
classpath:datasource.properties,\
classpath:mysql-properties.properties,\
optional:file:./cloud-deployment.properties,\
classpath:test-properties/
7. Conclusion
In this tutorial, we learned to import and register default property files as well as multiple custom property files using @PropertySource and @PropertySources annotations. We learned to bind the fields with property values using @ConfigurationProperties annotations.
We learned to validate the property values during application startup and even specify optional values if a corresponding property name is not found.
Happy Learning !!
Leave a Reply