SpringBoot source code analysis-easy configuration file reading

Project address
springboot_01

We should have seen the
load(), getPropertySources(), and addLast()
methods in the previous article . Let's write a simple configuration file reading program.

Let's take a look at the last load() method we saw before.

private void load(PropertySourceLoader loader, String location, Profile profile, DocumentFilter filter,
				DocumentConsumer consumer) {
    
    
	Resource[] resources = getResources(location);
	for (Resource resource : resources) {
    
    
		try {
    
    
			if (resource == null || !resource.exists()) {
    
    
				if (this.logger.isTraceEnabled()) {
    
    
					StringBuilder description = getDescription("Skipped missing config ", location, resource,
							profile);
					this.logger.trace(description);
				}
				continue;
			}
			if (!StringUtils.hasText(StringUtils.getFilenameExtension(resource.getFilename()))) {
    
    
				if (this.logger.isTraceEnabled()) {
    
    
					StringBuilder description = getDescription("Skipped empty config extension ", location,
							resource, profile);
					this.logger.trace(description);
				}
				continue;
			}
			String name = "applicationConfig: [" + getLocationName(location, resource) + "]";
			List<Document> documents = loadDocuments(loader, name, resource);
			if (CollectionUtils.isEmpty(documents)) {
    
    
				if (this.logger.isTraceEnabled()) {
    
    
					StringBuilder description = getDescription("Skipped unloaded config ", location, resource,
							profile);
					this.logger.trace(description);
				}
				continue;
			}
			List<Document> loaded = new ArrayList<>();
			for (Document document : documents) {
    
    
				if (filter.match(document)) {
    
    
					addActiveProfiles(document.getActiveProfiles());
					addIncludedProfiles(document.getIncludeProfiles());
					loaded.add(document);
				}
			}
			Collections.reverse(loaded);
			if (!loaded.isEmpty()) {
    
    
				loaded.forEach((document) -> consumer.accept(profile, document));
				if (this.logger.isDebugEnabled()) {
    
    
					StringBuilder description = getDescription("Loaded config file ", location, resource,
							profile);
					this.logger.debug(description);
				}
			}
		}
		catch (Exception ex) {
    
    
			StringBuilder description = getDescription("Failed to load property source from ", location,
					resource, profile);
			throw new IllegalStateException(description.toString(), ex);
		}
	}
}

Here we mentioned that List<Document> documents = loadDocuments(loader, name, resource);this line of code is to read the configuration file and see how it is implemented.
Insert picture description here
You can see from the debug information that the value of documents is empty. Our configuration file is a key-value pair, so it may be the data obtained in the part circled above.
Insert picture description here
By looking at the debug console, it is indeed in this position.
So let's check this method.
Insert picture description here
These two classes are very familiar, but enter different classes for different configuration file suffixes. You may understand all of them when you
Insert picture description hereInsert picture description here
Insert picture description here
enter this.process(callback, yaml, resource);
Insert picture description here
here. new UnicodeReader(resource.getInputStream());Read file data.
Back again

private List<Document> loadDocuments(PropertySourceLoader loader, String name, Resource resource)
				throws IOException {
    
    
	DocumentsCacheKey cacheKey = new DocumentsCacheKey(loader, resource);
	List<Document> documents = this.loadDocumentsCache.get(cacheKey);
	if (documents == null) {
    
    
		List<PropertySource<?>> loaded = loader.load(name, resource);
		documents = asDocuments(loaded);
		this.loadDocumentsCache.put(cacheKey, documents);
	}
	return documents;
}

In this methoddocuments = asDocuments(loaded);
Insert picture description here

Then the basic steps are understood.

  1. First read the configuration file information.
  2. Packaging properties
  3. getPropertySources()
  4. addLast()

Okay, now we start to code

Create a my.properties file.

name=龙小虬

As we mentioned before,
Insert picture description here
we scanned the spring.factories file org.springframework.boot.SpringApplicationRunListener, so we also create a spring.factories file. Configuration information
Insert picture description here

org.springframework.boot.SpringApplicationRunListener=\
com.lxq.listener.MySpringApplicationRunListeners

And create MySpringApplicationRunListeners.java

package com.lxq.listener;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringApplicationRunListener;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.env.PropertySource;

import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Properties;

/**
 * @author 龙小虬
 * @date 2021/3/24 14:57
 */
public class MySpringApplicationRunListeners implements SpringApplicationRunListener {
    
    

    @Override
    public void starting() {
    
    
        System.out.println(">>>>>starting<<<<<");
    }

    @Override
    public void environmentPrepared(ConfigurableEnvironment environment) {
    
    
        // 先读取配置文件到程序中,然后放入SpringBoot中
        Properties properties = new Properties();
        try {
    
    
            // 1. 读取我们的my.properties文件,配置编码,防止中文乱码
            properties.load(new InputStreamReader(this.getClass().getClassLoader().
                    getResourceAsStream("my.properties"), "GB2312"));
            // 2. 读取名称名称为my
            PropertySource propertySource = new PropertiesPropertySource("my", properties);
            //3. 将资源添加到项目中
            MutablePropertySources propertySources = environment.getPropertySources();
            //4. 通过api接口可以将配置文件读取 到SpringBoot项目中
            propertySources.addLast(propertySource);
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }
    }
}

Create MyController.java

package com.lxq.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author 龙小虬
 * @date 2021/3/24 14:53
 */
@RestController
public class MyController {
    
    

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

    @RequestMapping(value = "/test",produces = "text/plain;charset=UTF-8")
    public String test(){
    
    
        return "手动注入my.properties"+name;
    }
}

Run the code. Report an error directly
Insert picture description here

getDeclaredConstructorObviously the reflection execution is wrong.
So let's see where the imitation is wrong.
Insert picture description here
Find this class.
Insert picture description hereYou can see that this class is constructed with parameters, while we are constructed without parameters. So we also add one. In fact, it can be seen in another place. We mentioned that the starting() method will be called back. So we look at him.
Insert picture description here
Then see that the listener.starting() method
Insert picture description herehas parameter construction here, so we really have a problem with this step.
We add a parameter structure in MySpringApplicationRunListeners.java

private final SpringApplication application;

private final String[] args;

public MySpringApplicationRunListeners(SpringApplication application, String[] args) {
    
    
    this.application = application;
    this.args = args;
}

Run and visithttp://localhost:8090/test
Insert picture description here

Guess you like

Origin blog.csdn.net/weixin_43911969/article/details/115193185