Spring的@PropertySource注解源码解析

 更新时间:2023年11月23日 11:00:53   作者:Super_Leng  
这篇文章主要介绍了Spring的@PropertySource注解源码解析,就以源码时序图的方式,直观的感受下@PropertySource注解在Spring源码层面的执行流程,需要的朋友可以参考下

一、源码时序图

本节,就以源码时序图的方式,直观的感受下@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源码中的执行流程分析完毕。

到此这篇关于Spring的@PropertySource注解源码解析的文章就介绍到这了,更多相关@PropertySource注解源码内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Presto自定义函数@SqlNullable引发问题详解

    Presto自定义函数@SqlNullable引发问题详解

    这篇文章主要为大家介绍了Presto自定义函数@SqlNullable引发问题详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-12-12
  • 谈谈对Java中的volatile的理解

    谈谈对Java中的volatile的理解

    这篇文章主要介绍了对Java中的volatile的理解,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-11-11
  • 详解SpringBoot 调用外部接口的三种方式

    详解SpringBoot 调用外部接口的三种方式

    SpringBoot不仅继承了Spring框架原有的优秀特性,而且还通过简化配置来进一步简化了Spring应用的整个搭建和开发过程,这篇文章主要介绍了SpringBoot 调用外部接口的三种方式,需要的朋友可以参考下
    2023-04-04
  • Feign调用服务时丢失Cookie和Header信息的解决方案

    Feign调用服务时丢失Cookie和Header信息的解决方案

    这篇文章主要介绍了Feign调用服务时丢失Cookie和Header信息的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-03-03
  • Spring-cloud 服务发现与消费(以ribbon为例)

    Spring-cloud 服务发现与消费(以ribbon为例)

    这篇文章主要介绍了Spring-cloud 服务发现与消费(以ribbon为例),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-04-04
  • SpringBoot读取yml文件中配置数组的2种方法

    SpringBoot读取yml文件中配置数组的2种方法

    这篇文章主要介绍了SpringBoot读取yml文件中配置数组的2种方法,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-12-12
  • Java中jstat命令的使用详解

    Java中jstat命令的使用详解

    jstat命令可以查看堆内存各部分的使用量,以及加载类的数量,下面这篇文章主要给大家介绍了关于Java中jstat命令使用的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-03-03
  • Maven deploy plugin使用方式

    Maven deploy plugin使用方式

    这篇文章主要介绍了Maven deploy plugin使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-11-11
  • request如何获取body的json数据

    request如何获取body的json数据

    这篇文章主要介绍了request如何获取body的json数据操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-06-06
  • 解决SpringBoot自定义拦截器和跨域配置冲突的问题

    解决SpringBoot自定义拦截器和跨域配置冲突的问题

    这篇文章主要介绍了解决SpringBoot自定义拦截器和跨域配置冲突的问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-08-08

最新评论