Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / Spring

Tidy Spring - Configuration Properties

5.00/5 (2 votes)
28 Apr 2016CPOL4 min read 8.1K  
List of practices to work effectively on Spring applications. This part focuses on effective handling of configuration properties.

Spring has become extremely popular among Java developers. After all, it's a great project with tons of useful features. I decided to share with you a list of practices I use to work effectively on Spring applications, avoid unnecessary framework coupling and achieve a tidy application architecture. The whole set of practices was too long for one article so I divided it into smaller parts. In this one, I will focus on effective handling of configuration properties.

YAML

It is possible to use YAML files as a source of properties. I find this format more readable than conventional properties file, especially when you get used to it. It also supports collections and other data structures.

Example .properties file (from Spring Boot docs):

environments.dev.url=http://dev.bar.com
environments.dev.name=Developer Setup
environments.prod.url=http://foo.bar.com
environments.prod.name=My Cool App

And the YAML equivalent:

environments:
    dev:
        url: http://dev.bar.com
        name: Developer Setup
    prod:
        url: http://foo.bar.com
        name: My Cool App

Of course, if your set of properties is pretty flat and simple, you can stick to classic properties files. But remember, complexity grows over time and later you might be reluctant to deal with it. If you already have some complex property configuration, YAML is definitely the way to go.

Avoid Spreading @Value's

In annotation-heavy Spring applications, I often see @Value annotations spread all over the place. This seems like a really bad idea to me - it makes each of the classes dependent on a property name (or even multiple property names!). If you want to change it, you have to update all files referencing it. If you want to find out where the property is used, you have to go for a text search. Don't do this to yourself.

If you follow the tip (from the previous post) to extract business features into Spring-independent components, then the ability to spread @Value around is already limited (good for you).

@ConfigurationProperties

A much better approach to handling properties is to use a few configuration classes annotated with @ConfigurationProperties. If you follow the same naming in config file and the class, you don't even have to explicitly specify the key's name.

For the example set of properties:

externalApi:
    url: http://externalApi.com
    port: 8080
    username: foo
    password: bar

The configuration class would look like this:

Java
@Component
@ConfigurationProperties(prefix = "externalApi")
public class ExternalApiConfiguration {
    private String url;
    private String port;
    private String username;
    private String password;

    // getters and setters
}

With this approach, when you want to find/change property handling, you just look for a single class with a descriptive name.

It's very useful to establish a naming convention for these classes, e.g., SomethingConfiguration or SomethingProperties. Once you have a few of these in a single component, you might go for a common package, e.g., config.

Custom Structures

In case of complex configurations, e.g., lists or nested key-values, we can write custom structures to hold the properties in a better manner. All we have to do is create a class with desired properties and a bunch of getters/setters.

For the environments example from Spring docs:

environments:
    dev:
        url: http://dev.bar.com
        name: Developer Setup
    prod:
        url: http://foo.bar.com
        name: My Cool App

The configuration class could look like this:

Java
@Component
@ConfigurationProperties(prefix = "environments")
public class EnvironmentsConfiguration {
    private Environment dev;
    private Environment prod;

    // getters and setters

    public static class Environment {
        private String url;
        private String name;

        // getters and setters
    }
}

Custom configuration structures reduce some boilerplate code and enhance readability. You avoid writing lots of similar methods like:

Java
public String getDevUrl() { ... }
public String getDevName() { ... }
public String getProdUrl() { ... }
public String getProdName() { ... }

Validation

When we use @ConfigurationProperties, we get out-of-the-box support for properties validation. We should make use of that, so that our application fails fast at start instead of misbehaving.

The ExternalApiConfiguration mentioned above, with validation, could look like this:

Java
@Component
@ConfigurationProperties(prefix = "externalApi")
public class ExternalApiConfiguration {

    @URL
    private String url;

    @Max(value = 65535)
    private int port;

    @NotBlank
    private String username;

    @NotBlank
    private String password;

If you can't find a predefined validator for your needs, it's very easy to write your own. All you have to do is create an annotation and associate it with a ConstraintValidator.

Java
@Constraint(validatedBy = MyConstraintValidator.class)
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyConstraint {
    String message() default "Default message used when constraint fails";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};
}

And the associated validator:

Java
public class MyConstraintValidator implements ConstraintValidator<MyConstraint, String> {

    @Override
    public void initialize(MyConstraint myConstraint) {}

    @Override
    public boolean isValid(String value, ConstraintValidatorContext c) {
        return value.equals("Tidy Java");
    }
}

It is also possible to specify an error message to display if validation fails. Unfortunately, in current Spring Boot version, if validation fails, Spring prints a long, ugly exception message and adding your own does not help. Therefore, I would recommend to avoid cluttering the code with custom messages.

In my projects, I rarely have a property without any validation. It's also not uncommon for me to write a custom validator, since it's both useful and easy. If you haven't tried it before, give it a shot.

Business Properties

It often happens that we need access to configuration in our business classes. In such cases, we should use Dependency Inversion to avoid framework coupling. We create an interface on the business side, then implement it in the "main" component and inject the configuration into appropriate objects.

Suppose we wanted to use environment configuration in the business part of the application. We could create an interface like this:

Java
public interface ExternalApiConfiguration {
    String getUrl();

    int getPort();

    String getUsername();

    String getPassword();
}

And then make the other class implement the interface:

Java
@Component
@ConfigurationProperties(prefix = "externalApi")
public class SpringExternalApiConfiguration implements ExternalApiConfiguration {

    @URL
    private String url;

    @Max(value = 65535)
    private int port;

    @NotBlank
    private String username;

    @NotBlank
    private String password;

   // getters and setters
}

These interfaces don't get a separate package. Instead, they "sit" next to the classes using them. This way, we express the logical biding between the interface and the using class.

Segregation

Another important thing is to avoid putting all properties into a single configuration class. Instead, go for a few specialized ones. Configuration tends to stack up over time and we might end up with something too big and unmanageable. The same applies to configuration interfaces. Too much knowledge can finally become a problem (see: Interface Segregation Principle).

Conclusion

YAML is a viable replacement for classic properties files. To avoid configuration mess, we should favor a few classes with @ConfigurationProperties over @Value spread all over the place. Complex property structures can be enclosed in separate classes. To avoid misbehaviour caused by configuration, we can use built-in validation with annotations to fail fast. Configuration that has to be accessed from the business part should be abstracted using well-segregated interfaces.

Other Parts of the Series

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)