Example demonstration of user-defined configuration source of SpringBoot basic series

Posted by dabbott on Sun, 30 Jan 2022 15:15:32 +0100

[SpringBoot basic series] Introduction to the use posture of custom configuration source

The previous blog post introduced some knowledge points of @ Value, and mentioned one point. Can the configuration corresponding to @ Value be obtained from other data sources except in the configuration file, such as redis, db and http?

Having learned about SpringCloud Config, you can give the exact answer. Yes, and it's still cool to use. Remote configuration supports configuration dynamic refresh. Next, let's take a look at how to configure custom data sources in SpringBoot

1. Project environment

Coupon network https://m.fenfaw.net/

1. Project dependency

This project uses springboot 2.2.1 Release + Maven 3.5.3 + idea for development

Open a web service for testing

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

II. Custom configuration source

@The member decorated with Value reads the configuration from the Envrionment when binding the configuration, so what we need to do is to register a custom configuration source, which can realize our demand scenario with the help of MapPropertySource

1. User defined data source

getProperties is the simplest method to override the custom mapysource data source

The implementation is as follows

public class SimplePropertiesSource extends MapPropertySource {
    public SimplePropertiesSource(String name, Map<String, Object> source) {
        super(name, source);
    }

    public SimplePropertiesSource() {
        this("filePropertiesSource", new HashMap<>());
    }

    /**
     * Override this method, which is suitable for obtaining configuration in real time
     *
     * @param name
     * @return
     */
    @Override
    public Object getProperty(String name) {
        // Note that this logic is executed only for configurations that start with customization
        if (name.startsWith("selfdefine.")) {
            return name + "_" + UUID.randomUUID();
        }
        return super.getProperty(name);
    }
}

2. Data source registration

The above just declares the configuration source, and then registers it in the Environment so that it can be used

@RestController
@SpringBootApplication
public class Application {
    private Environment environment;

    @Bean
    public SimplePropertiesSource simplePropertiesSource(ConfigurableEnvironment environment) {
        this.environment = environment;
        SimplePropertiesSource ropertiesSource = new SimplePropertiesSource();
        environment.getPropertySources().addLast(ropertiesSource);
        return ropertiesSource;
    }

    // Get configuration
    @GetMapping(path = "get")
    public String getProperty(String key) {
        return environment.getProperty(key);
    }

    public static void main(String[] args) {
        SpringApplication.run(Application.class);
    }
}

It can be seen from the above output that the random configuration value will be obtained at the beginning of the custom configuration; If it does not start with selfdefine and there is no corresponding configuration, it returns null

3. File based custom configuration source

The above may be a bit of a joke. Next, we will put the configuration source in the user-defined file and support the modification of file configuration

public class FilePropertiesSource extends MapPropertySource {
    public FilePropertiesSource(String name, Map<String, Object> source) {
        super(name, source);
    }

    public FilePropertiesSource() {
        this("filePropertiesSource", new HashMap<>());
    }

    // This method is suitable for retrieving all configurations at one time, and then querying the corresponding configuration from memory to improve service performance
    // Update once every 10s
    @PostConstruct
    @Scheduled(fixedRate = 10_000)
    public void refreshSource() throws IOException {
        String ans =
                FileCopyUtils.copyToString(new InputStreamReader(FilePropertiesSource.class.getClassLoader().getResourceAsStream("kv.properties")));
        Map<String, Object> map = new HashMap<>();
        for (String sub : ans.split("
")) {
            if (sub.isEmpty()) {
                continue;
            }
            String[] kv = StringUtils.split(sub, "=");
            if (kv.length != 2) {
                continue;
            }

            map.put(kv[0].trim(), kv[1].trim());
        }

        source.clear();
        source.putAll(map);
    }
}

A timer is written above to refresh the configuration information in memory every 10s. Of course, a file change listener can be configured here. If you are interested, you can see how Java can play the monitoring of file changes

Corresponding configuration file

user=xhh
name=One ash
age=18

The registered posture is consistent with the above, so it will not be explained separately. Next, we will demonstrate the use

It can be seen from the above that after the configuration in the file is modified, it will be refreshed after a period of time

4. @Value binding custom configuration

Next, let's see if binding @ Value to a custom configuration can succeed

Adjust the Application above and add a member attribute

@Value("${name}")
private String name;

@GetMapping(path = "get")
public String getProperty(String key) {
    return name + "|" + environment.getProperty(key);
}

Another test found an exception, saying that this configuration does not exist!!!

(that's too much. After watching it for a long time, I was told that I couldn't. why don't you make a bad comment quickly 😡😡😡)

I've written here. Of course, I have to continue to try to save it. Why can I get the configuration directly through the Environment, but the @ Value annotation binding can't?

”The culprit "lies in the initialization sequence. Before I plug my customized configuration source into the Envrionment, you have a meeting to start binding. It's like preparing to give a bad comment to" a gray blog ". It turns out that you haven't paid attention yet... (well, I admit you can comment if you haven't paid attention to it.) 😭)

According to the previous knowledge points (as for which knowledge points, it's impossible to make a long story short. See the following selected blog posts)

  • [SpringBoot basic series - actual combat] how to specify bean s to load first (application)
  • Several postures for specifying initialization order of Bean in SpringBoot series tutorials
  • Spring boot series of tutorials on the wrong loading order of beans and refuting the rumor of using posture

The simplest way to solve this problem is as follows

Create an independent configuration class to realize the registration of user-defined data sources

@Configuration
public class AutoConfig {
    @Bean
    public FilePropertiesSource filePropertiesSource(ConfigurableEnvironment environment) {
        FilePropertiesSource filePropertiesSource = new FilePropertiesSource();
        environment.getPropertySources().addLast(filePropertiesSource);
        return filePropertiesSource;
    }
}

Specify bean dependencies on the test class

@DependsOn("filePropertiesSource")
@EnableScheduling
@RestController
@SpringBootApplication
public class Application {
    @Autowired
    private Environment environment;

    @Value("${name}")
    private String name;

    @GetMapping(path = "get")
    public String getProperty(String key) {
        return name + "|" + environment.getProperty(key);
    }

    public static void main(String[] args) {
        SpringApplication.run(Application.class);
    }
}

Test again and the results are as follows

As can be seen from the above illustration, there is no problem binding the customized data source configuration. However, when the configuration changes, the bound name field is not updated

In short, it doesn't support dynamic refresh, which is uncomfortable. I just want dynamic refresh. What should I do?

  • Don't worry, the new blog has been arranged. In the next article, I'll give it to you. (if you're afraid of getting lost, you might as well pay attention to "a gray blog“ 🐺)

5. Summary

Finally, according to the usual summary, although the length of this article is long, the knowledge points are relatively concentrated. To sum up, it can be done in two sentences

  • The user-defined configuration source is implemented by inheriting MapPropertySource. It is registered in Envrionment and can be used by @ Value
  • When Binding custom configuration source with @ Value, pay attention to the registration order before bean initialization

OK, here is the end of the text. I am a gray. Welcome all the big guys to step on the long grass official account, "a gray blog".

3. Can not miss the source code and related knowledge points

0. Project

  • Project: https://github.com/liuyueyi/spring-boot-demo
  • Source code: https://github.com/liuyueyi/spring-boot-demo/tree/master/spring-boot/002-dynamic-envronment

Configure series blog posts

  • [SpringBoot basic series] @ Value what you don't know about
  • [SpringBoot basic series] things you don't know in the configuration properties configuration binding
  • [SpringBoot basic series] PropertySource loading Yaml configuration file example demonstration
  • [SpringBoot basic series] implement a custom configuration loader (application)
  • Configuration refresh of configuration information in SpringBoot Basics
  • Custom configuration specification and configuration reference of configuration information in SpringBoot Foundation
  • SpringBoot Basics: environment configuration information
  • How to read the configuration information in the basic chapter of SpringBoot

1. A gray Blog

The above contents are only the words of one family. Due to limited personal ability, it is inevitable that there are omissions and mistakes. If you find a bug or have better suggestions, you are welcome to criticize and correct and be grateful

The following is a gray personal blog, which records all blog posts in study and work. Welcome to visit

  • A personal Blog https://blog.hhui.top
  • A gray blog spring special blog http://spring.hhui.top