Inject Properties using @Value Annotations vs Environment vs @ConfigurationProperties

@Value Annotations vs Environment vs @ConfigurationProperties.

Spring provides some ways to inject properties from properties file. We have learned how to use them in 3 articles:
@Value Annotation
Environment
@ConfigurationProperties

This tutorial helps you have a comparison view covering those solutions.

1. @Value Annotation

We can get property value anywhere by using @Value, we can inject default value, List/Arrays, special types (DateTime, Pattern), value that should be treated as null easily with just one line added. So it is very easy to implement.


@DateTimeFormat(pattern="yy/MM/dd HH:mm")
@Value("${config.time.key}")
private LocalDateTime time;

But to be sure that this is possible, PropertySourcesPlaceholderConfigurer must exist in the application contexts that placeholders resolution is required.

	public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
//		return new PropertySourcesPlaceholderConfigurer();
		
		PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
//		configurer.setLocation(new ClassPathResource("myapp.properties"));
		
		configurer.setIgnoreUnresolvablePlaceholders(true);
		configurer.setNullValue("This is NULL");
		
		return configurer;
	}

This approach also has another disadvantage that it makes each class which uses @Value depends on one or more property name. If we wanna change any property, we have to update all files which referenced it, or if we wanna find where it is used, we must make a text searching.

2. Environment

To work with Environment, we use @Autowired:


@Autowired
Environment environment;

or get it from Application Context:


Environment environment= (Environment) context.getEnvironment();

Then, to read the individual property in application, we have getProperty method:


System.out.println("- connection Url: " + environment.getProperty("app.connection.url"));

The Environment is a combination of profiles and properties. It causes the big difference in usage of Environment Object to inject Property Value when comparing to @Value.

"The role of the Environment object with relation to profiles is in determining which profiles (if any) are currently active, and which profiles (if any) should be active by default." - Spring's docs.

That means, with the Environment object, we can access the information related to profiles, but can not if using @Value. So, if we don't need profile's information, we should use @Value annotation.

"In most cases, however, application-level beans should not need to interact with the Environment directly but instead may have to have ${...} property values replaced by a property placeholder configurer such as PropertySourcesPlaceholderConfigurer" - Spring's docs.

However, using Environment to get properties value is just like using @Value - increasing Spring coupling if we don't have a separate class for properties.

3. Getting Properties as a Configuration Service

It is so bad when scattering our configuration in many places. There will be coupling and classifying problem:
- full text searching to find where the property key is used, and feel "good" when renaming just one key wherever it exists.
- every class we created can get any property value, so we can't control which class will use which properties.

To avoid those bad things, just encapsulate them in a Configuration Service Class. In our examples, we have used a class named ApplicationProperties:


//annotations
public class ApplicationPropertiesA {
    @Value("${config.string.key}")
    private String stringKey;

    //getters and setters...
}

public class MyServiceA {
    //inject ApplicationPropertiesA applicationProperties;
}

public class ApplicationPropertiesB {
    private String connectionUrl;
    private String connectionName;

    //getters and setters...
}

//annotations
public class AppConfig {
    @Autowired
    Environment environment;

    @Bean
    ApplicationPropertiesB appProperties() {
        //bean get value: environment.getProperty(...);
        //inject value to ApplicationPropertiesB bean object
        //return bean;
    }
}

public class MyServiceB {
    //inject ApplicationPropertiesB applicationProperties;
    //applicationProperties.getConnectionUrl();
}

4. @ConfigurationProperties

Spring Boot provides an powerful annotation to work with properties that allows complex beans (bean class with sub-class inside) and validation the configuration of our application. And if we have the class follow the same naming with properties file, we don't even have to specify the property key string. If we wanna inject properties like this (with connection object as a sub-object of app object):


app.connection.url=https://ozenero.com
app.connection.name=Java Sample Approach
app.host=localhost
app.port=123

We just create a class like this:


@ConfigurationProperties(ignoreUnknownFields = false, prefix = "app")
public class ApplicationProperties {
    @NotNull
    private Connection connection;
    @NotBlank
    private String host;
    @Max(value = 65535)
    private int port;

    //getters and setters...
}

We use a seperate configuration properties classes annotated with @ConfigurationProperties, so property values can be bound to structured objects conveniently.
With this approach, when we wanna find or change the handling properties object, we just notice a separate component class.

For configuration class of the whole application (AppConfig.java), we should enable class above by using @EnableConfigurationProperties annotation:


@Configuration
@EnableConfigurationProperties(ApplicationProperties.class)
public class AppConfig {
}

To get environment properties value, we create a Service and inject AppicationProperties object and use getter methods easily:


public class MyServiceB {
    //inject ApplicationProperties applicationProperties;
    //applicationProperties.getConnection();
}

5. Conclusion

Each way has it own advantages and disadvantages, we have a view about them. Depending on application and usage, you should consider which is the most effective.

But if you don't have any idea, we highly recommend Spring Boot @ConfigurationProperties, it is the best choice in most cases.

Leave a Reply

Your email address will not be published. Required fields are marked *