SpringBoot配置文件解析过程

SpringBoot配置文件解析过程

  • 一、自动配置类 PropertyPlaceholderAutoConfiguration
  • 二、PropertySourcesPlaceholderConfigurer
    • 2.1 PropertySourcesPlaceholderConfigurer.processProperties()
    • 2.2 StringValueResolver.resolveStringValue(String strVal)
    • 2.3 propertyResolver.resolveRequiredPlaceholders(strVal)
  • 三、PropertySource
  • 四、Environment
    • 4.1 Environment 对象的初始化
    • 4.2 SpringApplication.prepareEnvironment()

一、自动配置类 PropertyPlaceholderAutoConfiguration

SpringBoot 配置文件解析是通过其自动装配原理实现的。关于自动装配原理可以参考SpringBoot 核心原理。

Springboot 中配置文件解析的自动配置类为 PropertyPlaceholderAutoConfiguration

@Configuration(proxyBeanMethods = false)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
public class PropertyPlaceholderAutoConfiguration {

	@Bean
	@ConditionalOnMissingBean(search = SearchStrategy.CURRENT)
	public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
		return new PropertySourcesPlaceholderConfigurer();
	}
}

二、PropertySourcesPlaceholderConfigurer

SpringBoot 通过自动装配原理装载了一个 PropertySourcesPlaceholderConfigurer 类,该类实现了 Spring 中的BeanFactoryPostProcessor 接口的,Spring 在容器启动的时候会调用其中的 postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) 方法,完成一些初始化动作。

public class PropertySourcesPlaceholderConfigurer extends PlaceholderConfigurerSupport implements EnvironmentAware {
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        if (this.propertySources == null) {
            this.propertySources = new MutablePropertySources();
            if (this.environment != null) {
            	// 将 Environment 对象封装成 PropertySource对象,并添加到 MutablePropertySources 的list中
                this.propertySources.addLast(new PropertySource<Environment>("environmentProperties", this.environment) {
                    @Nullable
                    public String getProperty(String key) {// 获取Environment中的值的时候调用
                        return ((Environment)this.source).getProperty(key);
                    }
                });
            }

            try {
            	// 加载本地配置文件,包装成 PropertySource 对象
                PropertySource<?> localPropertySource = new PropertiesPropertySource("localProperties", this.mergeProperties());
                // 添加到 MutablePropertySources 的list中
                if (this.localOverride) {// 是否覆盖本地配置文件:如果key相同,队尾覆盖队头
                    this.propertySources.addFirst(localPropertySource);// 队头
                } else {
                    this.propertySources.addLast(localPropertySource);// 队尾
                }
            } catch (IOException var3) {
                throw new BeanInitializationException("Could not load properties", var3);
            }
        }

		// 解析属性:将Spring IoC容器中@Value标注的属性进行解析赋值
        this.processProperties(beanFactory, (ConfigurablePropertyResolver)(new PropertySourcesPropertyResolver(this.propertySources)));
        this.appliedPropertySources = this.propertySources;
    }
}

2.1 PropertySourcesPlaceholderConfigurer.processProperties()

PropertySourcesPlaceholderConfigurer 中的 processProperties() 方法主要进行了属性的解析,将形如@Value("spring.appliation.name:finpc") 注解标注的属性进行赋值。主要代码如下。

public class PropertySourcesPlaceholderConfigurer extends PlaceholderConfigurerSupport implements EnvironmentAware {
	/**
	 * 属性解析
	 */
	protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, final ConfigurablePropertyResolver propertyResolver) throws BeansException {
        propertyResolver.setPlaceholderPrefix(this.placeholderPrefix);// 设置前缀 ${
        propertyResolver.setPlaceholderSuffix(this.placeholderSuffix);// 设置后缀 }
        propertyResolver.setValueSeparator(this.valueSeparator);// 设置分隔符

		// 创建 String 类型的值解析器:在进行 赋值的时候,会调用其中的resolveStringValue(String strVal);方法
        StringValueResolver valueResolver = (strVal) -> {
            String resolved = this.ignoreUnresolvablePlaceholders ? propertyResolver.resolvePlaceholders(strVal) : propertyResolver.resolveRequiredPlaceholders(strVal);
            if (this.trimValues) {
                resolved = resolved.trim();
            }

            return resolved.equals(this.nullValue) ? null : resolved;
        };
		// 解析 @Value("spring.appliation.name:finpc") 标注的属性值
        this.doProcessProperties(beanFactoryToProcess, valueResolver);
    }
	
	/**
	 * 解析 @Value("spring.appliation.name:finpc") 标注的属性值
	 */
	protected void doProcessProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
			StringValueResolver valueResolver) {
		// 包装 valueResolver
		BeanDefinitionVisitor visitor = new BeanDefinitionVisitor(valueResolver);
		
		// 获取容器中所有的 beanName
		String[] beanNames = beanFactoryToProcess.getBeanDefinitionNames();
		for (String curName : beanNames) {// 循环遍历
			// Check that we're not parsing our own bean definition,
			// to avoid failing on unresolvable placeholders in properties file locations.
			if (!(curName.equals(this.beanName) && beanFactoryToProcess.equals(this.beanFactory))) {// 过滤排出
				BeanDefinition bd = beanFactoryToProcess.getBeanDefinition(curName);
				try {
					// 解析beanDefinition中属性的值,例如:${xx.xx}解析成真正的配置属性值
					// 该方法核心逻辑会调用上面创建的 StringValueResolver 对象的resolveStringValue(String strVal)方法
					visitor.visitBeanDefinition(bd);
				}
				catch (Exception ex) {
					throw new BeanDefinitionStoreException(bd.getResourceDescription(), curName, ex.getMessage(), ex);
				}
			}
		}

		// New in Spring 2.5: resolve placeholders in alias target names and aliases as well.
		beanFactoryToProcess.resolveAliases(valueResolver);

		// New in Spring 3.0: resolve placeholders in embedded values such as annotation attributes.
		beanFactoryToProcess.addEmbeddedValueResolver(valueResolver);
	}
}

2.2 StringValueResolver.resolveStringValue(String strVal)

StringValueResolver 对象为String 类型的值解析器,是解析@Value注解的核心对象。

StringValueResolver valueResolver = (strVal) -> {
		// ignoreUnresolvablePlaceholders:是否忽略不可解析的占位符,默认false
        String resolved = this.ignoreUnresolvablePlaceholders ? propertyResolver.resolvePlaceholders(strVal) : propertyResolver.resolveRequiredPlaceholders(strVal);
        if (this.trimValues) {
            resolved = resolved.trim();
        }

        return resolved.equals(this.nullValue) ? null : resolved;
    };

2.3 propertyResolver.resolveRequiredPlaceholders(strVal)

通过 AbstractPropertyResolver 类的 resolveRequiredPlaceholders() 方法,解析 strVal。

public abstract class AbstractPropertyResolver implements ConfigurablePropertyResolver {
	@Override
	public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {
		if (this.strictHelper == null) {
			this.strictHelper = createPlaceholderHelper(false);
		}
		return doResolvePlaceholders(text, this.strictHelper);
	}
	
	private String doResolvePlaceholders(String text, PropertyPlaceholderHelper helper) {
		return helper.replacePlaceholders(text, this::getPropertyAsRawString);
	}
}

最终会调用 PropertyPlaceholderHelper 的 replacePlaceholders(String value, PlaceholderResolver placeholderResolver) 方法,完成解析。

public class PropertyPlaceholderHelper {
	public String replacePlaceholders(String value, PlaceholderResolver placeholderResolver) {
		Assert.notNull(value, "'value' must not be null");
		return parseStringValue(value, placeholderResolver, null);
	}
	
	protected String parseStringValue(
			String value, PlaceholderResolver placeholderResolver, @Nullable Set<String> visitedPlaceholders) {
		// 是否以前缀开始 ${
		int startIndex = value.indexOf(this.placeholderPrefix);
		if (startIndex == -1) {
			return value;
		}

		StringBuilder result = new StringBuilder(value);
		while (startIndex != -1) {
			// 获取最后一个后缀 } 的位置
			int endIndex = findPlaceholderEndIndex(result, startIndex);
			if (endIndex != -1) {
				// 截取 ${ 到 } 中间的字符串
				String placeholder = result.substring(startIndex + this.placeholderPrefix.length(), endIndex);
				String originalPlaceholder = placeholder;
				if (visitedPlaceholders == null) {
					visitedPlaceholders = new HashSet<>(4);
				}
				if (!visitedPlaceholders.add(originalPlaceholder)) {
					throw new IllegalArgumentException(
							"Circular placeholder reference '" + originalPlaceholder + "' in property definitions");
				}
				// Recursive invocation, parsing placeholders contained in the placeholder key.
				// 递归调用,解析 ${${}} 类型的参数
				placeholder = parseStringValue(placeholder, placeholderResolver, visitedPlaceholders);
				
				// Now obtain the value for the fully resolved key...
				// 获取解析的值,调用 ConfigurationPropertySourcesPropertyResolver 中的 getPropertyAsRawString(String key) 方法
				// 调用 propertySources 中的getProperty(String key)方法,即调用 environment 对象的getProperty(key)方法
				String propVal = placeholderResolver.resolvePlaceholder(placeholder);
				
				if (propVal == null && this.valueSeparator != null) {// 如果为空,取默认值
					int separatorIndex = placeholder.indexOf(this.valueSeparator);
					if (separatorIndex != -1) {
						String actualPlaceholder = placeholder.substring(0, separatorIndex);
						// 获取 ":"号后面的默认值
						String defaultValue = placeholder.substring(separatorIndex + this.valueSeparator.length());
						// ":"前面的参数解析
						propVal = placeholderResolver.resolvePlaceholder(actualPlaceholder);
						// 如果解析不到,则用默认值
						if (propVal == null) {
							propVal = defaultValue;
						}
					}
				}
				if (propVal != null) {
					// Recursive invocation, parsing placeholders contained in the
					// previously resolved placeholder value.
					// 递归调用,解析 ${${}} 类型的参数
					propVal = parseStringValue(propVal, placeholderResolver, visitedPlaceholders);
					result.replace(startIndex, endIndex + this.placeholderSuffix.length(), propVal);
					if (logger.isTraceEnabled()) {
						logger.trace("Resolved placeholder '" + placeholder + "'");
					}
					startIndex = result.indexOf(this.placeholderPrefix, startIndex + propVal.length());
				}
				else if (this.ignoreUnresolvablePlaceholders) {
					// Proceed with unprocessed value.
					startIndex = result.indexOf(this.placeholderPrefix, endIndex + this.placeholderSuffix.length());
				}
				else {
					throw new IllegalArgumentException("Could not resolve placeholder '" +
							placeholder + "'" + " in value \"" + value + "\"");
				}
				visitedPlaceholders.remove(originalPlaceholder);
			}
			else {
				startIndex = -1;
			}
		}
		return result.toString();
	}
}

可以看到 PropertySourcesPlaceholderConfigurer 在启动过程中,主要干了三件事情:

  1. 将 Environment 对象封装成 PropertySource对象,并添加到 MutablePropertySources 的list中。
  2. 加载本地配置文件,包装成 PropertySource 对象,并添加到 MutablePropertySources 的list中。
  3. 解析属性:将Spring IoC容器中@Value标注的属性进行解析赋值。

其中,有两个很重要的对象 PropertySourceEnvironment ,SpringBoot 初始化 IoC 容器中的属性时,会调用 PropertySource 的 getProperty(String key) 方法,最终会直接调用 Environment 的 getProperty(key) 方法。

三、PropertySource

PropertySource 可以理解为一种配置属性的来源,比如一个 application.properties 配置内容就会存储到一个 PropertySource 对象中。所有的配置属性源都会封装为一个 PropertySource 对象,Spring启动初始化,通过该对象的 getProperty() 方法获取 @Value 中key 的属性值。

PropertySource 为抽象类,主要属性及抽象方法如下。

public abstract class PropertySource<T> {

	protected final Log logger = LogFactory.getLog(getClass());

	protected final String name;

	protected final T source;

	@Nullable
	public abstract Object getProperty(String name);
}
  • name 是标识属性来源的名称,根据name可以找到一个 PropertySource 对象。
  • source 是用来存储属性 key 和 value 的结构,比如 source 可以是一个 map,可以是一个properties。
  • getProperty(String name) 方法:根据key获取到这个key对应的属性值。

在上面的 PropertySourcesPlaceholderConfigurer 中,将一个 environment 对象封装为 PropertySource 对象,在获取值的时候,就会调用 environment 对象的 getProperty() 方法。

四、Environment

4.1 Environment 对象的初始化

在 SpringBoot 启动 main 方法中,会执行 SpringApplication 中的 run() 方法,其中,会创建并初始化一个 Environment 对象,具体代码如下。

public class SpringApplication {
	public ConfigurableApplicationContext run(String... args) {
		// 创建一个 StopWatch 对象,记录run()启动时长
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        // 创建bootstrap上下文
        DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();
        ConfigurableApplicationContext context = null;
        this.configureHeadlessProperty();
        // 从类路径下的 META-INF/spring.factories文件中获取所有对应SpringApplicationRunListener的全路径数组
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        // 启动SpringApplicationRunListener
        listeners.starting(bootstrapContext, this.mainApplicationClass);

        try {
        	// 加载application.properties和外部的属性配置
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            /*************根据监听器和默认的参数,准备spring环境***************/
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);
            this.configureIgnoreBeanInfo(environment);
            // 打印Banner,可以自定义启动logo(在resources路径下创建一个banner.txt文件,将你想打印的图标放入其中)
            Banner printedBanner = this.printBanner(environment);
            // 创建 ApplicationContext 容器,根据WebApplicationType 类型决定创建容器类型
            // WebApplicationType:NONE,不启动内嵌的WebServer,不是运行web application
            // WebApplicationType:SERVLET,启动内嵌的基于servlet的web server
            // WebApplicationType:REACTIVE,启动内嵌的reactive web server,这个application是一个reactive web application
            context = this.createApplicationContext();
            context.setApplicationStartup(this.applicationStartup);
            // 准备应用上下文,在refresh前加载并执行所有的ConfigurableApplicationContext的initialize 方法。
            this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
            // 刷新容器,初始化ioc容器,向容器中加入配置类、组件,并且可以出发自动配置功能
            this.refreshContext(context);
            // 执行Spring容器初始化的后置处理
            this.afterRefresh(context, applicationArguments);
            stopWatch.stop();
            if (this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
            }

            listeners.started(context);
            // 执行callRunners, 支持自定义run方法
            this.callRunners(context, applicationArguments);
        } catch (Throwable var10) {
            this.handleRunFailure(context, var10, listeners);
            throw new IllegalStateException(var10);
        }

        try {
            listeners.running(context);
            return context;
        } catch (Throwable var9) {
            this.handleRunFailure(context, var9, (SpringApplicationRunListeners)null);
            throw new IllegalStateException(var9);
        }
    }
}

4.2 SpringApplication.prepareEnvironment()

SpringBoot 在启动过程中,会通过 prepareEnvironment() 方法创建并初始化一个 Environment 对象。

public class SpringApplication {

	private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
			DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
		// Create and configure the environment
		// 创建 Environment
		ConfigurableEnvironment environment = getOrCreateEnvironment();
		// 配置 Environment
		configureEnvironment(environment, applicationArguments.getSourceArgs());
		ConfigurationPropertySources.attach(environment);
		// 触发监听器:通过 EventPublishingRunListener 监听器广播一个 ApplicationEnvironmentPreparedEvent 事件
		// ConfigFileApplicationListener 监听到这个事件后,会加载 classpath:/,classpath:/config/,file:./,file:./config/*/,file:./config/ 配置文件)
		listeners.environmentPrepared(bootstrapContext, environment);
		DefaultPropertiesPropertySource.moveToEnd(environment);
		Assert.state(!environment.containsProperty("spring.main.environment-prefix"),
				"Environment prefix cannot be set via properties.");
		bindToSpringApplication(environment);
		if (!this.isCustomEnvironment) {
			environment = convertEnvironment(environment);
		}
		ConfigurationPropertySources.attach(environment);
		return environment;
	}

	/**
	 * 创建 Environment
	 */
	private ConfigurableEnvironment getOrCreateEnvironment() {
		if (this.environment != null) {
			return this.environment;
		}
		switch (this.webApplicationType) {// 根据当前应用类型创建,当前应用为 SERVLET 类型
		case SERVLET:
			// 通过构造函数加载所有环境变量
			return new ApplicationServletEnvironment();
		case REACTIVE:
			return new ApplicationReactiveWebEnvironment();
		default:
			return new ApplicationEnvironment();
		}
	}
}

可以看到,通过上面的分析,Environment 对象初始化完成,具体结构如下。
SpringBoot配置文件解析过程_第1张图片

你可能感兴趣的:(微服务与分布式,spring,boot,java,spring)