Spring源码之@Value属性赋值

1,064 阅读3分钟

喜欢Spring源码的朋友,点点关注,您的支持是我前进的动力。

对@value属性赋值是发生在bean属性填充阶段,也就是populateBean方法。

下面是相关代码的时序图:

详解@value属性是如何赋值的

具体代码定位到
DefaultListableBeanFactory类的doResolveDependency方法:

@Nullable
	public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
			@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {

		InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
		try {
      
       Class<?> type = descriptor.getDependencyType();
      //获得@Value注解的value值
       Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
       if (value != null) {
	  if (value instanceof String) {
        //解析value值,并且从MutablePropertySources中获取属性值
	String strVal = resolveEmbeddedValue((String) value);
	BeanDefinition bd = (beanName != null && containsBean(beanName) ? getMergedBeanDefinition(beanName) : null);
	value = evaluateBeanDefinitionString(strVal, bd);
				}
				TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
				return (descriptor.getField() != null ?
						converter.convertIfNecessary(value, type, descriptor.getField()) :
						converter.convertIfNecessary(value, type, descriptor.getMethodParameter()));
			}
      ......
			return result;
		}
		finally {
			ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
		}
	}

getSuggestedValue是获得@Value注解的value值,例如我们在某个类里面定义了cc属性

@Value("${test.cc}")
private String cc;

getSuggestedValue获取到值是${test.cc}。

resolveEmbeddedValue方法是从MutablePropertySources中获取到属性值.

我们看下resolveEmbeddedValue方法是如何获取到属性值的。

@Override
@Nullable
public String resolveEmbeddedValue(@Nullable String value) {
   if (value == null) {
      return null;
   }
   String result = value;
   for (StringValueResolver resolver : this.embeddedValueResolvers) {
      result = resolver.resolveStringValue(result);
      if (result == null) {
         return null;
      }
   }
   return result;
}

embeddedValueResolvers是CopyOnWriteArrayList类型,我们期望的resolver是从environment中的MutablePropertySources获取到配置项的值。

我们通过idea,看到StringValueResolver的实现类有
PlaceholderResolvingStringValueResolver,StaticStringValueResolver,EmbeddedValueResolver,这几个实现类的resolveStringValue都没有从MutablePropertySources取值,这里的StringValueResolver真正的实现类在哪里呢?这个问题困惑了我好久,下面我们一起看下真正的实现类在哪里

众里寻她千百度,蓦然回首....

在idea中查找embeddedValueResolvers的Usages,找到了
PropertySourcesPlaceholderConfigurer类,它是BeanFactoryPostProcessor的实现类。

@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
   
   //....省略前面的代码
  
   processProperties(beanFactory, new PropertySourcesPropertyResolver(this.propertySources));
   this.appliedPropertySources = this.propertySources;
}

postProcessBeanFactory方法中调用了processProperties方法,点进方法查看一下,关键点就在里面

protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
      final ConfigurablePropertyResolver propertyResolver) throws BeansException {

   propertyResolver.setPlaceholderPrefix(this.placeholderPrefix);
   propertyResolver.setPlaceholderSuffix(this.placeholderSuffix);
   propertyResolver.setValueSeparator(this.valueSeparator);
   //创建了匿名的StringValueResolver的实现类,这才是获取@Value值的处理类
   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);
   };

   doProcessProperties(beanFactoryToProcess, valueResolver);
}

在上面的方法内部创建了匿名的StringValueResolver的实现类,实现了resolveStringValue方法,doProcessProperties方法会将StringValueResolver对象加入到embeddedValueResolvers中去。

匿名的StringValueResolver的实现类的resolveStringValue方法调用的是 PropertySourcesPropertyResolver类的处理方法,参数是MutablePropertySources类型的,内容包含了apollo配置,环境变量,项目内的properties文件,yml文件等配置。

一言以蔽之,真正获取属性值的类是PropertySourcesPropertyResolver

如何从MutablePropertySources中获取某个配置项的?

具体处理在
PropertySourcesPropertyResolver类的getPropertyAsRawString方法

protected String getPropertyAsRawString(String key) {
		return getProperty(key, String.class, false);
	}

	@Nullable
	protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) {
		if (this.propertySources != null) {
			for (PropertySource<?> propertySource : this.propertySources) {
				if (logger.isTraceEnabled()) {
					logger.trace("Searching for key '" + key + "' in PropertySource '" +
							propertySource.getName() + "'");
				}
				Object value = propertySource.getProperty(key);
				if (value != null) {
					if (resolveNestedPlaceholders && value instanceof String) {
						value = resolveNestedPlaceholders((String) value);
					}
					logKeyFound(key, propertySource, value);
					return convertValueIfNecessary(value, targetValueType);
				}
			}
		}
		if (logger.isDebugEnabled()) {
			logger.debug("Could not find key '" + key + "' in any property source");
		}
		return null;
	}

getProperty方法会对propertySources进行循环,获取到值,就退出循环。

比如说在apollo和application.properties文件中都存在相同的配置项test.cc,但是值不同,由于apollo配置是放在List的最前面,所以会取apollo里面的值,application.properties内的值会被忽略掉。这就是配置优先级的体现。

总结不易,请关注点赞哦!!!

详解@value属性是如何赋值的

\