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

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

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

java 复制代码
@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源码如下:

java 复制代码
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对象的源码如下:

java 复制代码
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对象源码如下:

java 复制代码
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的实例

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

2.2 读取系统配置

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

java 复制代码
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加载系统配置

java 复制代码
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

java 复制代码
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

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

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

java 复制代码
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

java 复制代码
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)方法内部

java 复制代码
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类名,然后实例化对象

java 复制代码
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也会被创建,源码如下:

java 复制代码
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对象会被创建,源码如下:

java 复制代码
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中,源码如下:

java 复制代码
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

java 复制代码
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事件

java 复制代码
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

java 复制代码
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

java 复制代码
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时获取配置信息

java 复制代码
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

java 复制代码
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源码如下:

java 复制代码
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,这决定了配置文件的加载位置及顺序

java 复制代码
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包含:

java 复制代码
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加载和解析配置文件,源码如下:

java 复制代码
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方法

java 复制代码
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

java 复制代码
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源码如下:

java 复制代码
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格式的配置文件

相关推荐
夜半被帅醒几秒前
MySQL 数据库优化详解【Java数据库调优】
java·数据库·mysql
万亿少女的梦1686 分钟前
基于Spring Boot的网络购物商城的设计与实现
java·spring boot·后端
醒了就刷牙34 分钟前
黑马Java面试教程_P9_MySQL
java·mysql·面试
m0_7482336440 分钟前
SQL数组常用函数记录(Map篇)
java·数据库·sql
编程爱好者熊浪2 小时前
JAVA HTTP压缩数据
java
吴冰_hogan2 小时前
JVM(Java虚拟机)的组成部分详解
java·开发语言·jvm
白宇横流学长3 小时前
基于java出租车计价器设计与实现【源码+文档+部署讲解】
java·开发语言
数据小爬虫@5 小时前
Java爬虫实战:深度解析Lazada商品详情
java·开发语言
咕德猫宁丶5 小时前
探秘Xss:原理、类型与防范全解析
java·网络·xss
F-2H6 小时前
C语言:指针4(常量指针和指针常量及动态内存分配)
java·linux·c语言·开发语言·前端·c++