@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。