spring PropertyPlaceholderConfigurer源码分析

1:写在前面

这篇文章中我们分析了BeanFactoryPostProcessor,知道了,其也是spring提供给我们的一个扩展点,这个是在外部配置生成BeanDefinition对象之后,在通过BeanDefinition创建spring bean之前,允许我们对BeanDefinition进行自己的定制化处理,本文要分析PropertyPlaceholderConfigure就是BeanFacotryPostProcessor的一个子类,利用其实现了占位符${变量名}替换的功能,这个功能是我们在工程上必用的一个功能,非常重要!!!
另外,关于PropertyPlaceholderConfigurer的使用例子,可以参考这里

2:类图

在这里插入图片描述

3:源码分析

首先执行到代码:

org.springframework.context.support.ClassPathXmlApplicationContext#ClassPathXmlApplicationContext(java.lang.String[], boolean, org.springframework.context.ApplicationContext)
public ClassPathXmlApplicationContext(
		String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
		throws BeansException {

	...snip...
	if (refresh) {
		refresh();
	}
}

继续看refresh();方法,源码如下:

org.springframework.context.support.AbstractApplicationContext#refresh
public void refresh() throws BeansException, IllegalStateException {
	synchronized (this.startupShutdownMonitor) {
		...snip...
		try {
			...snip...
			// <2021-04-07 15:13>
			invokeBeanFactoryPostProcessors(beanFactory);
			...snip...
		}

		catch (BeansException ex) {
			...snip...
		}

		finally {
			...snip...
		}
	}
}

其中<2021-04-07 15:13>处代码就是注册BeanFactoryPostProcessor,最终从这里会执行到如下代码:

org.springframework.context.support.PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors(java.util.Collection<? extends org.springframework.beans.factory.config.BeanFactoryPostProcessor>, org.springframework.beans.factory.config.ConfigurableListableBeanFactory)
private static void invokeBeanFactoryPostProcessors(
			Collection<? extends BeanFactoryPostProcessor> postProcessors, ConfigurableListableBeanFactory beanFactory) {
	// 循环遍历所有的BeanFactoryPostProcessor,调用postProcessBeanFactory方法
	for (BeanFactoryPostProcessor postProcessor : postProcessors) {
		// <2021-04-07 15:26>
		postProcessor.postProcessBeanFactory(beanFactory);
	}
}

我们就从2021-04-07 15:26>开始正式分析。

3.1:postProcessBeanFactory

该处调用的是PropertyPlaceholderConfigurer的父类PropertyResourceConfigurer,源码如下:

org.springframework.beans.factory.config.PropertyResourceConfigurer#postProcessBeanFactory
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
	try {
		// <2021-04-07 15:32>
		Properties mergedProps = mergeProperties();

		// <2021-04-07 15:40>
		convertProperties(mergedProps);

		// <2021-04-07 15:41>
		processProperties(beanFactory, mergedProps);
	}
	catch (IOException ex) {
		throw new BeanInitializationException("Could not load properties", ex);
	}
}

<2021-04-07 15:32>处是获取配置文件中配置的信息,可能如下图:
在这里插入图片描述
<2021-04-07 15:40>处最终还是将原始值,作为转换后的值,不知道到底有什么用。<2021-04-07 15:41>就是具体应用配置文件中的值到BeanDefinition中来替换占位符了,单起一部分来看。

3.2:processProperties

这里执行的方法所在的类就是我们本文要分析的PropertyPlaceHolderConfiguer了,源码如下:

org.springframework.beans.factory.config.PropertyPlaceholderConfigurer#processProperties
protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, Properties props)
			throws BeansException {
	// <2021-04-07 17:24>
	StringValueResolver valueResolver = new PlaceholderResolvingStringValueResolver(props);
	// <2021-04-07 17:31>
	doProcessProperties(beanFactoryToProcess, valueResolver);
}

<2021-04-07 17:24>使用该类完成${}占位符的替换工作,具体可以参考这里<2021-04-07 17:31>是执行具体的替换操作,详细参考3.3:doProcessProperties

3.3:doProcessProperties

源码如下:

org.springframework.beans.factory.config.PlaceholderConfigurerSupport#doProcessProperties
protected void doProcessProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
			StringValueResolver valueResolver) {
	// 通过字符串解析对象,创建bean定义的访问者对象BeanDefinitionVisitor
	BeanDefinitionVisitor visitor = new BeanDefinitionVisitor(valueResolver);
	// 获取所有的bean名称
	String[] beanNames = beanFactoryToProcess.getBeanDefinitionNames();
	for (String curName : beanNames) {
		// 注意此时为了能够处理BeanDefinition,配置的PropertyPlaceholderConfiguer已经初始化完毕了
		// 满足以下条件则为true
		// 1:当前bean名称不等于PropertyPlaceholderConfigurer的bean名称
		// 2:PropertyPlaceholderConfiguer所在的bean工厂和当前需要处理的bean工厂是同一个工厂容器
		if (!(curName.equals(this.beanName) && beanFactoryToProcess.equals(this.beanFactory))) {
			// 通过当前bean名称获取对应的db
			BeanDefinition bd = beanFactoryToProcess.getBeanDefinition(curName);
			try {
				// <2021-04-07 18:00>
				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);
}

<2021-04-07 18:00>处是核心代码,执行配置文件中占位符的替换操作,详细参考3.4:visitBeanDefinition

3.4:visitBeanDefinition

源码如下:

org.springframework.beans.factory.config.BeanDefinitionVisitor#visitBeanDefinition
public void visitBeanDefinition(BeanDefinition beanDefinition) {
	// 替换parent属性,如果有parent且存在${}占位符的话
	visitParentName(beanDefinition);
	// 替换class属性,如果有class且存在${}占位符的话
	visitBeanClassName(beanDefinition);
	// 替换facotry-bean属性,如果有factory-bean且存在${}占位符的话
	visitFactoryBeanName(beanDefinition);
	// 替换facotry-method属性,如果有factory-method且存在${}占位符的话
	visitFactoryMethodName(beanDefinition);
	// 替换scope属性,如果有scope且存在${}占位符的话
	visitScope(beanDefinition);
	// 如果是配置了<property>标签,这里在工程上会重点使用!!!
	if (beanDefinition.hasPropertyValues()) {
		// <2021-04-07 18:13>
		visitPropertyValues(beanDefinition.getPropertyValues());
	}
	// 如果配置了<constructor-arg>,进行相关占位符的替换,如果需要的话
	if (beanDefinition.hasConstructorArgumentValues()) {
		// 获取配置的构造函数参数值
		ConstructorArgumentValues cas = beanDefinition.getConstructorArgumentValues();
		// 处理通过index配置的构造函数参数
		visitIndexedArgumentValues(cas.getIndexedArgumentValues());
		// 处理通过name配置的构造函数参数
		visitGenericArgumentValues(cas.getGenericArgumentValues());
	}
}

<2021-04-07 18:13>处是处理属性的占位符,源码如下:

org.springframework.beans.factory.config.BeanDefinitionVisitor#visitPropertyValues
protected void visitPropertyValues(MutablePropertyValues pvs) {
	// 获取所有的pv
	PropertyValue[] pvArray = pvs.getPropertyValues();
	for (PropertyValue pv : pvArray) {
		// 解析原始值,替换占位符为实际值
		// <2021-04-07 18:28>
		Object newVal = resolveValue(pv.getValue());
		// 如果是处理后不相等,则说明替换了占位符,才处理
		if (!ObjectUtils.nullSafeEquals(newVal, pv.getValue())) {
			// 设置替换了占位符后的值
			pvs.add(pv.getName(), newVal);
		}
	}
}

<2021-04-07 18:28>处就是执行具体的替换,源码如下:

org.springframework.beans.factory.config.BeanDefinitionVisitor#resolveValue
protected Object resolveValue(@Nullable Object value) {
	...snip...
	else if (value instanceof String) {
		// <2021-04-07 18:31>
		return resolveStringValue((String) value);
	}
	return value;
}

<2021-04-07 18:31>处源码如下:

org.springframework.beans.factory.config.BeanDefinitionVisitor#resolveStringValue
protected String resolveStringValue(String strVal) {
	if (this.valueResolver == null) {
		throw new IllegalStateException("No StringValueResolver specified - pass a resolver " +
				"object into the constructor or override the 'resolveStringValue' method");
	}
	// <2021-04-07 18:33>
	String resolvedValue = this.valueResolver.resolveStringValue(strVal);
	// Return original String if not modified.
	return (strVal.equals(resolvedValue) ? strVal : resolvedValue);
}

重点看<2021-04-07 18:33>处代码,源码如下:

org.springframework.beans.factory.config.PropertyPlaceholderConfigurer.PlaceholderResolvingStringValueResolver#resolveStringValue
public String resolveStringValue(String strVal) throws BeansException {
	// <2021-04-07 18:34>
	String resolved = this.helper.replacePlaceholders(strVal, this.resolver);
	if (trimValues) {
		resolved = resolved.trim();
	}
	return (resolved.equals(nullValue) ? null : resolved);
}

继续看<2021-04-07 18:34>处代码,源码如下:

org.springframework.util.PropertyPlaceholderHelper#replacePlaceholders(java.lang.String, org.springframework.util.PropertyPlaceholderHelper.PlaceholderResolver)
public String replacePlaceholders(String value, PlaceholderResolver placeholderResolver) {
	Assert.notNull(value, "'value' must not be null");
	// <2021-04-07 18:35>
	return parseStringValue(value, placeholderResolver, null);
}

继续看<2021-04-07 18:35>处代码,这里就是真正执行替换的地方了,是核心,源码如下:

org.springframework.util.PropertyPlaceholderHelper#parseStringValue
protected String parseStringValue(
			String value, PlaceholderResolver placeholderResolver, @Nullable Set<String> visitedPlaceholders) {
	// this.placeholderPrefix这里为${,获取存在该前缀的第一个位置
	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) {
			// 获取${ 和 }中间的内容,如果是${my.name},则该值就是my.name
			String placeholder = result.substring(startIndex + this.placeholderPrefix.length(), endIndex);
			String originalPlaceholder = placeholder;
			// 初始化记录已经访问替换的占位符set集合
			if (visitedPlaceholders == null) {
				visitedPlaceholders = new HashSet<>(4);
			}
			// 如果是已经处理过,则抛出异常
			if (!visitedPlaceholders.add(originalPlaceholder)) {
				throw new IllegalArgumentException(
						"Circular placeholder reference '" + originalPlaceholder + "' in property definitions");
			}
			// 处理占位符中包含占位符的情况
			placeholder = parseStringValue(placeholder, placeholderResolver, visitedPlaceholders);
			// 从配置文件中获取占位符真正的值
			String propVal = placeholderResolver.resolvePlaceholder(placeholder);
			// 存在默认值的情况
			if (propVal == null && this.valueSeparator != null) {
				// 获取:的位置
				int separatorIndex = placeholder.indexOf(this.valueSeparator);
				// 如果存在:
				if (separatoIndex != -1) {
					// 获取真实的占位符,如${my.name:张三}这里就是my.name
					String actualPlaceholder = placeholder.substring(0, separatorIndex);
					// 获取默认值,如${my.name:张三}这里就是张三
					String defaultValue = placeholder.substring(separatorIndex + this.valueSeparator.length());
					// 获取真实占位符的值
					propVal = placeholderResolver.resolvePlaceholder(actualPlaceholder);
					// 如果是配置文件中没有,则使用默认值
					if (propVal == null) {
						propVal = defaultValue;
					}
				}
			}
			// 如果是获取到了占位符的值
			if (propVal != null) {
				propVal = parseStringValue(propVal, placeholderResolver, visitedPlaceholders);
				// 替换为真实值,如${my.name},如果propVal为张三的歌,则result变为张三的歌
				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();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值