Custom configuration source usage posture introduction

The previous blog introduces some knowledge points about @value, which mentioned one point: can the corresponding configuration of @value be obtained from other data sources besides configuration files, such as Redis, DB and HTTP?

SpringCloud Config allows remote configuration and dynamic configuration refreshes. How to configure a custom data source in SpringBoot

I. Project environment

1. Project dependencies

This project is developed by SpringBoot 2.2.1.RELEASE + Maven 3.5.3 + IDEA

Open a Web service for testing

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

II. Customize the configuration source

The @Value modifier reads the configuration from Envrionment when binding the configuration, so all we need to do is register a custom configuration source with MapPropertySource to implement the scenario

1. Customize data sources

To demonstrate the simplest custom configuration data source, rewrite the getProperties method of MapPropertySource

To achieve the following

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

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

    /** * override this method, suitable for getting configuration ** in real time@param name
     * @return* /
    @Override
    public Object getProperty(String name) {
        // Note that this logic is executed only for custom - starting configurations
        if (name.startsWith("selfdefine.")) {
            return name + "_" + UUID.randomUUID();
        }
        return super.getProperty(name); }}Copy the code

2. Data source registration

Just declare the configuration source, and then register it with the Environment so it can be made available

@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 the configuration
    @GetMapping(path = "get")
    public String getProperty(String key) {
        return environment.getProperty(key);
    }

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

As you can see from the above output, the custom configuration starts with a random configuration value. If the parameter does not start with selfdefine, no corresponding configuration is displayed

3. Custom configuration sources based on files

This may be a bit of a joke, but let’s put the configuration source in a custom file and support file configuration modification

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

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

    // This method is suitable for retrieving all configurations at once and then querying the corresponding configurations from memory to improve service performance
    // update every 10s
    @PostConstruct
    @Scheduled(fixedRate = 10_000)
    public void refreshSource(a) 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("\n")) {
            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); }}Copy the code

A timer can refresh the configuration information in memory every 10 seconds. Of course, here can also configure a file change listener, relevant interested words, can look at the Java implementation of file change monitor can play

Corresponding configuration file

user=xhh
name=A gray
age=18
Copy the code

The registered pose is the same as above, so it will not be explained separately

As you can see above, the configuration changes in the file are refreshed after a period of time

4. @ValueBind custom configurations

Let’s see if binding @value to a custom configuration works

Adjust the Application above by adding a member attribute

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

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

Test again found throw exception, say this configuration does not exist!!

(this is beyond the mark, saw along while, the result tells me not line, this also can’t hurriedly make a bad comment yao 😡😡😡)

That’s it, of course I have to keep trying to fix it. Why is it possible to get the configuration directly from Environment, but not from @value binding?

The “culprit” is the initialization order, my custom configuration source, before Envrionment has been jammed, you are meeting to start binding, as if to give a bad review to “a gloomy blog”, only to find that you haven’t paid attention to… (Ok, I admit you can comment on 😭)

Based on previous knowledge points (which ones are too short to mention, check out the following selected posts)

  • How to specify the bean to be loaded first
  • Postures for specifying the initialization order of beans in the SpringBoot series
  • The SpringBoot tutorial uses postures to refute the myth of incorrect Bean loading order

One of the simplest ways to solve this problem is as follows

Create a separate configuration class to register custom data sources

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

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); }}Copy the code

Again, the results are as follows

As you can see from the demo GIF above, there is no problem binding the custom data source configuration, but when the configuration changes, the bound name field is not updated with it

If I want to refresh dynamically, what should I do?

  • Don’t worry, the new blog has been arranged, the next one is provided (afraid of getting lost partners, might as well pay attention to “a grey blog” 🐺)

5. Summary

Finally according to the convention summary, although the length of this article is long, but the knowledge point is more concentrated, summed up, two sentences

  • Through inheritanceMapPropertySourceTo implement a custom configuration source, registered toEnvrionmentAvailable for@Valueuse
  • use@ValueWhen binding custom configuration sources, note that the order of registration precedes the initialization of beans

Good, to the end of the text here, I am a gray, welcome you big guy to step on the long grass of the public number “a gray blog”

III. Can’t miss the source code and related knowledge points

0. Project

  • Project: github.com/liuyueyi/sp…
  • Source: github.com/liuyueyi/sp…

Configure series of blog posts

  • What do you not know about @value
  • ConfigurationProperties: Things you didn’t know about the configuration bindings
  • PropertySource Shows how to load a Yaml configuration file
  • Implementing a custom configuration loader
  • Configuration refresh of SpringBoot Basic configuration information
  • Custom configuration specification and reference in configuration of SpringBoot Basic configuration information
  • SpringBoot Basic configuration information Environment configuration information
  • SpringBoot Basic configuration information how to read configuration information

1. An ashy Blog

As far as the letter is not as good, the above content is purely one’s opinion, due to the limited personal ability, it is inevitable that there are omissions and mistakes, if you find bugs or have better suggestions, welcome criticism and correction, don’t hesitate to appreciate

Below a gray personal blog, record all the study and work of the blog, welcome everyone to go to stroll

  • A grey Blog Personal Blog blog.hhui.top
  • A Grey Blog-Spring feature Blog Spring.hhui.top