Spring原理分析--获取Environment资源对象

1.使用getEnvironment()获取环境信息

ApplicationContext接口继承了EnvironmentCapable接口,可以通过getEnvironment()获取Environment配置信息,例如:

@SpringBootApplication
public class A01 {
    public static void main(String[] args) throws IOException {
        ConfigurableApplicationContext context = SpringApplication.run(A01.class, args);
        // 获取系统配置及自定义配置信息
        System.out.println(context.getEnvironment().getProperty("java_home"));
        System.out.println(context.getEnvironment().getProperty("server.port"));
    }
}

接下来我们就探究下SpringBoot是如何加载环境及配置信息的

2.Spring读取环境信息原理

2.1 创建Environment对象

SpringBoot启动时就会调用prepareEnvironment创建Environment对象,SpringApplication#run源码如下:

public ConfigurableApplicationContext run(String... args) {
    // 省略其他代码...

    // 创建Environment对象,其中包括系统环境信息及Spring配置文件信息
    ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
    configureIgnoreBeanInfo(environment);
    Banner printedBanner = printBanner(environment);
    context = createApplicationContext();
    context.setApplicationStartup(this.applicationStartup);
    // 将一些对象设置到容器中,其中包含Environment对象
    prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
    refreshContext(context);
    afterRefresh(context, applicationArguments);

    // 省略其他代码...

    return context;
}

进入prepareEnvironment方法中创建Environment对象的源码如下:

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
    // 1.创建ConfigurableEnvironment对象
    ConfigurableEnvironment environment = this.getOrCreateEnvironment();
    // 补充启动参数
    this.configureEnvironment(environment, applicationArguments.getSourceArgs());
    // 创建ConfigurationPropertySources
    ConfigurationPropertySources.attach(environment);
    // 2.通过发送事件的方式加载Spring配置
    listeners.environmentPrepared(bootstrapContext, environment);
    // 将DefaultPropertiesPropertySource移动到数组尾部
    DefaultPropertiesPropertySource.moveToEnd(environment);
    Assert.state(!environment.containsProperty("spring.main.environment-prefix"), "Environment prefix cannot be set via properties.");
    this.bindToSpringApplication(environment);
    if (!this.isCustomEnvironment) {
        EnvironmentConverter environmentConverter = new EnvironmentConverter(this.getClassLoader());
        environment = environmentConverter.convertEnvironmentIfNecessary(environment, this.deduceEnvironmentClass());
    }
    // 更新ConfigurationPropertySources
    ConfigurationPropertySources.attach(environment);
    return environment;
}

创建ConfigurableEnvironment对象源码如下:

private ConfigurableEnvironment getOrCreateEnvironment() {
    // 此时environment为null
    if (this.environment != null) {
        return this.environment;
    } else {
        // 调用DefaultApplicationContextFactory创建environment
        ConfigurableEnvironment environment = this.applicationContextFactory.createEnvironment(this.webApplicationType);
        if (environment == null && this.applicationContextFactory != ApplicationContextFactory.DEFAULT) {
            environment = ApplicationContextFactory.DEFAULT.createEnvironment(this.webApplicationType);
        }

        return (ConfigurableEnvironment)(environment != null ? environment : new ApplicationEnvironment());
    }
}

// DefaultApplicationContextFactory#createEnvironment
public ConfigurableEnvironment createEnvironment(WebApplicationType webApplicationType) {
    // 调用静态方法引用ApplicationContextFactory::createEnvironment创建environment
    return (ConfigurableEnvironment)this.getFromSpringFactories(webApplicationType, ApplicationContextFactory::createEnvironment, (Supplier)null);
}

private <T> T getFromSpringFactories(WebApplicationType webApplicationType, BiFunction<ApplicationContextFactory, WebApplicationType, T> action, Supplier<T> defaultResult) {
    Iterator var4 = SpringFactoriesLoader.loadFactories(ApplicationContextFactory.class, this.getClass().getClassLoader()).iterator();

    Object result;
    do {
        if (!var4.hasNext()) {
            return defaultResult != null ? defaultResult.get() : null;
        }
        ApplicationContextFactory candidate = (ApplicationContextFactory)var4.next();
        // 创建environment,实际调用的是Factory#createEnvironment
        result = action.apply(candidate, webApplicationType);
    } while(result == null);

    return result;
}

这里的静态方法引用调用的是AnnotationConfigServletWebServerApplicationContext.Factory#createEnvironment,创建的对象是ApplicationServletEnvironment的实例

public ConfigurableEnvironment createEnvironment(WebApplicationType webApplicationType) {
    return webApplicationType != WebApplicationType.SERVLET ? null : new ApplicationServletEnvironment();
}

2.2 读取系统配置

ApplicationServletEnvironment创建实例时会调用父类AbstractEnvironment的构造函数初始化环境信息

public AbstractEnvironment() {
    this(new MutablePropertySources());
}

protected AbstractEnvironment(MutablePropertySources propertySources) {
    this.logger = LogFactory.getLog(this.getClass());
    this.activeProfiles = new LinkedHashSet();
    this.defaultProfiles = new LinkedHashSet(this.getReservedDefaultProfiles());
    this.propertySources = propertySources;
    this.propertyResolver = this.createPropertyResolver(propertySources);
    // 会调用到StandardEnvironment#customizePropertySources加载系统配置
    this.customizePropertySources(propertySources);
}

调用子类的StandardEnvironment#customizePropertySources加载系统配置

protected void customizePropertySources(MutablePropertySources propertySources) {
    // 读取系统配置
    propertySources.addLast(new PropertiesPropertySource("systemProperties", this.getSystemProperties()));
    // 读取系统信息
    propertySources.addLast(new SystemEnvironmentPropertySource("systemEnvironment", this.getSystemEnvironment()));
}

// AbstractEnvironment#getSystemProperties
public Map<String, Object> getSystemProperties() {
    try {
        // 获取系统配置
        return System.getProperties();
    } catch (AccessControlException var2) {
        return new ReadOnlySystemAttributesMap() {
            @Nullable
            protected String getSystemAttribute(String attributeName) {
                try {
                    return System.getProperty(attributeName);
                } catch (AccessControlException var3) {
                    if (AbstractEnvironment.this.logger.isInfoEnabled()) {
                        AbstractEnvironment.this.logger.info("Caught AccessControlException when accessing system property '" + attributeName + "'; its value will be returned [null]. Reason: " + var3.getMessage());
                    }

                    return null;
                }
            }
        };
    }
}

// AbstractEnvironment#getSystemEnvironment
public Map<String, Object> getSystemEnvironment() {
    if (this.suppressGetenvAccess()) {
        return Collections.emptyMap();
    } else {
        try {
            // 读取环境信息
            return System.getenv();
        } catch (AccessControlException var2) {
            return new ReadOnlySystemAttributesMap() {
                @Nullable
                protected String getSystemAttribute(String attributeName) {
                    try {
                        return System.getenv(attributeName);
                    } catch (AccessControlException var3) {
                        if (AbstractEnvironment.this.logger.isInfoEnabled()) {
                            AbstractEnvironment.this.logger.info("Caught AccessControlException when accessing system environment variable '" + attributeName + "'; its value will be returned [null]. Reason: " + var3.getMessage());
                        }

                        return null;
                    }
                }
            };
        }
    }
}

到这一步,系统的环境信息就获取到了,例如java.home、os.version等,那么Spring配置文件application.properties等配置信息怎么获取的呢?

2.3 加载Spring配置

2.3.1 发布事件

加载Spring配置是通过调用listeners.environmentPrepared(bootstrapContext, environment)发布ApplicationEnvironmentPreparedEvent事件来完成的,具体看源码SpringApplicationRunListeners#environmentPrepared

void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) {
    this.doWithListeners("spring.boot.application.environment-prepared", (listener) -> {
        listener.environmentPrepared(bootstrapContext, environment);
    });
}

private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction) {
    this.doWithListeners(stepName, listenerAction, (Consumer)null);
}

private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction, Consumer<StartupStep> stepAction) {
    StartupStep step = this.applicationStartup.start(stepName);
    // 调用消费者发布事件
    this.listeners.forEach(listenerAction);
    if (stepAction != null) {
        stepAction.accept(step);
    }

    step.end();
}

doWithListeners中会调用listener.environmentPrepared(bootstrapContext, environment)发布事件,源码见EventPublishingRunListener#environmentPrepared

public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) {
    this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(bootstrapContext, this.application, this.args, environment));
}

可见发布的事件类型是ApplicationEnvironmentPreparedEvent,进入multicastEvent方法

public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
    ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
    Executor executor = getTaskExecutor();
    for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
        if (executor != null) {
            executor.execute(() -> invokeListener(listener, event));
        }
        else {
            // 调用监听器处理事件
            invokeListener(listener, event);
        }
    }
}

protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
    ErrorHandler errorHandler = getErrorHandler();
    if (errorHandler != null) {
        try {
            doInvokeListener(listener, event);
        }
        catch (Throwable err) {
            errorHandler.handleError(err);
        }
    }
    else {
        doInvokeListener(listener, event);
    }
}

@SuppressWarnings({"rawtypes", "unchecked"})
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
    try {
        listener.onApplicationEvent(event);
    }
    // 省略异常处理代码...
}

最终调用ApplicationListener的onApplicationEvent方法处理事件的

2.3.2 注册事件处理器

这里我们可能疑惑,这些事件处理器是怎么注册的呢?

首先SpringBoot项目启动时,会调用SpringApplication构造器设置listeners

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    this.bootstrapRegistryInitializers = new ArrayList<>(
        getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    // 设置ApplicationListener
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    this.mainApplicationClass = deduceMainApplicationClass();
}

进入getSpringFactoriesInstances(ApplicationListener.class)方法内部

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
    return getSpringFactoriesInstances(type, new Class<?>[] {});
}

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
    ClassLoader classLoader = getClassLoader();
    // 从配置文件中获取listener类名
    Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    // 实例化
    List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
    AnnotationAwareOrderComparator.sort(instances);
    return instances;
}

通过读取spring.factories配置文件获取listener类名,然后实例化对象

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
    ClassLoader classLoaderToUse = classLoader;
    if (classLoaderToUse == null) {
        classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
    }
    String factoryTypeName = factoryType.getName();
    return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}

private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
    Map<String, List<String>> result = cache.get(classLoader);
    if (result != null) {
        return result;
    }
    
    result = new HashMap<>();
    try {
        // 获取META-INF/spring.factories资源文件URL
        Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
        while (urls.hasMoreElements()) {
            URL url = urls.nextElement();
            UrlResource resource = new UrlResource(url);
            // 读取配置文件中的值
            Properties properties = PropertiesLoaderUtils.loadProperties(resource);
            for (Map.Entry<?, ?> entry : properties.entrySet()) {
                String factoryTypeName = ((String) entry.getKey()).trim();
                String[] factoryImplementationNames =
                StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
                for (String factoryImplementationName : factoryImplementationNames) {
                    result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
                    .add(factoryImplementationName.trim());
                }
            }
        }
    
        result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
                          .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
        cache.put(classLoader, result);
    }
    catch (IOException ex) {
        throw new IllegalArgumentException("Unable to load factories from location [" +
                                           FACTORIES_RESOURCE_LOCATION + "]", ex);
    }
    return result;
}

spring-boot的jar包中的META-INF/spring.factories配置如下:

# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.env.EnvironmentPostProcessorApplicationListener

所以EnvironmentPostProcessorApplicationListener会被添加到容器中

当调用SpringApplication.run时,EventPublishingRunListener也会被创建,源码如下:

public ConfigurableApplicationContext run(String... args) {
    // 省略其他代码...

    // 创建EventPublishingRunListener
    SpringApplicationRunListeners listeners = getRunListeners(args);

    // 省略其他代码...
}

private SpringApplicationRunListeners getRunListeners(String[] args) {
    Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
    return new SpringApplicationRunListeners(logger,
                                             getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args),
                                             this.applicationStartup);
}

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
    return getSpringFactoriesInstances(type, new Class<?>[] {});
}

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
    ClassLoader classLoader = getClassLoader();
    // 从配置文件中获取SpringApplicationRunListener的类名
    Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    // 实例化对象
    List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
    AnnotationAwareOrderComparator.sort(instances);
    return instances;
}

spring-boot的jar包中的META-INF/spring.factories配置如下:

# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener

所以EventPublishingRunListener对象会被创建,源码如下:

public EventPublishingRunListener(SpringApplication application, String[] args) {
    this.application = application;
    this.args = args;
    this.initialMulticaster = new SimpleApplicationEventMulticaster();
    // 添加ApplicationListener到initialMulticaster中
    for (ApplicationListener<?> listener : application.getListeners()) {
        this.initialMulticaster.addApplicationListener(listener);
    }
}

application.getListeners()会获取到上文中添加到容器中的listeners,将每个listener添加到initialMulticaster中,源码如下:

public void addApplicationListener(ApplicationListener<?> listener) {
    synchronized (this.defaultRetriever) {
        Object singletonTarget = AopProxyUtils.getSingletonTarget(listener);
        if (singletonTarget instanceof ApplicationListener) {
            this.defaultRetriever.applicationListeners.remove(singletonTarget);
        }
        // 添加到defaultRetriever的applicationListeners成员变量中
        this.defaultRetriever.applicationListeners.add(listener);
        this.retrieverCache.clear();
    }
}

这样defaultRetriever的applicationListeners成员变量就保存了这些listener对象

再看看multicastEvent方法中的getApplicationListeners(event, type),会调用retrieveApplicationListeners

private Collection<ApplicationListener<?>> retrieveApplicationListeners(
    ResolvableType eventType, @Nullable Class<?> sourceType, @Nullable CachedListenerRetriever retriever) {

    List<ApplicationListener<?>> allListeners = new ArrayList<>();
    Set<ApplicationListener<?>> filteredListeners = (retriever != null ? new LinkedHashSet<>() : null);
    Set<String> filteredListenerBeans = (retriever != null ? new LinkedHashSet<>() : null);

    Set<ApplicationListener<?>> listeners;
    Set<String> listenerBeans;
    synchronized (this.defaultRetriever) {
        // 从defaultRetriever中获取listeners
        listeners = new LinkedHashSet<>(this.defaultRetriever.applicationListeners);
        listenerBeans = new LinkedHashSet<>(this.defaultRetriever.applicationListenerBeans);
    }

    // 过滤符合条件的listener
    for (ApplicationListener<?> listener : listeners) {
        if (supportsEvent(listener, eventType, sourceType)) {
            if (retriever != null) {
                filteredListeners.add(listener);
            }
            allListeners.add(listener);
        }
    }

    // 省略部分代码...

    return allListeners;
}

这样就获取到了listener处理事件,其中就包括EnvironmentPostProcessorApplicationListener

2.3.3 处理事件

EnvironmentPostProcessorApplicationListener#onApplicationEvent会处理上述发布的ApplicationEnvironmentPreparedEvent事件

public void onApplicationEvent(ApplicationEvent event) {
    if (event instanceof ApplicationEnvironmentPreparedEvent) {
        // 处理ApplicationEnvironmentPreparedEvent事件
        this.onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent)event);
    }
    if (event instanceof ApplicationPreparedEvent) {
        this.onApplicationPreparedEvent();
    }
    if (event instanceof ApplicationFailedEvent) {
        this.onApplicationFailedEvent();
    }
}

private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
    ConfigurableEnvironment environment = event.getEnvironment();
    SpringApplication application = event.getSpringApplication();
    // 获取环境相关的后处理器
    for (EnvironmentPostProcessor postProcessor : getEnvironmentPostProcessors(application.getResourceLoader(),
                                                                               event.getBootstrapContext())) {
        postProcessor.postProcessEnvironment(environment, application);
    }
}

getEnvironmentPostProcessors会读取spring.factories配置文件获取EnvironmentPostProcessor

List<EnvironmentPostProcessor> getEnvironmentPostProcessors(ResourceLoader resourceLoader,
                                                            ConfigurableBootstrapContext bootstrapContext) {
    ClassLoader classLoader = (resourceLoader != null) ? resourceLoader.getClassLoader() : null;
    // 这个postProcessorsFactory是通过构造器添加的
    EnvironmentPostProcessorsFactory postProcessorsFactory = this.postProcessorsFactory.apply(classLoader);
    return postProcessorsFactory.getEnvironmentPostProcessors(this.deferredLogs, bootstrapContext);
}

this.postProcessorsFactory.apply(classLoader)会调用如下EnvironmentPostProcessorsFactory#fromSpringFactories,加载EnvironmentPostProcessor

static EnvironmentPostProcessorsFactory fromSpringFactories(ClassLoader classLoader) {
    return new ReflectionEnvironmentPostProcessorsFactory(classLoader,
                                                          SpringFactoriesLoader.loadFactoryNames(EnvironmentPostProcessor.class, classLoader));
}

spring-boot的jar包中的META-INF/spring.factories配置如下:

# Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor,\
org.springframework.boot.env.RandomValuePropertySourceEnvironmentPostProcessor,\
org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,\
org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor,\
org.springframework.boot.reactor.DebugAgentEnvironmentPostProcessor

getEnvironmentPostProcessors就会实例化EnvironmentPostProcessor,这样ConfigDataEnvironmentPostProcessor对象就会被创建,调用它的postProcessEnvironment时获取配置信息

void postProcessEnvironment(ConfigurableEnvironment environment, ResourceLoader resourceLoader,
                            Collection<String> additionalProfiles) {
    try {
        this.logger.trace("Post-processing environment to add config data");
        resourceLoader = (resourceLoader != null) ? resourceLoader : new DefaultResourceLoader();
        // 创建ConfigDataEnvironment获取配置信息
        getConfigDataEnvironment(environment, resourceLoader, additionalProfiles).processAndApply();
    }
    catch (UseLegacyConfigProcessingException ex) {
        this.logger.debug(LogMessage.format("Switching to legacy config file processing [%s]",
                                            ex.getConfigurationProperty()));
        configureAdditionalProfiles(environment, additionalProfiles);
        postProcessUsingLegacyApplicationListener(environment, resourceLoader);
    }
}

ConfigDataEnvironment getConfigDataEnvironment(ConfigurableEnvironment environment, ResourceLoader resourceLoader,
                                               Collection<String> additionalProfiles) {
    return new ConfigDataEnvironment(this.logFactory, this.bootstrapContext, environment, resourceLoader,
                                     additionalProfiles, this.environmentUpdateListener);
}

ConfigDataEnvironment是SpringBoot配置数据加载和管理的核心组件,它的构造函数如下,主要创建了ConfigDataLocationResolvers、ConfigDataLoaders及ConfigDataEnvironmentContributors

ConfigDataEnvironment(DeferredLogFactory logFactory, ConfigurableBootstrapContext bootstrapContext,
                      ConfigurableEnvironment environment, ResourceLoader resourceLoader, Collection<String> additionalProfiles,
                      ConfigDataEnvironmentUpdateListener environmentUpdateListener) {
    Binder binder = Binder.get(environment);
    UseLegacyConfigProcessingException.throwIfRequested(binder);
    this.logFactory = logFactory;
    this.logger = logFactory.getLog(getClass());
    this.notFoundAction = binder.bind(ON_NOT_FOUND_PROPERTY, ConfigDataNotFoundAction.class)
        .orElse(ConfigDataNotFoundAction.FAIL);
    this.bootstrapContext = bootstrapContext;
    this.environment = environment;
    // 创建配置文件位置解析器
    this.resolvers = createConfigDataLocationResolvers(logFactory, bootstrapContext, binder, resourceLoader);
    this.additionalProfiles = additionalProfiles;
    this.environmentUpdateListener = (environmentUpdateListener != null) ? environmentUpdateListener
        : ConfigDataEnvironmentUpdateListener.NONE;
    // 创建配置文件加载器
    this.loaders = new ConfigDataLoaders(logFactory, bootstrapContext, resourceLoader.getClassLoader());
    // 创建ConfigDataEnvironmentContributors
    this.contributors = createContributors(binder);
}

// ConfigDataLocationResolvers构造器
ConfigDataLocationResolvers(DeferredLogFactory logFactory, ConfigurableBootstrapContext bootstrapContext,
                            Binder binder, ResourceLoader resourceLoader) {
    this(logFactory, bootstrapContext, binder, resourceLoader, SpringFactoriesLoader
         .loadFactoryNames(ConfigDataLocationResolver.class, resourceLoader.getClassLoader()));
}

// ConfigDataLoaders构造器
ConfigDataLoaders(DeferredLogFactory logFactory, ConfigurableBootstrapContext bootstrapContext,
                  ClassLoader classLoader) {
    this(logFactory, bootstrapContext, classLoader,
         SpringFactoriesLoader.loadFactoryNames(ConfigDataLoader.class, classLoader));
}

这里也通过SpringFactoriesLoader#loadFactoryNames从配置文件中创建ConfigDataLocationResolver及ConfigDataLoader的,spring-boot的jar包中的META-INF/spring.factories配置如下:

# ConfigData Location Resolvers
org.springframework.boot.context.config.ConfigDataLocationResolver=\
org.springframework.boot.context.config.ConfigTreeConfigDataLocationResolver,\
org.springframework.boot.context.config.StandardConfigDataLocationResolver

# ConfigData Loaders
org.springframework.boot.context.config.ConfigDataLoader=\
org.springframework.boot.context.config.ConfigTreeConfigDataLoader,\
org.springframework.boot.context.config.StandardConfigDataLoader

其中StandardConfigDataLocationResolver用于解析标准的配置文件位置,例如类路径下的config目录中的applicaiton.properties或者applicaiton.yml,而StandardConfigDataLoader则用于加载标准的配置数据

创建ConfigDataEnvironmentContributors源码如下:

private ConfigDataEnvironmentContributors createContributors(Binder binder) {
    this.logger.trace("Building config data environment contributors");
    MutablePropertySources propertySources = this.environment.getPropertySources();
    List<ConfigDataEnvironmentContributor> contributors = new ArrayList<>(propertySources.size() + 10);
    PropertySource<?> defaultPropertySource = null;
    for (PropertySource<?> propertySource : propertySources) {
        if (DefaultPropertiesPropertySource.hasMatchingName(propertySource)) {
            defaultPropertySource = propertySource;
        }
        else {
            this.logger.trace(LogMessage.format("Creating wrapped config data contributor for '%s'",
                                                propertySource.getName()));
            contributors.add(ConfigDataEnvironmentContributor.ofExisting(propertySource));
        }
    }
    // 关键方法:getInitialImportContributors
    contributors.addAll(getInitialImportContributors(binder));
    if (defaultPropertySource != null) {
        this.logger.trace("Creating wrapped config data contributor for default property source");
        contributors.add(ConfigDataEnvironmentContributor.ofExisting(defaultPropertySource));
    }
    return createContributors(contributors);
}

protected ConfigDataEnvironmentContributors createContributors(
    List<ConfigDataEnvironmentContributor> contributors) {
    return new ConfigDataEnvironmentContributors(this.logFactory, this.bootstrapContext, contributors);
}

ConfigDataEnvironmentContributors主要作用是维护一组ConfigDataEnvironmentContributor,而每个ConfigDataEnvironmentContributor用于配置数据的加载、处理和管理

这里重点看下getInitialImportContributors(binder)获取初始导入的ConfigDataEnvironmentContributor,这决定了配置文件的加载位置及顺序

private List<ConfigDataEnvironmentContributor> getInitialImportContributors(Binder binder) {
    List<ConfigDataEnvironmentContributor> initialContributors = new ArrayList<>();
    // 1.指定spring.config.import要导入的额外配置文件位置
    addInitialImportContributors(initialContributors, bindLocations(binder, IMPORT_PROPERTY, EMPTY_LOCATIONS));
    // 2.指定spring.config.additional-location额外的配置文件位置
    addInitialImportContributors(initialContributors,
                                 bindLocations(binder, ADDITIONAL_LOCATION_PROPERTY, EMPTY_LOCATIONS));
    // 3.指定spring.config.location默认配置文件位置
    addInitialImportContributors(initialContributors,
                                 bindLocations(binder, LOCATION_PROPERTY, DEFAULT_SEARCH_LOCATIONS));
    return initialContributors;
}

private void addInitialImportContributors(List<ConfigDataEnvironmentContributor> initialContributors,
                                          ConfigDataLocation[] locations) {
    // 从后往前遍历
    for (int i = locations.length - 1; i >= 0; i--) {
        initialContributors.add(createInitialImportContributor(locations[i]));
    }
}

在第3步中指定的配置文件地址DEFAULT_SEARCH_LOCATIONS包含:

static final ConfigDataLocation[] DEFAULT_SEARCH_LOCATIONS;
static {
    List<ConfigDataLocation> locations = new ArrayList<>();
    locations.add(ConfigDataLocation.of("optional:classpath:/;optional:classpath:/config/"));
    locations.add(ConfigDataLocation.of("optional:file:./;optional:file:./config/;optional:file:./config/*/"));
    DEFAULT_SEARCH_LOCATIONS = locations.toArray(new ConfigDataLocation[0]);
}

而addInitialImportContributors是从后往前遍历的,因此加载顺序为:

file:./
file:./config/
file:./config/*/
classpath:/
classpath:/config/

创建ConfigDataEnvironmentContributors完成后就会调用ConfigDataEnvironment#processAndApply加载和解析配置文件,源码如下:

void processAndApply() {
    // 创建数据导入器
    ConfigDataImporter importer = new ConfigDataImporter(this.logFactory, this.notFoundAction, this.resolvers,
                                                         this.loaders);
    registerBootstrapBinder(this.contributors, null, DENY_INACTIVE_BINDING);
    // 执行加载以及解析初始的配置文件
    ConfigDataEnvironmentContributors contributors = processInitial(this.contributors, importer);
    // 处理profiles相关的配置,包括云平台配置及本地环境配置
    ConfigDataActivationContext activationContext = createActivationContext(
        contributors.getBinder(null, BinderOption.FAIL_ON_BIND_TO_INACTIVE_SOURCE));
    contributors = processWithoutProfiles(contributors, importer, activationContext);
    activationContext = withProfiles(contributors, activationContext);
    contributors = processWithProfiles(contributors, importer, activationContext);
    // 将配置数据添加到环境对象中
    applyToEnvironment(contributors, activationContext, importer.getLoadedLocations(),
                       importer.getOptionalLocations());
}

private ConfigDataEnvironmentContributors processInitial(ConfigDataEnvironmentContributors contributors,
                                                         ConfigDataImporter importer) {
    this.logger.trace("Processing initial config data environment contributors without activation context");
    // 调用ConfigDataEnvironmentContributors加载配置
    contributors = contributors.withProcessedImports(importer, null);
    registerBootstrapBinder(contributors, null, DENY_INACTIVE_BINDING);
    return contributors;
}

private ConfigDataEnvironmentContributors processWithoutProfiles(ConfigDataEnvironmentContributors contributors,
			ConfigDataImporter importer, ConfigDataActivationContext activationContext) {
    this.logger.trace("Processing config data environment contributors with initial activation context");
    contributors = contributors.withProcessedImports(importer, activationContext);
    registerBootstrapBinder(contributors, activationContext, DENY_INACTIVE_BINDING);
    return contributors;
}

private ConfigDataEnvironmentContributors processWithProfiles(ConfigDataEnvironmentContributors contributors,
			ConfigDataImporter importer, ConfigDataActivationContext activationContext) {
    this.logger.trace("Processing config data environment contributors with profile activation context");
    contributors = contributors.withProcessedImports(importer, activationContext);
    registerBootstrapBinder(contributors, activationContext, ALLOW_INACTIVE_BINDING);
    return contributors;
}

processInitial、processWithoutProfiles及processWithProfiles的逻辑都类似,会调用ConfigDataEnvironmentContributors#withProcessedImports方法

ConfigDataEnvironmentContributors withProcessedImports(ConfigDataImporter importer,
                                                       ConfigDataActivationContext activationContext) {
    ImportPhase importPhase = ImportPhase.get(activationContext);
    this.logger.trace(LogMessage.format("Processing imports for phase %s. %s", importPhase,
                                        (activationContext != null) ? activationContext : "no activation context"));
    ConfigDataEnvironmentContributors result = this;
    int processed = 0;
    // 遍历所有的ConfigDataEnvironmentContributor
    while (true) {
        ConfigDataEnvironmentContributor contributor = getNextToProcess(result, activationContext, importPhase);
        if (contributor == null) {
            this.logger.trace(LogMessage.format("Processed imports for of %d contributors", processed));
            return result;
        }
        if (contributor.getKind() == Kind.UNBOUND_IMPORT) {
            ConfigDataEnvironmentContributor bound = contributor.withBoundProperties(result, activationContext);
            result = new ConfigDataEnvironmentContributors(this.logger, this.bootstrapContext,
                                                           result.getRoot().withReplacement(contributor, bound));
            continue;
        }
        ConfigDataLocationResolverContext locationResolverContext = new ContributorConfigDataLocationResolverContext(
            result, contributor, activationContext);
        ConfigDataLoaderContext loaderContext = new ContributorDataLoaderContext(this);
        List<ConfigDataLocation> imports = contributor.getImports();
        this.logger.trace(LogMessage.format("Processing imports %s", imports));
        // 执行解析和加载数据
        Map<ConfigDataResolutionResult, ConfigData> imported = importer.resolveAndLoad(activationContext,
                                                                                       locationResolverContext, loaderContext, imports);
        this.logger.trace(LogMessage.of(() -> getImportedMessage(imported.keySet())));
        ConfigDataEnvironmentContributor contributorAndChildren = contributor.withChildren(importPhase,
                                                                                           asContributors(imported));
        result = new ConfigDataEnvironmentContributors(this.logger, this.bootstrapContext,
                                                       result.getRoot().withReplacement(contributor, contributorAndChildren));
        processed++;
    }
}

调用ConfigDataImporter#resolveAndLoad

Map<ConfigDataResolutionResult, ConfigData> resolveAndLoad(ConfigDataActivationContext activationContext,
                                                           ConfigDataLocationResolverContext locationResolverContext, ConfigDataLoaderContext loaderContext,
                                                           List<ConfigDataLocation> locations) {
    try {
        Profiles profiles = (activationContext != null) ? activationContext.getProfiles() : null;
        // 解析配置文件路径
        List<ConfigDataResolutionResult> resolved = resolve(locationResolverContext, profiles, locations);
        // 加载配置文件
        return load(loaderContext, resolved);
    }
    catch (IOException ex) {
        throw new IllegalStateException("IO error on loading imports from " + locations, ex);
    }
}

private Map<ConfigDataResolutionResult, ConfigData> load(ConfigDataLoaderContext loaderContext,
                                                         List<ConfigDataResolutionResult> candidates) throws IOException {
    Map<ConfigDataResolutionResult, ConfigData> result = new LinkedHashMap<>();
    // 从后往前读取
    for (int i = candidates.size() - 1; i >= 0; i--) {
        ConfigDataResolutionResult candidate = candidates.get(i);
        ConfigDataLocation location = candidate.getLocation();
        ConfigDataResource resource = candidate.getResource();
        this.logger.trace(LogMessage.format("Considering resource %s from location %s", resource, location));
        if (resource.isOptional()) {
            this.optionalLocations.add(location);
        }
        if (this.loaded.contains(resource)) {
            this.logger
            .trace(LogMessage.format("Already loaded resource %s ignoring location %s", resource, location));
            this.loadedLocations.add(location);
        }
        else {
            try {
                // 加载配置文件
                ConfigData loaded = this.loaders.load(loaderContext, resource);
                if (loaded != null) {
                    this.logger.trace(LogMessage.format("Loaded resource %s from location %s", resource, location));
                    this.loaded.add(resource);
                    this.loadedLocations.add(location);
                    result.put(candidate, loaded);
                }
            }
            catch (ConfigDataNotFoundException ex) {
                handle(ex, location, resource);
            }
        }
    }
    return Collections.unmodifiableMap(result);
}

读取文件是从最后一个开始读取,所以配置优先级顺序从高到低为:

file:./config/*/
file:./config/
file:./
classpath:/config/
classpath:/

这里通过StandardConfigDataLoader读取配置文件,StandardConfigDataLoader#load源码如下:

public ConfigData load(ConfigDataLoaderContext context, StandardConfigDataResource resource)
throws IOException, ConfigDataNotFoundException {
    if (resource.isEmptyDirectory()) {
        return ConfigData.EMPTY;
    }
    ConfigDataResourceNotFoundException.throwIfDoesNotExist(resource, resource.getResource());
    StandardConfigDataReference reference = resource.getReference();
    Resource originTrackedResource = OriginTrackedResource.of(resource.getResource(),
                                                              Origin.from(reference.getConfigDataLocation()));
    String name = String.format("Config resource '%s' via location '%s'", resource,
                                reference.getConfigDataLocation());
    // 调用PropertySourceLoader接口加载配置文件
    List<PropertySource<?>> propertySources = reference.getPropertySourceLoader().load(name, originTrackedResource);
    PropertySourceOptions options = (resource.getProfile() != null) ? PROFILE_SPECIFIC : NON_PROFILE_SPECIFIC;
    return new ConfigData(propertySources, options);
}

PropertySourceLoader接口的实现类有PropertiesPropertySourceLoader和YamlPropertySourceLoader,其中PropertiesPropertySourceLoader可以加载properties和xml格式的配置文件,YamlPropertySourceLoader可以加载yml及yaml格式的配置文件

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/600731.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

HackMyVM-Slowman

目录 信息收集 arp nmap whatweb WEB web信息收集 gobuster FTP匿名登录 hydra mysql爆破 mysql登录 fcrackzip爆破 hashcat爆破 ssh登录 提权 系统信息收集 python Capabilities提权 信息收集 arp ┌──(root㉿0x00)-[~/HackMyVM] └─# arp-scan -l Interf…

【Java 刷题记录】前缀和

前缀和 25. 一维前缀和 示例1&#xff1a; 输入&#xff1a; 3 2 1 2 4 1 2 2 3输出&#xff1a; 3 6import java.util.Scanner;// 注意类名必须为 Main, 不要有任何 package xxx 信息 public class Main {public static void main(String[] args) {Scanner in new Scanner(S…

信创 | 信创产业数字化转型与升级:路径规划与实践!

信创产业的数字化转型与升级路径&#xff0c;主要围绕着构建国产化信息技术软硬件底层架构体系和全周期生态体系&#xff0c;解决核心技术关键环节“卡脖子”的问题&#xff0c;以推动中国经济数字化转型的平稳健康发展。 一、信创产业的发展趋势包括&#xff1a; 加强国产信息…

️测试问我:为啥阅读量计数这么简单的功能你都能写出bug?

前言 可乐他们团队最近在做一个文章社区平台,由于人手不够,后端部分也是由前端同学来实现,使用的是 nest 。 今天他接到了一个需求,就是在用户点开文章详情的时候,把阅读量 +1 ,这里不需要判断用户是否阅读过,无脑 +1 就行。 它心想:这么简单,这不是跟 1+1 一样么。…

使用pandas的merge()和join()函数进行数据处理

目录 一、引言 二、pandas的merge()函数 基本用法 实战案例 三、pandas的join()函数 基本用法 实战案例 四、merge()与join()的比较与选择 使用场景&#xff1a; 灵活性&#xff1a; 选择建议&#xff1a; 五、进阶案例与代码 六、总结 一、引言 在数据分析和处理…

领航法律科技,法大大多年深耕再获认可!

近日&#xff0c;“乘势破局 第八届新兴法律服务业高峰论坛”在上海隆重举行。作为国内领先的电子签厂商&#xff0c;法大大凭借在法律科技领域的多年深耕与沉淀&#xff0c;荣获“法律科技领航机构”称号。 据悉&#xff0c;新兴法律服务业高峰论坛作为国内首个聚焦“新兴法律…

董事长张轶群刚被罚,合规问题屡见不鲜,富友支付IPO胜算几何?

第三方支付机构富友支付又双叒来冲刺上市了。 与此前两次冲刺A股不同的是&#xff0c;富友支付此次选择在港股上市。近日&#xff0c;富友支付向港交所主板递交上市申请&#xff0c;联席保荐人为中信证券、申万宏源香港。值得一提的是&#xff0c;此前的2018年、2021年&#x…

网络基础——路由

网络基础——路由 要想网络畅通&#xff0c;应让网络中的路由器知道如何转发数据包到各个网段。路由器根据路由表来转发数据包&#xff0c;而路由表是通过直连网络、静态路由以及动态路由来构建的。 route命令&#xff0c;底层是使用ioctl实现&#xff1b;ip命令&#xff0c;…

Misc 流量分析

流量分析简介 网络流量分析是指捕捉网络中流动的数据包&#xff0c;并通过查看包内部数据以及进行相关的协议、流量分析、统计等来发现网络运行过程中出现的问题。 在CTF比赛中&#xff0c;以及各种技能大赛对于流量包的分析取证是一种十分重要的题型。通常这类题目都是会提供…

Java | Leetcode Java题解之第66题加一

题目&#xff1a; 题解&#xff1a; class Solution {public int[] plusOne(int[] digits) {int n digits.length;for (int i n - 1; i > 0; --i) {if (digits[i] ! 9) {digits[i];for (int j i 1; j < n; j) {digits[j] 0;}return digits;}}// digits 中所有的元素…

【牛客】【模板】差分

原题链接&#xff1a;登录—专业IT笔试面试备考平台_牛客网 目录 1. 题目描述 2. 思路分析 3. 代码实现 1. 题目描述 2. 思路分析 差分模板。 b[0]a[0]; b[1]a[1]-a[0]; b[2]a[2]-a[1]; ...... b[n-1]a[n-1]-a[n-2]; b[n]a[n]-a[n-1]; 差分标记&#xff1a;b[l]k,b…

2024年荆州中级工程师报名开始了吗?

2024年荆州中级工程师职称报名已经开始了 2024年荆州中级职称报名时间&#xff1a; &#xff08;一&#xff09;网上报名时间&#xff1a; 4月26日9时至5月10日16时。超过时间将不能操作。 &#xff08;二&#xff09;网上缴费时间&#xff1a; 4月26日9时至5月10日24时 网上…

(五)JVM实战——JVM性能调优与监控

JVM调优案例的场景 为什么要调优&#xff1a;防止或者解决jvm虚拟机中的OOM问题&#xff1b;减少FullGC出现的频率&#xff0c;解决系统运行卡、慢问题JVM调优案例的四个方面 OOM(堆溢出)&#xff1a;java heap spaceOOM(元空间溢出)&#xff1a;MetaspaceOOM(GC overhead lim…

分析错误ValueError: could not determine the shape of object type ‘Series‘

这个错误提示 ValueError: could not determine the shape of object type Series 通常发生在尝试将 pandas 的 Series 直接转换为 PyTorch 的 tensor 时&#xff0c;尤其是当 Series 的数据类型不明确或者包含非数值类型的数据时。为了修正这个问题&#xff0c;确保在转换之前…

利用Jenkins完成Android项目打包

问题和思路 目前存在的问题 打包操作由开发人员完成&#xff0c;这样开发进度容易被打断。 解决问题的思路 将打包操作交测试/产品/开发人员来完成&#xff0c;主要是测试/开发。 按照以上的思路&#xff0c;那么JenkinsGradle的解决方案是比较经济的&#xff0c;实现起来…

跟随Facebook的足迹:社交媒体背后的探索之旅

在当今数字化时代&#xff0c;社交媒体已经成为了人们日常生活中不可或缺的一部分。而在这庞大的社交媒体网络中&#xff0c;Facebook作为其中的巨头&#xff0c;一直在引领着潮流。从创立之初的一个大学社交网络到如今的全球性平台&#xff0c;Facebook的发展历程承载了无数故…

雷军-2022.8小米创业思考-6-互联网七字诀之专注:有所为,有所不为;克制贪婪,少就是多;一次解决一个最迫切的需求

第六章 互联网七字诀 专注、极致、口碑、快&#xff0c;这就是我总结的互联网七字诀&#xff0c;也是我对互联网思维的高度概括。 专注 从商业角度看&#xff0c;专注就是要“把鸡蛋尽量放在一个篮子里”。这听起来似乎有些不合理&#xff0c;大家的第一反应可能是“风险会不会…

stripe支付

使用第一个示例 1、示例中的PRICE_ID需要去Stripe控制台->产品目录创建产品 1、 添加产品 2、点击查看创建的产品详情 4、这个API ID就是demo中的PRICE_ID 注意&#xff1a;需要注意的是&#xff0c;测试模式和生产模式中的 $stripeSecretKey 需要对应上。简而言之就是不能生…

【嵌入式必读】一文彻底理解PID自整定及PID自整定代码设计

文章目录 1. 前言2. PID简介3. 常用的PID自整定方法3.1 临界度比例法3.2 衰减曲线法 4. 继电反馈整定法原理4.1 继电反馈自整定的基本思想4.2 继电反馈自整定原理 5. 算法设计5.1 振荡的生成5.2 提取出临界周期 T c T_c Tc​和振荡波形幅值 A A A5.3 计算出PID参数 6 原代码6.1…

【Linux】Docker 安装部署 Nacos

个人简介&#xff1a;Java领域新星创作者&#xff1b;阿里云技术博主、星级博主、专家博主&#xff1b;正在Java学习的路上摸爬滚打&#xff0c;记录学习的过程~ 个人主页&#xff1a;.29.的博客 学习社区&#xff1a;进去逛一逛~ 【Linux】Docker 安装部署 Nacos docker搜索na…
最新文章