Once again about properties or where it comes from

The cat is trying to understand where it is indicated on which port the web server will start

The cat is trying to understand where it is indicated on which port the web server will start

What are we talking about?

Hi all! This article will focus on custom configuration settings for Spring applications. When I just started learning Spring, naturally, one of the sources of knowledge was ready-made examples, sample projects. And I was terribly infuriated that some values ​​\u200b\u200bneeded for the application to work appeared “out of nowhere”. For example, the author of some tutorial suggested to check the newly created training application to go to localhost on port 8088. Where does 8088 come from? Why not 8089? It turned out that there are special files for such custom settings. So:

What are the custom options?

Custom parameters are used by Spring itself, various libraries, and, at the request of the developer, their own can be added. List of all Spring parameters can be seen here.

For example, on which port the built-in http server will spin (if we use Spring Web), the parameter is responsible server.port. In the same tutorial from the introduction in the corresponding file server.port was equal 8088. It looks (in the simplest case) like this:

server.port=8088

A parameter name can (and usually does) have multiple parts. For example, all “spring” parameters begin with the word “spring”. Custom (user) parameters entered by the developer of the final application can begin, for example, with the word application or any other. Depending on the file format used, parts are separated differently (see the next section). The simplest option, just dots. User parameters example:

# Личное дело любимой собаки
application.dog.name=Полкан
application.dog.breed=овчарка
application.dog.color=коричневый
# Имя любимого кота
application.cat.name=Мурзик

What are the sources of custom settings?

Custom settings are stored in a file that can be named differently:

  • application.<…> – base default

  • – the file name and path to it is determined by the @PropertySource annotation

  • bootstrap.<...> – initial parameter values ​​when using spring-cloud

  • .<...> — matches the application name, available through the config server (relevant for Spring Cloud)

Each of these options can be in key/value format or in yml(yaml) format. In the case of key/value, the file must have the extension properties and, as you might guess, each line should look like “key=value”. Comment lines begin with the “#” character. The most common option is when the parameter file is called application.properties.

Sample content in the previous section.

The Yml(yaml) format is a tree, where each part of the parameter name is a node name and the value is a leaf. The name of each node is written on a new line and ends with a colon. Each child node is located two spaces to the right of the parent. Comment lines also begin with the “#” character. It all looks something like this:

application:
  # Личное дело любимой собаки
  dog:
    name: Полкан
    breed: овчарка
    color: коричневый
  # Имя любимого кота
  cat:
    name: Мурзик
...

Be careful, the number of spaces in indentation for the yaml format matters!

The first option application.property or application.yml out of the box only works in Spring Boot. In classic Spring, the @ annotationPropertySource required. It is placed on any configuration class (a class already annotated with @Configuration), which is supposed to access custom parameters, and looks like this:

@Configuration
@PropertySource("classpath:application.properties")
public class AppConfig { … }

Also, Spring Boot allows you to have multiple configuration options in different files. In this case, the file name must match the following pattern: application..propertiesso or application..yml. The actual option will be selected depending on the active spring profile. What it is is not discussed in this article, but you can read, for example, here.

Where are these custom settings sources located?

By default, these files should be in classpath. Most often in the folder “src/main/resources“. If there is a desire to add the settings to a file with a different name and / or in a different path, it is necessary before the class marked with the @ annotationConfiguration or before the class in which you plan to use custom parameters, specify the already familiar annotation @PropertySource with the path and name of the parameter file, like so:

@PropertySource("file:/home/alex/tmp/temporary.properties")

Also, config-server can be used as a source of configuration files when using SpringBoot. How does the application know which url to look for config-server? It first finds a local config to load basic settings like the path to config-server. This file must be in the classpath and named application.properties or application.yml or application.yaml. Depending on the version of Spring Cloud, the name may not be applicationA bootstrap. Valid filename extensions are the same.

In this file, Spring looks for a parameter named (in dot format) “spring.cloud.config.uri” or “spring.cloud.config.discovery.serviceId“. Example:

spring.cloud.config.uri=http://localhost:8888

or

spring.cloud.config.discovery.serviceId=config

The second option, in case of using Registry and Discovery Service (What kind of animal is this? can be read here).

How do I use (access) custom options?

There are several ways. With an annotation @Valueannotations @ConfigurationProperties or with the help of the Environment spring interface, or rather, the methods of the classes that it extends. Let’s take a closer look at all the options.

This method is the easiest. It is enough to specify this annotation before the class field, and when creating the bean, Spring will initialize this field with the value from the config file. Example:

@Value("${application.dog.name}")
private String dogName;

If the config is the same as in our example from the beginning of the article, then after creating a bean for a class that has a field dogName, this field will be equal to “Polkan”. Annotation @Value There is another very useful feature. Before the closing curly brace, separated by a colon after the parameter name, you can set its default value. That is, if the specified property is not found in the config, then the field marked with such an annotation will be equal to this value. Example:

@Value("${application.dog.size:маленькая собачка}")
private String dogSize;

Since in our parameter config application.dog.size no, field dogSize will be initialized by default with the value “little dog”.

Here, to access the properties specified in the config, you need to create a special class marked with this annotation. Further, the fields of this class will correspond to the properties of config. Moreover, the correspondence will be established automatically, based on the names. Moreover, the names should be similar “approximately”. The last possibility is called Relaxed binding.

In order to make it more convenient, you can set a prefix in the annotation, from which the properties from the config, bound to the fields of the annotated class, will begin. A bit confusing? Let’s look at an example. Config let’s take a friend, about Polkan and Murzik. Here’s what the corresponding classes would look like:

@Data // Это аннотация lombok, которая автоматически генерирует getter-ы и setter-ы
@Configuration // Аннотация Spring, благодаря которой автоматически будет создан been
@ConfigurationProperties(prefix = "application.dog")
public class DogConfig {
    private String name;
    private String breed;
    private String color;
}
@Data // Это аннотация lombok, которая автоматически генерирует getter-ы и 
//setter-ы
@Configuration // Аннотация Spring, благодаря которой автоматически 
//будет создан been
@ConfigurationProperties(prefix = "application.cat")
public class CatConfig {
    private String name;
}

And access to our settings from consumer classes will look like this:

@Component
public class ConfigConsumer {
    @Autowired
    private DogConfig dogConfig;

    @Autowired
    private CatConfig catConfig;

    public void printConfiguration() {
        System.out.println(dogConfig.getName());
        System.out.println(dogConfig.getBreed());
        System.out.println(dogConfig.getColor());

        System.out.println(catConfig.getName());
    }
}

Well main:

@SpringBootApplication
// @EnableConfigurationProperties(AppConfig.class) - требуется, если 
// НЕ Spring Boot
public class SpringPropertiesApplication {
      public static void main(String[] args) {
	ApplicationContext context =
                   SpringApplication.run(SpringPropertiesApplication.class, args);
	context.getBean(ConfigConsumer.class).printConfiguration();
	System.exit(0);
      }
}

Spring automatically creates a bean of type Environment. And if you auto-brew it, then through it you can access both the customizable parameters and the environment variables of the application execution environment. Example:

@Component
public class ConfigConsumer {
 
    @Autowired
    private Environment env;

    public void printConfiguration() {
 
        System.out.println(env.getProperty("HOME"));
        System.out.println(env.getProperty("application.dog.name"));
   }
}

Doing the above main-a will output in my case:

/home/alex
Полкан

What are the nuances?

  1. annotation @Value put on the class field, annotation @ConfigurationProperties – per class.

  2. Custom settings may not be available everywhere. For example, in the constructor to use them directly will not work. It has to do with the lifecycle of a spring application. The fact is that in order for the parameters to be available, spring must process our configs. And he does this immediately before creating the beans, or rather, before placing them in the context, after the constructor is called. However, if the class whose constructor we want to use a custom parameter is itself a spring bean (annotated with @Component or similar) and a constructor with a parameter, then this restriction can be bypassed. It will look like this:

@Data
@Component // Bean Spring
public class ParamInConstructor {
    private String priority;

    @Autowired
    public ParamInConstructor(@Value("${priority:normal}") String priority) {
        this.priority = priority;
    }
}
  1. There is a nuance with national alphabets. All custom parameter values ​​in config files must be encoded ISO 8859-1, otherwise we risk getting bugs in a running application. Fortunately, modern IDEs have an automatic transcoding feature called native-to-ascii. If you turn it on, then you can not think about this problem. In IntelyJ Idea version 2020.3.4 auto-transcoding is enabled here:

Setting native-to-ascii in Idea IDE

Setting native-to-ascii in Idea IDE

  1. When defining a parameter in config, you can assign it the value of an environment variable. Let’s say we want to be able to set the name of our favorite dog in the “DOG” environment variable. But, if those who will deploy our application fully trust us and are not going to create any environment variables, then the default name should be substituted. It is done like this:

application.dog.name=${DOG:Полкан}
  1. The parameter value can be multiline. To do this, you need to insert “\n” at the transfer point, which means a line break:

application.cat.characteristics=Big \n Brown Soft Lazy

Conversely, it is possible that the parameter value is one, but a very long string. Which does not fit into the width of the screen. For convenience, you can write it in several lines, but the parameter will contain everything in one. To do this, put a backslash “\” in the place of the transition to a new line:

application.cat.characteristics=Big \
  Brown \
  Soft \
  Lazy

Parameter application.cat.characteristics in the example will contain one line, although it is written in several.

What’s left?

Well, probably, there is still a lot of things left, but what I would like to mention, but what we will not dwell on in detail:

  • meta-data. In the case of accessing custom settings via @ConfigurationProperties, it is possible to use meta-data. This is detailed information about customizable options to help the IDE provide contextual hints, suggest auto-completion, warn about type mismatches, and so on.

  • SpEL evaluation. This feature, on the contrary, is available when using the @Value annotation. SpEL – Spring Expression Language – Spring Expression Language. A very useful feature that allows, for example, to decompose the value of the parameter into elements of the list (ArrayList):

arrayOfStrings=cat, dog, bear
@Value("#{'${arrayOfStrings}'.split(',')}")
private List<String> listOfStrings;

Conclusion

This article does not purport to be a comprehensive guide to using Spring custom options. Rather, it is intended to help “find ends” in someone else’s code for those who, for some reason, are trying to figure it out.

Useful links:

Custom Spring Options

Spring Profiles

Comparison @ConfigurationProperties and @Value

Working with parameters via @ConfigurationProperties

Spring Cloud: Registry and Discovery Service

SpEL in @Value to work with ArrayList and HashMap

Guide to Spring @Value annotations

Similar Posts

Leave a Reply

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