深入分析@PropertySource源码
文章目录
- 一、源码时序图
- 二、源码解析
- 1. 运行案例程序启动类
- 2. 解析ConfigurationClassParser类的doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass, Predicatefilter)方法
- 3. 解析PropertySourceRegistry类的processPropertySource(AnnotationAttributes propertySource)方法
- 4. 解析PropertySourceProcessor类的processPropertySource(PropertySourceDescriptor descriptor)方法
- 5. 解析DefaultPropertySourceFactory类的createPropertySource(String name, EncodedResource resource)方法
- 6. 回到PropertySourceProcessor类的processPropertySource(PropertySourceDescriptor descriptor)方法
- 7. 解析PropertySourceProcessor类的addPropertySource(PropertySource<?> propertySource)方法
一、源码时序图
本节,就以源码时序图的方式,直观的感受下@PropertySource注解在Spring源码层面的执行流程。@PropertySource注解在Spring源码层面的执行流程如图所示:
由图可以看出,@PropertySource注解在Spring源码层面的执行流程会涉及到PropertySourceTest类、AnnotationConfigApplicationContext类、AbstractApplicationContext类、PostProcessorRegistrationDelegate类、ConfigurationClassPostProcessor类、ConfigurationClassParser类、PropertySourceRegistry类、PropertySourceProcessor类和DefaultPropertySourceFactory类。具体的源码执行细节参见源码解析部分。
二、源码解析
@PropertySource注解在Spring源码层面的执行流程,结合源码执行的时序图,会理解的更加深刻。
1. 运行案例程序启动类
在PropertySourceTest类的main()方法中调用了AnnotationConfigApplicationContext类的构造方法,并传入了PropertySourceConfig类的Class对象来创建IOC容器。接下来,会进入AnnotationConfigApplicationContext类的构造方法。
注意:@PropertySource注解在Spring源码中的执行流程的(2)~(11)步与前面章节的@Import注解相同,这里不再赘述,直接跳到ConfigurationClassParser类的doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass, Predicatefilter)方法。
2. 解析ConfigurationClassParser类的doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass, Predicatefilter)方法
protected final SourceClass doProcessConfigurationClass(
ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
throws IOException {
//#############省略其他代码################
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), PropertySources.class,
org.springframework.context.annotation.PropertySource.class)) {
if (this.propertySourceRegistry != null) {
this.propertySourceRegistry.processPropertySource(propertySource);
}
}
//#############省略其他代码################
}
可以看到,在ConfigurationClassParser类的doProcessConfigurationClass()方法中,遍历获取到的@PropertySources注解和@PropertySource注解的属性,并且调用propertySourceRegistry对象的processPropertySource()方法解析注解属性的值。
3. 解析PropertySourceRegistry类的processPropertySource(AnnotationAttributes propertySource)方法
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;
}
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");
Class<? extends PropertySourceFactory> factorClassToUse = (factoryClass != PropertySourceFactory.class ? factoryClass : null);
PropertySourceDescriptor descriptor = new PropertySourceDescriptor(Arrays.asList(locations), ignoreResourceNotFound, name, factorClassToUse, encoding);
this.propertySourceProcessor.processPropertySource(descriptor);
this.descriptors.add(descriptor);
}
可以看到,在PropertySourceRegistry类的processPropertySource()方法中,解析@PropertySource注解中的属性后,将解析出的属性值封装到PropertySourceDescriptor对象中,调用propertySourceProcessor对象的processPropertySource()方法,并传入PropertySourceDescriptor对象进行进一步处理。
4. 解析PropertySourceProcessor类的processPropertySource(PropertySourceDescriptor descriptor)方法
public void processPropertySource(PropertySourceDescriptor descriptor) throws IOException {
String name = descriptor.name();
String encoding = descriptor.encoding();
List<String> locations = descriptor.locations();
Assert.isTrue(locations.size() > 0, "At least one @PropertySource(value) location is required");
boolean ignoreResourceNotFound = descriptor.ignoreResourceNotFound();
PropertySourceFactory factory = (descriptor.propertySourceFactory() != null ? instantiateClass(descriptor.propertySourceFactory()) : DEFAULT_PROPERTY_SOURCE_FACTORY);
for (String location : locations) {
try {
String resolvedLocation = this.environment.resolveRequiredPlaceholders(location);
Resource resource = this.resourceLoader.getResource(resolvedLocation);
addPropertySource(factory.createPropertySource(name, new EncodedResource(resource, encoding)));
}
catch (IllegalArgumentException | FileNotFoundException | UnknownHostException | SocketException ex) {
//#########省略其他代码################
}
}
}
可以看到,在processPropertySource()方法中,会通过@PropertySource注解的属性值解析出配置文件的内容,并且通过factory对象的createPropertySource()方法来创建PropertySource对象。
5. 解析DefaultPropertySourceFactory类的createPropertySource(String name, EncodedResource resource)方法
@Override
public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
return (name != null ? new ResourcePropertySource(name, resource) : new ResourcePropertySource(resource));
}
6. 回到PropertySourceProcessor类的processPropertySource(PropertySourceDescriptor descriptor)方法
在PropertySourceProcessor类的processPropertySource()方法中,创建完PropertySource对象后,会调用addPropertySource()方法将获取到的属性值添加到Spring的环境变量中。
7. 解析PropertySourceProcessor类的addPropertySource(PropertySource<?> propertySource)方法
private void addPropertySource(org.springframework.core.env.PropertySource<?> propertySource) {
String name = propertySource.getName();
MutablePropertySources propertySources = this.environment.getPropertySources();
if (this.propertySourceNames.contains(name)) {
org.springframework.core.env.PropertySource<?> existing = propertySources.get(name);
if (existing != null) {
PropertySource<?> newSource = (propertySource instanceof ResourcePropertySource ?((ResourcePropertySource) propertySource).withResourceName() : propertySource);
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);
}
可以看到,在PropertySourceProcessor类的addPropertySource()方法中,会将解析出的配置文件的内容添加到Spring的环境变量中。具体就是在PropertySourceProcessor类的addPropertySource()方法中,获取到ConfigurableEnvironment中的MutablePropertySources对象,用来存储解析出的配置文件中的配置项内容。如果有相同的配置项内容,将existing对象强转为CompositePropertySource类型,把新旧相同的配置项进行合并,再放到MutablePropertySources对象中。
后续就可以通过Spring的环境变量,来获取到配置文件中的配置项内容。
至此,@PropertySource注解在Spring源码中的执行流程分析完毕。