spring学习(六)——3.1 Spring的 BeanFactoryPostProcessor

参考文章:

http://www.iocoder.cn/

Spring 容器启动阶段,Spring 提供了一种容器扩展机制:BeanFactoryPostProcessor,该机制作用于容器启动阶段,允许我们在容器实例化 Bean 之前对注册到该容器的 BeanDefinition 做出修改。

BeanFactoryPostProcessor

其类图

  • BeanFactoryPostProcessor为其顶层接口
  • PropertyResourceConfigurer 该类为属性资源的配置类
  • PropertyPlaceholderConfigurer 重写了processProperties方法
  • PropertyOverrideConfigurer 实现了对所有bean的属性的修改

本篇主要介绍接口和几个实现类相关的差异。后续介绍其应用

顶层接口

BeanFactoryPostProcessor是此类方法的顶层接口

public interface BeanFactoryPostProcessor {

	/**
	 * Modify the application context's internal bean factory after its standard
	 * initialization. 
         * 所有的 BeanDefinition 已经完成了加载即加载至 BeanFactory 中,但是还没有完成初始化
         * This allows for overriding or adding
	 * properties even to eager-initializing beans.
	 * @param beanFactory the bean factory used by the application context
	 * @throws org.springframework.beans.BeansException in case of errors
	 */
	void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;

}

其顶层接口只存在一个方法 postProcessBeanFactory

注意的是在 #postProcessBeanFactory(...) 方法中,千万不能进行 Bean 的实例化工作,因为这样会导致 Bean 过早实例化,会产生严重后果。期主要是与BeanDefinition进行打交道的。

PropertyResourceConfigurer

	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		try {
			// 获得外部数据
			Properties mergedProps = mergeProperties();

			// 进行属性的合并转换
			convertProperties(mergedProps);

			// 处理属性
			processProperties(beanFactory, mergedProps);
		}
		catch (IOException ex) {
			throw new BeanInitializationException("Could not load properties", ex);
		}
	}

起实现了基础的postProcessBeanFactory

PropertyResourceConfigurer 允许我们用 Properties 文件中的属性,来定义应用上下文(配置文件或者注解)

PropertyPlaceholderConfigurer

继承于PlaceholderConfigurerSupport 和 PropertyResourceConfigurer

和父类主要区别是,其实现了processProperties方法

	/**
	 * Visit each bean definition in the given bean factory and attempt to replace ${...} property
	 * placeholders with values from the given properties.
	 */
	@Override
	protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, Properties props)
			throws BeansException {

		StringValueResolver valueResolver = new PlaceholderResolvingStringValueResolver(props);
		doProcessProperties(beanFactoryToProcess, valueResolver);
	}

PropertyOverrideConfigurer

PropertyOverrideConfigurer 继承于 PropertyResourceConfigurer,其实现了另外一种processProperties的逻辑

@Override
	protected void processProperties(ConfigurableListableBeanFactory beanFactory, Properties props)
			throws BeansException {

		for (Enumeration<?> names = props.propertyNames(); names.hasMoreElements();) {
			String key = (String) names.nextElement();
			try {
				processKey(beanFactory, key, props.getProperty(key));
			}
			catch (BeansException ex) {
				String msg = "Could not process key '" + key + "' in PropertyOverrideConfigurer";
				if (!this.ignoreInvalidKeys) {
					throw new BeanInitializationException(msg, ex);
				}
				if (logger.isDebugEnabled()) {
					logger.debug(msg, ex);
				}
			}
		}
	}

所以我们可以发现最终PropertyPlaceholderConfigurer 和PropertyOverrideConfigurer  区别在processProperties上面

 

PropertyPlaceholderConfigurer .processProperties

其包含两个步骤

  1.         StringValueResolver valueResolver = new PlaceholderResolvingStringValueResolver(props);
  2.         doProcessProperties(beanFactoryToProcess, valueResolver);

两个步骤的意思

  1. StringValueResolver 为一个解析 String 类型值的策略接口,用来解析String,是String的解析策略
  2. 得到 String 解析器的实例 valueResolver 后使用doProcessProperties进行最终的值操作

执行数据解析的内容

	protected void doProcessProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
			StringValueResolver valueResolver) {
		// 获得BeanDefinition的访问器
		BeanDefinitionVisitor visitor = new BeanDefinitionVisitor(valueResolver);
		// 从beanFactory中获取需要被处理的类名称
		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
				BeanDefinition bd = beanFactoryToProcess.getBeanDefinition(curName);
				try {
					
					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);
	}

然后检验bean的所有属性

public void visitBeanDefinition(BeanDefinition beanDefinition) {
		// 遍历给定的BeanDefinition对象和可变属性值
		// parent 、class 、factory-bean 、factory-method 、scope 、property 、constructor-arg 。
		visitParentName(beanDefinition);
		visitBeanClassName(beanDefinition);
		visitFactoryBeanName(beanDefinition);
		visitFactoryMethodName(beanDefinition);
		visitScope(beanDefinition);
		if (beanDefinition.hasPropertyValues()) {
			visitPropertyValues(beanDefinition.getPropertyValues());
		}
		if (beanDefinition.hasConstructorArgumentValues()) {
			ConstructorArgumentValues cas = beanDefinition.getConstructorArgumentValues();
			visitIndexedArgumentValues(cas.getIndexedArgumentValues());
			visitGenericArgumentValues(cas.getGenericArgumentValues());
		}
	}

遍历解析值

protected void visitPropertyValues(MutablePropertyValues pvs) {
		PropertyValue[] pvArray = pvs.getPropertyValues();
		for (PropertyValue pv : pvArray) {
			Object newVal = resolveValue(pv.getValue());
			// 判断真值
			if (!ObjectUtils.nullSafeEquals(newVal, pv.getValue())) {
				// 设置到 PropertyValue 中
				pvs.add(pv.getName(), newVal);
			}
		}
	}

然后在resolveValue方法中,使用

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");
		}
		// 是我们在构造 BeanDefinitionVisitor 实例时传入的 String 类型解析器
		// 解析最终值
		String resolvedValue = this.valueResolver.resolveStringValue(strVal);
		// Return original String if not modified.
		return (strVal.equals(resolvedValue) ? strVal : resolvedValue);
	}

其解析器就是哦们构造时候传入的String解析器

然后我们使用PropertyPlaceholderConfigurer.java中的解析方法

		public String resolveStringValue(String strVal) throws BeansException {
			// 解析最终值
			// helper 为 PropertyPlaceholderHelper 实例对象,
			// 而 PropertyPlaceholderHelper 则是处理应用程序中包含占位符的字符串工具类
			String resolved = this.helper.replacePlaceholders(strVal, this.resolver);
			// 移除多余空格
			if (trimValues) {
				resolved = resolved.trim();
			}
			// 返回最终值
			return (resolved.equals(nullValue) ? null : resolved);
		}

其核心替换方法在replacePlaceholders中的paresStringValue

	protected String parseStringValue(
			String value, PlaceholderResolver placeholderResolver, Set<String> visitedPlaceholders) {

		StringBuilder result = new StringBuilder(value);
		// 获取前缀 "${" 的索引位置
		int startIndex = value.indexOf(this.placeholderPrefix);
		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.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...
				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);
						// 获取 : 后面部分 defaultValue
						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();
	}

 

 

PropertyOverrideConfigurer.processProperties

 

	protected void processProperties(ConfigurableListableBeanFactory beanFactory, Properties props)
			throws BeansException {

		// 遍历数据集合
		for (Enumeration<?> names = props.propertyNames(); names.hasMoreElements();) {
			String key = (String) names.nextElement();
			try {
				processKey(beanFactory, key, props.getProperty(key));
			}
			catch (BeansException ex) {
				String msg = "Could not process key '" + key + "' in PropertyOverrideConfigurer";
				if (!this.ignoreInvalidKeys) {
					throw new BeanInitializationException(msg, ex);
				}
				if (logger.isDebugEnabled()) {
					logger.debug(msg, ex);
				}
			}
		}
	}

循环遍历参数的时候进行赋值的主要处理逻辑在processKey中

	protected void processKey(ConfigurableListableBeanFactory factory, String key, String value)
			throws BeansException {
		// 判断是否存在.的索引位置。this.beanNameSeparator 值为.
		int separatorIndex = key.indexOf(this.beanNameSeparator);
		if (separatorIndex == -1) {
			throw new BeanInitializationException("Invalid key '" + key +
					"': expected 'beanName" + this.beanNameSeparator + "property'");
		}
		// 截取第一个.获得类名称
		String beanName = key.substring(0, separatorIndex);
		// 获得类的属性名称
		String beanProperty = key.substring(separatorIndex + 1);
		// 将类名添加至类名集合中
		this.beanNames.add(beanName);
		// 设置属性值
		applyPropertyValue(factory, beanName, beanProperty, value);
		if (logger.isDebugEnabled()) {
			logger.debug("Property '" + key + "' set to value [" + value + "]");
		}
	}

通过解析,获得bean类的信息,然后根据工厂,属性名,值和类在applyPropertyValue进行值替换

protected void applyPropertyValue(
			ConfigurableListableBeanFactory factory, String beanName, String property, String value) {
		// 根据类名获得BeanDefinition
		BeanDefinition bd = factory.getBeanDefinition(beanName);
		BeanDefinition bdToUse = bd;
		// 循环拿到BeanDefinition的原型
		while (bd != null) {
			bdToUse = bd;
			bd = bd.getOriginatingBeanDefinition();
		}
		// 拿到值对象
		PropertyValue pv = new PropertyValue(property, value);
		// 设置 PropertyValue 到 BeanDefinition 中
		pv.setOptional(this.ignoreInvalidKeys);
		bdToUse.getPropertyValues().addPropertyValue(pv);
	}

此方法最终赋值在addPropertValue中,其中getOriginatingBeanDefinition是获取此beanDefinition的原型,然后在原型上进行操作

赋值操作就是简单的查询,合并,覆盖

public MutablePropertyValues addPropertyValue(PropertyValue pv) {
		for (int i = 0; i < this.propertyValueList.size(); i++) {
			PropertyValue currentPv = this.propertyValueList.get(i);
			// 找到属性名称
			if (currentPv.getName().equals(pv.getName())) {
				// 合并值
				pv = mergeIfRequired(pv, currentPv);
				// 放入值
				setPropertyValueAt(pv, i);
				return this;
			}
		}
		// 未匹配到,添加到 propertyValueList 中
		this.propertyValueList.add(pv);
		return this;
	}

总结

  • PropertyPlaceholderConfigurer:属性点位符配置器。该容器后处理器负责读取Properties属性文件里的属性值
  • PropertyOverrideConfigurer:类似于PropertyPlaceholderConfigurer,但是与后者相比,前者对于bean属性可以有缺省值或者根本没有值。如果起覆盖作用的 Properties文件没有某个bean属性的内容,那么缺省的上下文定义将被使用。
  • PropertyOverrideConfigurer:可以覆盖之前定义的配置,假如有多个PropertyOverrideConfigurer配置,则最后一个PropertyOverrideConfigurer取胜

后续会使用几个demo来体现两者的不同之处

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大·风

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值