【spring】@PropertySource与@Value的实现原理

@PropertySource的使用

package com.morris.spring.entity;

import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;

@Component
@Data
@PropertySource("classpath:application.properties")
public class Author {
	@Value("${author.name}")
	private String name;
}

实现原理

看源码之前,先来大胆的猜测下@PropertySource的实现原理:

  • @PropertySource是何时被spring所解析的?@PropertySource的解析肯定要发生在Bean的实例化之前,应该是在扫描@Component注解时顺便扫描了@PropertySource,而扫描@Component注解的结果就是向spring容器中加入BeanDefinition,所以@PropertySource的扫描应该发生在实现了BeanDefinitionRegistryPostProcessor的子类中。

  • 占位符的值是何时被填充的?是否像xml中的占位符填充一样,在BeanFactoryPostProcessor中直接填充BeanDefiniton中对应的属性。

@PropertySource注解的解析

调用入口:

org.springframework.context.annotation.ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry
->org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions
-->org.springframework.context.annotation.ConfigurationClassParser#parse(java.util.Set<org.springframework.beans.factory.config.BeanDefinitionHolder>)
---> org.springframework.context.annotation.ConfigurationClassParser#parse(org.springframework.core.type.AnnotationMetadata, java.lang.String)
----> org.springframework.context.annotation.ConfigurationClassParser#processConfigurationClass
-----> org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass

可以看出@PropertySource的解析确实发生在BeanDefinitionRegistryPostProcessor中。

ConfigurationClassParser#doProcessConfigurationClass

org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass

protected final SourceClass doProcessConfigurationClass(
		ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
		throws IOException {

	if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
		// Recursively process any member (nested) classes first
		processMemberClasses(configClass, sourceClass, filter);
	}

	// Process any @PropertySource annotations
	for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
			sourceClass.getMetadata(), PropertySources.class,
			org.springframework.context.annotation.PropertySource.class)) {
		if (this.environment instanceof ConfigurableEnvironment) {
			processPropertySource(propertySource);
		}
		else {
			logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
					"]. Reason: Environment must implement ConfigurableEnvironment");
		}
	}
... ...

ConfigurationClassParser#processPropertySource

org.springframework.context.annotation.ConfigurationClassParser#processPropertySource

private void processPropertySource(AnnotationAttributes propertySource) throws IOException {
	String name = propertySource.getString("name");
	if (!StringUtils.hasLength(name)) {
		name = null;
	}
	String encoding = propertySource.getString("encoding");
	if (!StringUtils.hasLength(encoding)) {
		encoding = null;
	}
	// 获取@PropertySource注解的value属性的值,也就是application.properties的位置
	String[] locations = propertySource.getStringArray("value");
	Assert.isTrue(locations.length > 0, "At least one @PropertySource(value) location is required");
	boolean ignoreResourceNotFound = propertySource.getBoolean("ignoreResourceNotFound");

	Class<? extends PropertySourceFactory> factoryClass = propertySource.getClass("factory");
	PropertySourceFactory factory = (factoryClass == PropertySourceFactory.class ?
			DEFAULT_PROPERTY_SOURCE_FACTORY : BeanUtils.instantiateClass(factoryClass));

	for (String location : locations) {
		try {
			String resolvedLocation = this.environment.resolveRequiredPlaceholders(location);
			Resource resource = this.resourceLoader.getResource(resolvedLocation);
			// 封装为一个PropertySource,new ResourcePropertySource()
			/**
			 * @see DefaultPropertySourceFactory#createPropertySource(java.lang.String, org.springframework.core.io.support.EncodedResource)
			 */
			addPropertySource(factory.createPropertySource(name, new EncodedResource(resource, encoding)));
		}
		catch (IllegalArgumentException | FileNotFoundException | UnknownHostException | SocketException ex) {
			// Placeholders not resolvable or resource not found when trying to open it
			if (ignoreResourceNotFound) {
				if (logger.isInfoEnabled()) {
					logger.info("Properties location [" + location + "] not resolvable: " + ex.getMessage());
				}
			}
			else {
				throw ex;
			}
		}
	}
}

DefaultPropertySourceFactory#createPropertySource

org.springframework.core.io.support.DefaultPropertySourceFactory#createPropertySource

@Override
public PropertySource<?> createPropertySource(@Nullable String name, EncodedResource resource) throws IOException {
	return (name != null ? new ResourcePropertySource(name, resource) : new ResourcePropertySource(resource));
}

ConfigurationClassParser#addPropertySource

org.springframework.context.annotation.ConfigurationClassParser#addPropertySource

private void addPropertySource(PropertySource<?> propertySource) {
	String name = propertySource.getName();
	MutablePropertySources propertySources = ((ConfigurableEnvironment) this.environment).getPropertySources();

	if (this.propertySourceNames.contains(name)) {
		// We've already added a version, we need to extend it
		// @PropertySource(name = "p1", value = "classpath:application.properties")
		// @PropertySource(name = "p1", value = "classpath:application.properties")
		PropertySource<?> existing = propertySources.get(name);
		if (existing != null) {
			PropertySource<?> newSource = (propertySource instanceof ResourcePropertySource ?
					((ResourcePropertySource) propertySource).withResourceName() : propertySource);
			// 处理多个ResourcePropertySource具体相同的name
			if (existing instanceof CompositePropertySource) {
				((CompositePropertySource) existing).addFirstPropertySource(newSource);
			}
			else {
				if (existing instanceof ResourcePropertySource) {
					existing = ((ResourcePropertySource) existing).withResourceName();
				}
				CompositePropertySource composite = new CompositePropertySource(name);
				composite.addPropertySource(newSource);
				composite.addPropertySource(existing);
				propertySources.replace(name, composite);
			}
			return;
		}
	}

	if (this.propertySourceNames.isEmpty()) {
		propertySources.addLast(propertySource);
	}
	else {
		String firstProcessed = this.propertySourceNames.get(this.propertySourceNames.size() - 1);
		propertySources.addBefore(firstProcessed, propertySource);
	}
	this.propertySourceNames.add(name);
}

总结:ConfigurationClassParser会对@PropertySource进行解析,然后封装到Environment的PropertySources中,与xml中占位符的处理逻辑类似。

从上面的源码,我们可以得出一个结论,不论是系统属性、环境变量还是系统中的属性都可以从Environment中获取,如果我们的代码中要使用这些属性不仅可以使用@Value注解来获取,也可以通过向代码中注入Environment对象来获取属性的值,例如:

package com.morris.spring.entity.aware;

import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;

@Configuration
@PropertySource("classpath:application.properties")
public class EnvironmentAwareBean implements EnvironmentAware {

	@Override
	public void setEnvironment(Environment environment) {
		System.out.println(environment.getProperty("author.name"));
	}
}

甚至可以注入Environment对象来解析我们自己的一些带有占位符的字符串,例如Mybatis中SQL中带有#{xxx},${xxx},都可以借助Environment来完成解析。

@Value注解的收集

@Value注解的收集与@Autowired注解的收集是在一处。

AutowiredAnnotationBeanPostProcessor#postProcessMergedBeanDefinition

org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#postProcessMergedBeanDefinition

public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
	InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
	metadata.checkConfigMembers(beanDefinition);
}

private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
	// Fall back to class name as cache key, for backwards compatibility with custom callers.
	String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
	// Quick check on the concurrent map first, with minimal locking.
	InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
	if (InjectionMetadata.needsRefresh(metadata, clazz)) {
		synchronized (this.injectionMetadataCache) {
			metadata = this.injectionMetadataCache.get(cacheKey);
			if (InjectionMetadata.needsRefresh(metadata, clazz)) {
				if (metadata != null) {
					metadata.clear(pvs);
				}
				metadata = buildAutowiringMetadata(clazz);
				this.injectionMetadataCache.put(cacheKey, metadata);
			}
		}
	}
	return metadata;
}

private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
	// autowiredAnnotationTypes是在构造方法中被初始化的
	if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) {
		return InjectionMetadata.EMPTY;
	}

	List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
	Class<?> targetClass = clazz;

	do {
		final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();

		// 查找带有@Autowired注解的属性
		ReflectionUtils.doWithLocalFields(targetClass, field -> {
			MergedAnnotation<?> ann = findAutowiredAnnotation(field);
			if (ann != null) {
				if (Modifier.isStatic(field.getModifiers())) {
					if (logger.isInfoEnabled()) {
						logger.info("Autowired annotation is not supported on static fields: " + field);
					}
					return;
				}
				boolean required = determineRequiredStatus(ann);
				currElements.add(new AutowiredFieldElement(field, required));
			}
		});

		// 查找带有@Autowired注解的方法
		ReflectionUtils.doWithLocalMethods(targetClass, method -> {
			Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
			if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
				return;
			}
			MergedAnnotation<?> ann = findAutowiredAnnotation(bridgedMethod);
			if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
				if (Modifier.isStatic(method.getModifiers())) {
					if (logger.isInfoEnabled()) {
						logger.info("Autowired annotation is not supported on static methods: " + method);
					}
					return;
				}
				if (method.getParameterCount() == 0) {
					if (logger.isInfoEnabled()) {
						logger.info("Autowired annotation should only be used on methods with parameters: " +
								method);
					}
				}
				boolean required = determineRequiredStatus(ann);
				PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
				currElements.add(new AutowiredMethodElement(method, required, pd));
			}
		});

		elements.addAll(0, currElements);
		targetClass = targetClass.getSuperclass();
	}
	while (targetClass != null && targetClass != Object.class);

	return InjectionMetadata.forElements(elements, clazz);
}

private MergedAnnotation<?> findAutowiredAnnotation(AccessibleObject ao) {
	MergedAnnotations annotations = MergedAnnotations.from(ao);
	for (Class<? extends Annotation> type : this.autowiredAnnotationTypes) {
		MergedAnnotation<?> annotation = annotations.get(type);
		if (annotation.isPresent()) {
			return annotation;
		}
	}
	return null;
}

AutowiredAnnotationBeanPostProcessor#AutowiredAnnotationBeanPostProcessor

autowiredAnnotationTypes这个是在构造方法中维护的:

org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#AutowiredAnnotationBeanPostProcessor

public AutowiredAnnotationBeanPostProcessor() {
	this.autowiredAnnotationTypes.add(Autowired.class);
	this.autowiredAnnotationTypes.add(Value.class);
	try {
		this.autowiredAnnotationTypes.add((Class<? extends Annotation>)
				ClassUtils.forName("javax.inject.Inject", AutowiredAnnotationBeanPostProcessor.class.getClassLoader()));
		logger.trace("JSR-330 'javax.inject.Inject' annotation found and supported for autowiring");
	}
	catch (ClassNotFoundException ex) {
		// JSR-330 API not available - simply skip.
	}
}

最终带有@Value注解的属性会被封装为一个个AutowiredMethodElement对象缓存起来。

占位符的填充

被@Value注解修饰的属性的占位符的填充不是像xml中的占位符填充一样在BeanFactoryPostProcessor中直接填充BeanDefiniton中对应的属性,而是在Bean实例化后对属性进行注入时顺带填充了占位符。

AutowiredAnnotationBeanPostProcessor#postProcessProperties

Bean实例化后会调用AutowiredAnnotationBeanPostProcessor#postProcessProperties来注入属性的值。

org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#postProcessProperties

public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
	// 这里会从缓存中取出@Autowired修饰的属性和方法
	InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
	try {
		// 对这些属性进行设置,方法进行调用
		metadata.inject(bean, beanName, pvs);
	}
	catch (BeanCreationException ex) {
		throw ex;
	}
	catch (Throwable ex) {
		throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
	}
	return pvs;
}

InjectionMetadata#inject

org.springframework.beans.factory.annotation.InjectionMetadata#inject

public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
	Collection<InjectedElement> checkedElements = this.checkedElements;
	Collection<InjectedElement> elementsToIterate =
			(checkedElements != null ? checkedElements : this.injectedElements);
	if (!elementsToIterate.isEmpty()) {
		for (InjectedElement element : elementsToIterate) {
			/**
			 * @see InjectedElement#inject(java.lang.Object, java.lang.String, org.springframework.beans.PropertyValues)
			 * @see AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject(java.lang.Object, java.lang.String, org.springframework.beans.PropertyValues)
			 * @see AutowiredAnnotationBeanPostProcessor.AutowiredMethodElement#inject(java.lang.Object, java.lang.String, org.springframework.beans.PropertyValues)
			 */
			element.inject(target, beanName, pvs);
		}
	}
}

中间省略一堆方法:

org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject
-> org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#resolveFieldValue
-->org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveDependency
--->org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency
---->org.springframework.beans.factory.support.AbstractBeanFactory#resolveEmbeddedValue

AbstractBeanFactory#resolveEmbeddedValue

最终会调用到:

org.springframework.beans.factory.support.AbstractBeanFactory#resolveEmbeddedValue

public String resolveEmbeddedValue(@Nullable String value) {
	if (value == null) {
		return null;
	}
	String result = value;
	// 这里embeddedValueResolvers最终会调用到Environment
	for (StringValueResolver resolver : this.embeddedValueResolvers) {
		result = resolver.resolveStringValue(result);
		if (result == null) {
			return null;
		}
	}
	return result;
}

那么AbstractBeanFactory的embeddedValueResolvers属性从哪里来的呢?

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
... ....
	if (!beanFactory.hasEmbeddedValueResolver()) {
		// 将Environment中的StringValueResolver添加到BeanFactory
		beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
	}
... ...
	// 实例化
	beanFactory.preInstantiateSingletons();
}

总结:AutowiredAnnotationBeanPostProcessor注入属性的同时会对属性中的占位符进行填充,而填充的值来源于上一步中加入到Environment的PropertySources。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

morris131

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

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

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

打赏作者

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

抵扣说明:

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

余额充值