or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

admin-jmx.mdansi-support.mdaot-native-image.mdapplication-info.mdavailability.mdbootstrap.mdbootstrapping.mdbuilder.mdcloud-platform.mdconfiguration-annotations.mdconfiguration-data.mdconfiguration-properties.mdconversion.mddiagnostics.mdenvironment-property-sources.mdindex.mdjson-support.mdlifecycle-events.mdlogging.mdorigin-tracking.mdresource-loading.mdretry-support.mdssl-tls.mdstartup-metrics.mdsupport.mdsystem-utilities.mdtask-execution.mdthreading.mdutilities.mdvalidation.mdweb-support.md
tile.json

configuration-data.mddocs/

Spring Boot Configuration Data Loading

Quick Reference

This documentation has been enhanced for AI coding agents with comprehensive examples, complete API signatures, thread safety notes, error handling patterns, and production-ready usage patterns.

Core ConfigData Components

ComponentPurposeThread SafetyKey Features
ConfigDataContainer for loaded configurationThread-safe (immutable)Property sources, options, profile-specific data
ConfigDataLocationUser-specified locationThread-safe (immutable)Wildcards, optional locations, profile-specific
ConfigDataResourceResolved loadable resourceThread-safe (immutable)Origin tracking, profile-specific handling
ConfigDataLocationResolverResolve locations to resourcesThread-safeCustom location resolution, profile support
ConfigDataLoaderLoad data from resourcesThread-safeYAML, properties, custom formats
ConfigDataEnvironmentPostProcessorOrchestrates loadingThread-safeEntry point for ConfigData system
ProfilesManages active/default profilesThread-safe (immutable)Profile checking, iteration, active/default separation
ConfigDataLoaderContextContext for loadersThread-safeBootstrap context access
ConfigDataLocationResolverContextContext for resolversThread-safeBinder, parent resource, bootstrap context
ConfigDataEnvironmentUpdateListenerTracks environment updatesThread-safeProperty source tracking, profile change notification
ConfigDataNotFoundActionAction on missing configThread-safe (enum)FAIL or IGNORE missing configuration

ConfigData Options

OptionPurposeThread SafetyBehavior
PROFILE_SPECIFICProfile-specific configThread-safe (enum)Only loaded for active profiles
IGNORE_IMPORTSSkip import processingThread-safe (enum)Don't process spring.config.import
IGNORE_PROFILESSkip profile activationThread-safe (enum)Don't activate profiles from this source

Built-in Location Resolvers

ResolverSupported LocationsThread SafetyFeatures
StandardConfigDataLocationResolverclasspath:, file:, optional:Thread-safeStandard file system and classpath loading
ConfigTreeConfigDataLocationResolverconfigtree:Thread-safeKubernetes ConfigMap/Secret directories

Built-in Loaders

LoaderSupported ResourcesThread SafetyFormats
StandardConfigDataLoaderFile-based resourcesThread-safe.properties, .xml, .yaml, .yml
ConfigTreeConfigDataLoaderConfig tree resourcesThread-safeDirectory-based key-value pairs

ConfigData Exceptions

ExceptionPurposeWhen ThrownRecovery
ConfigDataNotFoundExceptionResource not foundLoading missing required configMake location optional: optional:classpath:/config.yml
ConfigDataResourceNotFoundExceptionSpecific resource missingResolver can't find resourceCheck file path, make optional
ConfigDataNotFoundActionAction on not foundConfigurationFAIL (default) or IGNORE

This document covers Spring Boot's configuration data loading mechanism, which provides a flexible and extensible way to load configuration from various sources during application startup.

Overview

Spring Boot's configuration data loading system is built around several key abstractions:

  • ConfigData: Represents configuration data loaded from a resource
  • ConfigDataLocation: A user-specified location string (e.g., "classpath:/application.yml")
  • ConfigDataResource: A resolved resource that can be loaded
  • ConfigDataLocationResolver: Resolves locations into resources
  • ConfigDataLoader: Loads configuration data from resources
  • ConfigDataEnvironmentPostProcessor: Orchestrates the entire loading process

Core Types

ConfigData

ConfigData represents configuration data that has been loaded and may contribute property sources to Spring's Environment.

package org.springframework.boot.context.config;

import java.util.Collection;
import java.util.List;
import org.springframework.core.env.PropertySource;

/**
 * Configuration data that has been loaded from a ConfigDataResource and may
 * ultimately contribute PropertySource instances to Spring's Environment.
 *
 * @since 2.4.0
 */
public final class ConfigData {

    /**
     * A ConfigData instance that contains no data.
     */
    public static final ConfigData EMPTY = new ConfigData(Collections.emptySet());

    /**
     * Create a new ConfigData instance with the same options applied to each source.
     *
     * @param propertySources the config data property sources in ascending priority order
     * @param options the config data options applied to each source
     */
    public ConfigData(Collection<? extends PropertySource<?>> propertySources,
                      Option... options);

    /**
     * Create a new ConfigData instance with specific property source options.
     *
     * @param propertySources the config data property sources in ascending priority order
     * @param propertySourceOptions the property source options
     * @since 2.4.5
     */
    public ConfigData(Collection<? extends PropertySource<?>> propertySources,
                      PropertySourceOptions propertySourceOptions);

    /**
     * Return the configuration data property sources in ascending priority order.
     * If the same key is contained in more than one of the sources, then the later
     * source will win.
     *
     * @return the config data property sources
     */
    public List<PropertySource<?>> getPropertySources();

    /**
     * Return the config data options that apply to the given source.
     *
     * @param propertySource the property source to check
     * @return the options that apply
     * @since 2.4.5
     */
    public Options getOptions(PropertySource<?> propertySource);

    /**
     * Option flags that can be applied.
     */
    public enum Option {
        /**
         * Ignore all imports properties from the source.
         */
        IGNORE_IMPORTS,

        /**
         * Ignore all profile activation and include properties.
         * @since 2.4.3
         */
        IGNORE_PROFILES,

        /**
         * Indicates that the source is "profile specific" and should be included
         * after profile specific sibling imports.
         * @since 2.4.5
         */
        PROFILE_SPECIFIC
    }

    /**
     * A set of Option flags.
     * @since 2.4.5
     */
    public static final class Options {
        /**
         * No options.
         */
        public static final Options NONE = new Options(Collections.emptySet());

        /**
         * Returns if the given option is contained in this set.
         *
         * @param option the option to check
         * @return true if the option is present
         */
        public boolean contains(Option option);

        /**
         * Create a new Options instance that contains the options in this set
         * excluding the given option.
         *
         * @param option the option to exclude
         * @return a new Options instance
         */
        public Options without(Option option);

        /**
         * Create a new Options instance that contains the options in this set
         * including the given option.
         *
         * @param option the option to include
         * @return a new Options instance
         */
        public Options with(Option option);

        /**
         * Create a new instance with the given Option values.
         *
         * @param options the options to include
         * @return a new Options instance
         */
        public static Options of(Option... options);
    }

    /**
     * Strategy interface used to supply Options for a given PropertySource.
     * @since 2.4.5
     */
    @FunctionalInterface
    public interface PropertySourceOptions {
        /**
         * PropertySourceOptions instance that always returns Options.NONE.
         * @since 2.4.6
         */
        PropertySourceOptions ALWAYS_NONE = /* implementation */;

        /**
         * Return the options that should apply for the given property source.
         *
         * @param propertySource the property source
         * @return the options to apply
         */
        Options get(PropertySource<?> propertySource);

        /**
         * Create a new PropertySourceOptions instance that always returns the
         * same options regardless of the property source.
         *
         * @param options the options to return
         * @return a new PropertySourceOptions instance
         */
        static PropertySourceOptions always(Option... options);

        /**
         * Create a new PropertySourceOptions instance that always returns the
         * same options regardless of the property source.
         *
         * @param options the options to return
         * @return a new PropertySourceOptions instance
         */
        static PropertySourceOptions always(Options options);
    }
}

ConfigDataLocation

ConfigDataLocation represents a user-specified location that can be resolved to one or more resources.

package org.springframework.boot.context.config;

import org.springframework.boot.origin.Origin;
import org.springframework.boot.origin.OriginProvider;

/**
 * A user specified location that can be resolved to one or more config data resources.
 * A ConfigDataLocation is a simple wrapper around a String value. The exact format of
 * the value will depend on the underlying technology, but is usually a URL-like syntax
 * consisting of a prefix and path. For example, "crypt:somehost/somepath".
 *
 * Locations can be mandatory or optional. Optional locations are prefixed with "optional:".
 *
 * @since 2.4.0
 */
public final class ConfigDataLocation implements OriginProvider {

    /**
     * Prefix used to indicate that a ConfigDataResource is optional.
     */
    public static final String OPTIONAL_PREFIX = "optional:";

    /**
     * Return if the location is optional and should ignore ConfigDataNotFoundException.
     *
     * @return if the location is optional
     */
    public boolean isOptional();

    /**
     * Return the value of the location (always excluding any user specified
     * "optional:" prefix).
     *
     * @return the location value
     */
    public String getValue();

    /**
     * Return if getValue() has the specified prefix.
     *
     * @param prefix the prefix to check
     * @return if the value has the prefix
     */
    public boolean hasPrefix(String prefix);

    /**
     * Return getValue() with the specified prefix removed. If the location does
     * not have the given prefix then the getValue() is returned unchanged.
     *
     * @param prefix the prefix to check
     * @return the value with the prefix removed
     */
    public String getNonPrefixedValue(String prefix);

    @Override
    public Origin getOrigin();

    /**
     * Return an array of ConfigDataLocation elements built by splitting this
     * ConfigDataLocation around a delimiter of ";".
     *
     * @return the split locations
     * @since 2.4.7
     */
    public ConfigDataLocation[] split();

    /**
     * Return an array of ConfigDataLocation elements built by splitting this
     * ConfigDataLocation around the specified delimiter.
     *
     * @param delimiter the delimiter to split on
     * @return the split locations
     * @since 2.4.7
     */
    public ConfigDataLocation[] split(String delimiter);

    /**
     * Factory method to create a new ConfigDataLocation from a string.
     *
     * @param location the location string
     * @return the ConfigDataLocation (which may be empty)
     */
    public static ConfigDataLocation of(String location);
}

ConfigDataResource

ConfigDataResource represents a single resource from which configuration data can be loaded.

package org.springframework.boot.context.config;

/**
 * A single resource from which ConfigData can be loaded. Implementations must
 * implement a valid equals(), hashCode(), and toString() methods.
 *
 * @since 2.4.0
 */
public abstract class ConfigDataResource {

    /**
     * Create a new non-optional ConfigDataResource instance.
     */
    public ConfigDataResource();

    /**
     * Create a new ConfigDataResource instance.
     *
     * @param optional if the resource is optional
     * @since 2.4.6
     */
    protected ConfigDataResource(boolean optional);
}

ConfigDataLocationResolver

ConfigDataLocationResolver resolves locations into resources.

package org.springframework.boot.context.config;

import java.util.List;
import org.springframework.boot.bootstrap.BootstrapContext;
import org.springframework.boot.bootstrap.BootstrapRegistry;
import org.springframework.boot.bootstrap.ConfigurableBootstrapContext;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.logging.DeferredLogFactory;
import org.springframework.core.io.ResourceLoader;

/**
 * Strategy interface used to resolve ConfigDataLocation locations into one or more
 * ConfigDataResource resources. Implementations should be added as spring.factories
 * entries.
 *
 * The following constructor parameter types are supported:
 * - DeferredLogFactory - if the resolver needs deferred logging
 * - Binder - if the resolver needs to obtain values from the initial Environment
 * - ResourceLoader - if the resolver needs a resource loader
 * - ConfigurableBootstrapContext - A bootstrap context that can be used to store
 *   objects that may be expensive to create, or need to be shared (BootstrapContext
 *   or BootstrapRegistry may also be used)
 *
 * Resolvers may implement Ordered or use the @Order annotation. The first resolver
 * that supports the given location will be used.
 *
 * @param <R> the resource type
 * @since 2.4.0
 */
public interface ConfigDataLocationResolver<R extends ConfigDataResource> {

    /**
     * Returns if the specified location address can be resolved by this resolver.
     *
     * @param context the location resolver context
     * @param location the location to check
     * @return if the location is supported by this resolver
     */
    boolean isResolvable(ConfigDataLocationResolverContext context,
                        ConfigDataLocation location);

    /**
     * Resolve a ConfigDataLocation into one or more ConfigDataResource instances.
     *
     * @param context the location resolver context
     * @param location the location that should be resolved
     * @return a list of ConfigDataResource resources in ascending priority order
     * @throws ConfigDataLocationNotFoundException on a non-optional location that
     *         cannot be found
     * @throws ConfigDataResourceNotFoundException if a resolved resource cannot be found
     */
    List<R> resolve(ConfigDataLocationResolverContext context,
                   ConfigDataLocation location)
            throws ConfigDataLocationNotFoundException, ConfigDataResourceNotFoundException;

    /**
     * Resolve a ConfigDataLocation into one or more ConfigDataResource instances
     * based on available profiles. This method is called once profiles have been
     * deduced from the contributed values. By default this method returns an empty list.
     *
     * @param context the location resolver context
     * @param location the location that should be resolved
     * @param profiles profile information
     * @return a list of resolved locations in ascending priority order
     * @throws ConfigDataLocationNotFoundException on a non-optional location that
     *         cannot be found
     */
    default List<R> resolveProfileSpecific(ConfigDataLocationResolverContext context,
                                          ConfigDataLocation location,
                                          Profiles profiles)
            throws ConfigDataLocationNotFoundException;
}

ConfigDataLoader

ConfigDataLoader loads configuration data from resources.

package org.springframework.boot.context.config;

import java.io.IOException;
import org.springframework.boot.bootstrap.BootstrapContext;
import org.springframework.boot.bootstrap.BootstrapRegistry;
import org.springframework.boot.bootstrap.ConfigurableBootstrapContext;
import org.springframework.boot.logging.DeferredLogFactory;

/**
 * Strategy class that can be used to load ConfigData for a given ConfigDataResource.
 * Implementations should be added as spring.factories entries.
 *
 * The following constructor parameter types are supported:
 * - DeferredLogFactory - if the loader needs deferred logging
 * - ConfigurableBootstrapContext - A bootstrap context that can be used to store
 *   objects that may be expensive to create, or need to be shared (BootstrapContext
 *   or BootstrapRegistry may also be used)
 *
 * Multiple loaders cannot claim the same resource.
 *
 * @param <R> the resource type
 * @since 2.4.0
 */
public interface ConfigDataLoader<R extends ConfigDataResource> {

    /**
     * Returns if the specified resource can be loaded by this instance.
     *
     * @param context the loader context
     * @param resource the resource to check
     * @return if the resource is supported by this loader
     */
    default boolean isLoadable(ConfigDataLoaderContext context, R resource);

    /**
     * Load ConfigData for the given resource.
     *
     * @param context the loader context
     * @param resource the resource to load
     * @return the loaded config data or null if the location should be skipped
     * @throws IOException on IO error
     * @throws ConfigDataResourceNotFoundException if the resource cannot be found
     */
    ConfigData load(ConfigDataLoaderContext context, R resource)
            throws IOException, ConfigDataResourceNotFoundException;
}

ConfigDataEnvironmentPostProcessor

ConfigDataEnvironmentPostProcessor orchestrates the configuration data loading process.

package org.springframework.boot.context.config;

import java.util.Collection;
import org.springframework.boot.EnvironmentPostProcessor;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.bootstrap.ConfigurableBootstrapContext;
import org.springframework.core.Ordered;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.io.ResourceLoader;

/**
 * EnvironmentPostProcessor that loads and applies ConfigData to Spring's Environment.
 *
 * @since 2.4.0
 */
public class ConfigDataEnvironmentPostProcessor
        implements EnvironmentPostProcessor, Ordered {

    /**
     * The default order for the processor.
     */
    public static final int ORDER = Ordered.HIGHEST_PRECEDENCE + 10;

    /**
     * Property used to determine what action to take when a
     * ConfigDataLocationNotFoundException is thrown.
     *
     * @see ConfigDataNotFoundAction
     */
    public static final String ON_LOCATION_NOT_FOUND_PROPERTY =
            "spring.config.on-not-found";

    /**
     * Constructor with deferred logging factory and bootstrap context.
     *
     * @param logFactory the deferred log factory
     * @param bootstrapContext the bootstrap context
     */
    public ConfigDataEnvironmentPostProcessor(DeferredLogFactory logFactory,
                                            ConfigurableBootstrapContext bootstrapContext);

    @Override
    public int getOrder();

    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment,
                                      SpringApplication application);

    /**
     * Apply ConfigData post-processing to an existing Environment. This method can
     * be useful when working with an Environment that has been created directly and
     * not necessarily as part of a SpringApplication.
     *
     * @param environment the environment to apply ConfigData to
     */
    public static void applyTo(ConfigurableEnvironment environment);

    /**
     * Apply ConfigData post-processing to an existing Environment. This method can
     * be useful when working with an Environment that has been created directly and
     * not necessarily as part of a SpringApplication.
     *
     * @param environment the environment to apply ConfigData to
     * @param resourceLoader the resource loader to use
     * @param bootstrapContext the bootstrap context to use or null to use a throw-away
     *        context
     * @param additionalProfiles any additional profiles that should be applied
     */
    public static void applyTo(ConfigurableEnvironment environment,
                              ResourceLoader resourceLoader,
                              ConfigurableBootstrapContext bootstrapContext,
                              String... additionalProfiles);

    /**
     * Apply ConfigData post-processing to an existing Environment. This method can
     * be useful when working with an Environment that has been created directly and
     * not necessarily as part of a SpringApplication.
     *
     * @param environment the environment to apply ConfigData to
     * @param resourceLoader the resource loader to use
     * @param bootstrapContext the bootstrap context to use or null to use a throw-away
     *        context
     * @param additionalProfiles any additional profiles that should be applied
     */
    public static void applyTo(ConfigurableEnvironment environment,
                              ResourceLoader resourceLoader,
                              ConfigurableBootstrapContext bootstrapContext,
                              Collection<String> additionalProfiles);

    /**
     * Apply ConfigData post-processing to an existing Environment with an update listener.
     *
     * @param environment the environment to apply ConfigData to
     * @param resourceLoader the resource loader to use
     * @param bootstrapContext the bootstrap context to use or null to use a throw-away
     *        context
     * @param additionalProfiles any additional profiles that should be applied
     * @param environmentUpdateListener optional ConfigDataEnvironmentUpdateListener
     *        that can be used to track Environment updates
     */
    public static void applyTo(ConfigurableEnvironment environment,
                              ResourceLoader resourceLoader,
                              ConfigurableBootstrapContext bootstrapContext,
                              Collection<String> additionalProfiles,
                              ConfigDataEnvironmentUpdateListener environmentUpdateListener);
}

Profiles

Profiles provides access to environment profiles that have been set directly or will be set based on configuration data.

package org.springframework.boot.context.config;

import java.util.Iterator;
import java.util.List;

/**
 * Provides access to environment profiles that have either been set directly on the
 * Environment or will be set based on configuration data property values.
 *
 * @since 2.4.0
 */
public class Profiles implements Iterable<String> {

    /**
     * Name of property to set to specify additionally included active profiles.
     */
    public static final String INCLUDE_PROFILES_PROPERTY_NAME = "spring.profiles.include";

    /**
     * Return an iterator for all accepted profiles.
     *
     * @return an iterator over accepted profiles
     */
    @Override
    public Iterator<String> iterator();

    /**
     * Return the active profiles.
     *
     * @return the active profiles (never null)
     */
    public List<String> getActive();

    /**
     * Return the default profiles.
     *
     * @return the default profiles (never null)
     */
    public List<String> getDefault();

    /**
     * Return the accepted profiles. This is the union of active and default profiles.
     *
     * @return the accepted profiles (never null)
     */
    public List<String> getAccepted();

    /**
     * Return if the given profile is active (accepted).
     *
     * @param profile the profile to test
     * @return true if the profile is active
     */
    public boolean isAccepted(String profile);
}

ConfigDataLoaderContext

ConfigDataLoaderContext provides context to ConfigDataLoader methods, mainly for accessing the bootstrap context.

package org.springframework.boot.context.config;

import org.springframework.boot.bootstrap.ConfigurableBootstrapContext;

/**
 * Context provided to ConfigDataLoader methods.
 *
 * @since 2.4.0
 */
public interface ConfigDataLoaderContext {

    /**
     * Provides access to the ConfigurableBootstrapContext shared across all
     * EnvironmentPostProcessors.
     *
     * @return the bootstrap context (never null)
     */
    ConfigurableBootstrapContext getBootstrapContext();
}

ConfigDataLocationResolverContext

ConfigDataLocationResolverContext provides context to ConfigDataLocationResolver methods, including access to the binder, parent resource, and bootstrap context.

package org.springframework.boot.context.config;

import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.bootstrap.ConfigurableBootstrapContext;
import org.springframework.lang.Nullable;

/**
 * Context provided to ConfigDataLocationResolver methods.
 *
 * @since 2.4.0
 */
public interface ConfigDataLocationResolverContext {

    /**
     * Provides access to a binder that can be used to obtain previously contributed
     * values.
     *
     * @return a binder instance (never null)
     */
    Binder getBinder();

    /**
     * Provides access to the parent ConfigDataResource that triggered the resolve
     * or null if there is no available parent.
     *
     * @return the parent resource or null
     */
    @Nullable
    ConfigDataResource getParent();

    /**
     * Provides access to the ConfigurableBootstrapContext shared across all
     * EnvironmentPostProcessors.
     *
     * @return the bootstrap context (never null)
     */
    ConfigurableBootstrapContext getBootstrapContext();
}

ConfigDataEnvironmentUpdateListener

ConfigDataEnvironmentUpdateListener is an event listener interface for tracking Environment updates triggered by ConfigDataEnvironmentPostProcessor.

package org.springframework.boot.context.config;

import java.util.EventListener;
import org.springframework.core.env.PropertySource;
import org.springframework.lang.Nullable;

/**
 * EventListener to listen to Environment updates triggered by the
 * ConfigDataEnvironmentPostProcessor.
 *
 * @since 2.4.2
 */
public interface ConfigDataEnvironmentUpdateListener extends EventListener {

    /**
     * A ConfigDataEnvironmentUpdateListener that does nothing.
     * Useful as a null-object implementation.
     */
    ConfigDataEnvironmentUpdateListener NONE = new ConfigDataEnvironmentUpdateListener() {};

    /**
     * Called when a new PropertySource is added to the Environment.
     *
     * @param propertySource the PropertySource that was added
     * @param location the original ConfigDataLocation of the source (may be null)
     * @param resource the ConfigDataResource of the source (may be null)
     */
    default void onPropertySourceAdded(PropertySource<?> propertySource,
                                      @Nullable ConfigDataLocation location,
                                      @Nullable ConfigDataResource resource) {
    }

    /**
     * Called when Environment profiles are set.
     *
     * @param profiles the profiles being set
     */
    default void onSetProfiles(Profiles profiles) {
    }

    /**
     * Called when config data options are obtained for a particular property source.
     * Allows modification of options before they are used.
     *
     * @param configData the config data
     * @param propertySource the property source
     * @param options the options as provided by ConfigData.getOptions(PropertySource)
     * @return the actual options that should be used (return the input options if no changes needed)
     * @since 3.5.1
     */
    default ConfigData.Options onConfigDataOptions(ConfigData configData,
                                                   PropertySource<?> propertySource,
                                                   ConfigData.Options options) {
        return options;
    }
}

ConfigDataNotFoundAction

ConfigDataNotFoundAction determines what action to take when an uncaught ConfigDataNotFoundException is thrown.

package org.springframework.boot.context.config;

import org.apache.commons.logging.Log;

/**
 * Action to take when an uncaught ConfigDataNotFoundException is thrown.
 *
 * @since 2.4.0
 */
public enum ConfigDataNotFoundAction {

    /**
     * Throw the exception to fail startup.
     * This is the default behavior.
     */
    FAIL,

    /**
     * Ignore the exception and continue processing the remaining locations.
     * Use this to make configuration locations optional without using the
     * "optional:" prefix.
     */
    IGNORE
}

Usage:

Set the action via the spring.config.on-not-found property (referenced by ConfigDataEnvironmentPostProcessor.ON_LOCATION_NOT_FOUND_PROPERTY):

# Continue processing even if required config is not found
spring.config.on-not-found=ignore

Or in YAML:

spring:
  config:
    on-not-found: ignore

Built-in Loaders

StandardConfigDataLoader

StandardConfigDataLoader loads standard configuration files using property source loaders.

package org.springframework.boot.context.config;

import java.io.IOException;
import org.springframework.core.io.Resource;

/**
 * ConfigDataLoader for Resource backed locations.
 *
 * @since 2.4.0
 */
public class StandardConfigDataLoader
        implements ConfigDataLoader<StandardConfigDataResource> {

    /**
     * Load configuration data from the given resource.
     *
     * @param context the loader context
     * @param resource the resource to load
     * @return the loaded ConfigData
     * @throws IOException on IO error
     * @throws ConfigDataNotFoundException if the resource cannot be found
     */
    @Override
    public ConfigData load(ConfigDataLoaderContext context,
                          StandardConfigDataResource resource)
            throws IOException, ConfigDataNotFoundException;
}

ConfigTreeConfigDataLoader

ConfigTreeConfigDataLoader loads configuration from config tree structures (e.g., Kubernetes ConfigMaps).

package org.springframework.boot.context.config;

import java.io.IOException;
import java.nio.file.Path;

/**
 * ConfigDataLoader for config tree locations. Config trees are directories where
 * each file represents a property, useful for Kubernetes ConfigMaps and Secrets.
 *
 * @since 2.4.0
 */
public class ConfigTreeConfigDataLoader
        implements ConfigDataLoader<ConfigTreeConfigDataResource> {

    /**
     * Load configuration data from the given config tree resource.
     *
     * @param context the loader context
     * @param resource the config tree resource to load
     * @return the loaded ConfigData
     * @throws IOException on IO error
     * @throws ConfigDataResourceNotFoundException if the resource cannot be found
     */
    @Override
    public ConfigData load(ConfigDataLoaderContext context,
                          ConfigTreeConfigDataResource resource)
            throws IOException, ConfigDataResourceNotFoundException;
}

Exceptions

Configuration data loading defines several exception types for error handling.

ConfigDataException

package org.springframework.boot.context.config;

/**
 * Abstract base class for configuration data exceptions.
 * @since 2.4.0
 */
public abstract class ConfigDataException extends RuntimeException {
    protected ConfigDataException(String message, Throwable cause);
}

ConfigDataNotFoundException

package org.springframework.boot.context.config;

/**
 * Exception thrown when ConfigData cannot be found.
 * @since 2.4.0
 */
public abstract class ConfigDataNotFoundException extends ConfigDataException
        implements OriginProvider {
    public abstract String getReferenceDescription();
}

ConfigDataLocationNotFoundException

package org.springframework.boot.context.config;

/**
 * Exception thrown when a ConfigDataLocation cannot be found.
 * @since 2.4.0
 */
public class ConfigDataLocationNotFoundException extends ConfigDataNotFoundException {
    public ConfigDataLocationNotFoundException(ConfigDataLocation location);
    public ConfigDataLocationNotFoundException(ConfigDataLocation location, Throwable cause);
    public ConfigDataLocation getLocation();
}

ConfigDataResourceNotFoundException

package org.springframework.boot.context.config;

/**
 * Exception thrown when a ConfigDataResource cannot be found.
 * @since 2.4.0
 */
public class ConfigDataResourceNotFoundException extends ConfigDataNotFoundException {
    public ConfigDataResourceNotFoundException(ConfigDataResource resource);
    public ConfigDataResourceNotFoundException(ConfigDataResource resource, Throwable cause);
    public ConfigDataResource getResource();
}

UnsupportedConfigDataLocationException

package org.springframework.boot.context.config;

/**
 * Exception thrown when a ConfigDataLocation is not supported.
 * @since 2.4.0
 */
public class UnsupportedConfigDataLocationException extends ConfigDataException {
    public UnsupportedConfigDataLocationException(ConfigDataLocation location);
}

InactiveConfigDataAccessException

package org.springframework.boot.context.config;

/**
 * Exception thrown when an attempt is made to access inactive config data.
 * @since 2.4.5
 */
public class InactiveConfigDataAccessException extends ConfigDataException {
    public InactiveConfigDataAccessException(ConfigDataResource resource);
    public ConfigDataResource getResource();
}

InvalidConfigDataPropertyException

package org.springframework.boot.context.config;

/**
 * Exception thrown when a config data property is invalid.
 * @since 2.4.0
 */
public class InvalidConfigDataPropertyException extends ConfigDataException {
    public InvalidConfigDataPropertyException(ConfigDataResource resource,
                                             String propertyName,
                                             Object value,
                                             String reason);
    public ConfigDataResource getResource();
    public String getPropertyName();
}

Property Source Loaders

Property source loaders are the underlying mechanism used by StandardConfigDataLoader to load configuration files.

PropertySourceLoader

package org.springframework.boot.env;

import java.io.IOException;
import java.util.List;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.Resource;

/**
 * Strategy interface located through SpringFactoriesLoader and used to load a
 * PropertySource.
 *
 * @since 1.0.0
 */
public interface PropertySourceLoader {

    /**
     * Returns the file extensions that the loader supports (excluding the '.').
     *
     * @return the file extensions
     */
    String[] getFileExtensions();

    /**
     * Load the resource into one or more property sources. Implementations may either
     * return a list containing a single source, or in the case of a multi-document
     * format such as yaml a source for each document in the resource.
     *
     * @param name the root name of the property source. If multiple documents are loaded
     *        an additional suffix should be added to the name for each source loaded.
     * @param resource the resource to load
     * @return a list of property sources
     * @throws IOException if the source cannot be loaded
     */
    List<PropertySource<?>> load(String name, Resource resource) throws IOException;
}

YamlPropertySourceLoader

package org.springframework.boot.env;

import java.io.IOException;
import java.util.List;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.Resource;

/**
 * Strategy to load '.yml' (or '.yaml') files into a PropertySource.
 *
 * @since 1.0.0
 */
public class YamlPropertySourceLoader implements PropertySourceLoader {

    /**
     * Returns the supported file extensions: "yml" and "yaml".
     *
     * @return the file extensions
     */
    @Override
    public String[] getFileExtensions();

    /**
     * Load YAML content from the resource into property sources. For multi-document
     * YAML files, returns one property source per document.
     *
     * @param name the root name of the property source
     * @param resource the YAML resource to load
     * @return a list of property sources, one per YAML document
     * @throws IOException if the resource cannot be loaded
     * @throws IllegalStateException if SnakeYAML is not on the classpath
     */
    @Override
    public List<PropertySource<?>> load(String name, Resource resource) throws IOException;
}

PropertiesPropertySourceLoader

package org.springframework.boot.env;

import java.io.IOException;
import java.util.List;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.Resource;

/**
 * Strategy to load '.properties' files into a PropertySource. Also supports
 * loading XML properties files.
 *
 * @since 1.0.0
 */
public class PropertiesPropertySourceLoader implements PropertySourceLoader {

    /**
     * Returns the supported file extensions: "properties" and "xml".
     *
     * @return the file extensions
     */
    @Override
    public String[] getFileExtensions();

    /**
     * Load properties content from the resource into property sources. Supports both
     * standard .properties files and XML properties files.
     *
     * @param name the root name of the property source
     * @param resource the properties resource to load
     * @return a list containing the loaded property source
     * @throws IOException if the resource cannot be loaded
     */
    @Override
    public List<PropertySource<?>> load(String name, Resource resource) throws IOException;
}

Implementation Examples

Custom ConfigDataLoader

Here's how to implement a custom loader for encrypted configuration files:

package com.example.config;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import org.springframework.boot.context.config.ConfigData;
import org.springframework.boot.context.config.ConfigDataLoader;
import org.springframework.boot.context.config.ConfigDataLoaderContext;
import org.springframework.boot.context.config.ConfigDataResourceNotFoundException;
import org.springframework.boot.env.OriginTrackedMapPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.util.StreamUtils;

/**
 * Custom ConfigDataLoader that loads encrypted configuration files.
 * Register in META-INF/spring.factories:
 * org.springframework.boot.context.config.ConfigDataLoader=\
 *   com.example.config.EncryptedConfigDataLoader
 */
public class EncryptedConfigDataLoader
        implements ConfigDataLoader<EncryptedConfigDataResource> {

    private final DecryptionService decryptionService;

    /**
     * Constructor accepting optional dependencies.
     * Spring Boot will inject available constructor parameters.
     */
    public EncryptedConfigDataLoader() {
        this.decryptionService = new DecryptionService();
    }

    @Override
    public boolean isLoadable(ConfigDataLoaderContext context,
                             EncryptedConfigDataResource resource) {
        // Check if the encrypted file exists and is readable
        return resource.getPath().toFile().canRead();
    }

    @Override
    public ConfigData load(ConfigDataLoaderContext context,
                          EncryptedConfigDataResource resource)
            throws IOException, ConfigDataResourceNotFoundException {

        // Read encrypted content
        byte[] encryptedBytes = Files.readAllBytes(resource.getPath());

        // Decrypt the content
        String decryptedContent = decryptionService.decrypt(encryptedBytes);

        // Parse the decrypted properties
        Properties properties = new Properties();
        properties.load(new StringReader(decryptedContent));

        // Convert to Map for PropertySource
        Map<String, Object> map = new HashMap<>();
        properties.forEach((key, value) -> map.put(key.toString(), value));

        // Create PropertySource with origin tracking
        String name = "Encrypted config from '" + resource.getPath() + "'";
        PropertySource<?> propertySource = new OriginTrackedMapPropertySource(
                name, map, true);

        // Return ConfigData with the loaded property source
        return new ConfigData(Collections.singletonList(propertySource));
    }
}

Custom ConfigDataLocationResolver

Here's how to implement a custom resolver for encrypted configuration locations:

package com.example.config;

import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.List;
import org.springframework.boot.context.config.ConfigDataLocation;
import org.springframework.boot.context.config.ConfigDataLocationNotFoundException;
import org.springframework.boot.context.config.ConfigDataLocationResolver;
import org.springframework.boot.context.config.ConfigDataLocationResolverContext;
import org.springframework.boot.context.config.ConfigDataResourceNotFoundException;
import org.springframework.boot.context.config.Profiles;
import org.springframework.boot.logging.DeferredLogFactory;
import org.springframework.core.Ordered;

/**
 * Custom ConfigDataLocationResolver that resolves "encrypted:" prefixed locations.
 * Register in META-INF/spring.factories:
 * org.springframework.boot.context.config.ConfigDataLocationResolver=\
 *   com.example.config.EncryptedConfigDataLocationResolver
 */
public class EncryptedConfigDataLocationResolver
        implements ConfigDataLocationResolver<EncryptedConfigDataResource>, Ordered {

    private static final String PREFIX = "encrypted:";

    private final Log log;

    /**
     * Constructor with deferred logging support.
     *
     * @param logFactory the deferred log factory
     */
    public EncryptedConfigDataLocationResolver(DeferredLogFactory logFactory) {
        this.log = logFactory.getLog(getClass());
    }

    @Override
    public int getOrder() {
        // Run before standard resolvers
        return Ordered.HIGHEST_PRECEDENCE;
    }

    @Override
    public boolean isResolvable(ConfigDataLocationResolverContext context,
                               ConfigDataLocation location) {
        // Check if location starts with "encrypted:" prefix
        return location.hasPrefix(PREFIX);
    }

    @Override
    public List<EncryptedConfigDataResource> resolve(
            ConfigDataLocationResolverContext context,
            ConfigDataLocation location)
            throws ConfigDataLocationNotFoundException,
                   ConfigDataResourceNotFoundException {

        // Remove the "encrypted:" prefix
        String path = location.getNonPrefixedValue(PREFIX);

        this.log.trace("Resolving encrypted config location: " + path);

        // Create the resource
        Path filePath = Paths.get(path);
        EncryptedConfigDataResource resource =
                new EncryptedConfigDataResource(filePath, location.isOptional());

        // Check if file exists for non-optional locations
        if (!location.isOptional() && !filePath.toFile().exists()) {
            throw new ConfigDataResourceNotFoundException(resource);
        }

        return Collections.singletonList(resource);
    }

    @Override
    public List<EncryptedConfigDataResource> resolveProfileSpecific(
            ConfigDataLocationResolverContext context,
            ConfigDataLocation location,
            Profiles profiles)
            throws ConfigDataLocationNotFoundException {

        // Handle profile-specific encrypted configs
        // e.g., encrypted:config-{profile}.enc
        String path = location.getNonPrefixedValue(PREFIX);
        List<EncryptedConfigDataResource> resources = new ArrayList<>();

        for (String profile : profiles.getAccepted()) {
            String profilePath = path.replace("{profile}", profile);
            Path filePath = Paths.get(profilePath);

            if (filePath.toFile().exists()) {
                resources.add(new EncryptedConfigDataResource(
                        filePath, location.isOptional()));
            }
        }

        return resources;
    }
}

Custom ConfigDataResource

package com.example.config;

import java.nio.file.Path;
import java.util.Objects;
import org.springframework.boot.context.config.ConfigDataResource;

/**
 * ConfigDataResource for encrypted configuration files.
 */
public class EncryptedConfigDataResource extends ConfigDataResource {

    private final Path path;

    /**
     * Create a new EncryptedConfigDataResource.
     *
     * @param path the path to the encrypted file
     * @param optional if the resource is optional
     */
    public EncryptedConfigDataResource(Path path, boolean optional) {
        super(optional);
        this.path = path;
    }

    /**
     * Get the path to the encrypted configuration file.
     *
     * @return the file path
     */
    public Path getPath() {
        return this.path;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null || getClass() != obj.getClass()) {
            return false;
        }
        EncryptedConfigDataResource other = (EncryptedConfigDataResource) obj;
        return Objects.equals(this.path, other.path);
    }

    @Override
    public int hashCode() {
        return Objects.hash(this.path);
    }

    @Override
    public String toString() {
        return "encrypted config [" + this.path + "]";
    }
}

Custom PropertySourceLoader

Here's how to implement a custom property source loader for a proprietary format:

package com.example.config;

import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.boot.env.OriginTrackedMapPropertySource;
import org.springframework.boot.env.PropertySourceLoader;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.Resource;

/**
 * Custom PropertySourceLoader for loading JSON configuration files.
 * Register in META-INF/spring.factories:
 * org.springframework.boot.env.PropertySourceLoader=\
 *   com.example.config.JsonPropertySourceLoader
 */
public class JsonPropertySourceLoader implements PropertySourceLoader {

    @Override
    public String[] getFileExtensions() {
        // Specify the file extensions this loader handles
        return new String[] { "json" };
    }

    @Override
    public List<PropertySource<?>> load(String name, Resource resource)
            throws IOException {

        // Read the JSON file
        try (InputStream input = resource.getInputStream()) {
            // Parse JSON (using Jackson or Gson)
            Map<String, Object> properties = parseJson(input);

            if (properties.isEmpty()) {
                return Collections.emptyList();
            }

            // Flatten nested JSON into property keys
            Map<String, Object> flattened = flattenMap(properties);

            // Create PropertySource with origin tracking
            PropertySource<?> propertySource = new OriginTrackedMapPropertySource(
                    name, Collections.unmodifiableMap(flattened), true);

            return Collections.singletonList(propertySource);
        }
    }

    /**
     * Parse JSON input stream into a Map.
     *
     * @param input the JSON input stream
     * @return the parsed map
     * @throws IOException if parsing fails
     */
    private Map<String, Object> parseJson(InputStream input) throws IOException {
        // Implementation using JSON parser
        ObjectMapper mapper = new ObjectMapper();
        return mapper.readValue(input,
                new TypeReference<Map<String, Object>>() {});
    }

    /**
     * Flatten nested JSON map into property keys with dot notation.
     * For example: {"server": {"port": 8080}} becomes {"server.port": "8080"}
     *
     * @param source the nested map
     * @return the flattened map
     */
    private Map<String, Object> flattenMap(Map<String, Object> source) {
        Map<String, Object> result = new HashMap<>();
        flattenMap("", source, result);
        return result;
    }

    private void flattenMap(String prefix, Map<String, Object> source,
                           Map<String, Object> result) {
        source.forEach((key, value) -> {
            String fullKey = prefix.isEmpty() ? key : prefix + "." + key;
            if (value instanceof Map) {
                @SuppressWarnings("unchecked")
                Map<String, Object> map = (Map<String, Object>) value;
                flattenMap(fullKey, map, result);
            } else {
                result.put(fullKey, value);
            }
        });
    }
}

Usage Examples

Using Custom Loaders in Application

package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * Application using custom configuration loaders.
 * Configuration locations can be specified in several ways:
 *
 * 1. Via spring.config.location property:
 *    --spring.config.location=encrypted:/etc/config/app.enc
 *
 * 2. Via spring.config.additional-location property:
 *    --spring.config.additional-location=encrypted:/secure/secrets.enc
 *
 * 3. Via spring.config.import property:
 *    spring.config.import=encrypted:/secure/db-config.enc
 */
@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

Configuration in application.yml

# application.yml

# Import encrypted configuration
spring:
  config:
    import:
      - encrypted:/etc/config/database.enc
      - optional:encrypted:/etc/config/optional-settings.enc
      - configtree:/etc/config/configmap/

# Standard configuration
server:
  port: 8080
# Properties from encrypted files will be merged with these

Programmatic Configuration Loading

package com.example;

import org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor;
import org.springframework.boot.origin.OriginTrackedMapPropertySource;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.StandardEnvironment;

/**
 * Example of programmatically loading configuration data.
 */
public class ProgrammaticConfigExample {

    /**
     * Load configuration data programmatically into an environment.
     */
    public void loadConfiguration() {
        // Create a new environment
        ConfigurableEnvironment environment = new StandardEnvironment();

        // Apply ConfigData processing
        ConfigDataEnvironmentPostProcessor.applyTo(environment);

        // Now the environment contains all loaded configuration
        String property = environment.getProperty("my.custom.property");

        System.out.println("Loaded property: " + property);
    }

    /**
     * Load configuration with additional profiles.
     */
    public void loadConfigurationWithProfiles() {
        ConfigurableEnvironment environment = new StandardEnvironment();

        // Apply ConfigData processing with profiles
        ConfigDataEnvironmentPostProcessor.applyTo(
                environment,
                null,  // ResourceLoader (null uses default)
                null,  // BootstrapContext (null creates throw-away)
                "dev", "local"  // Additional profiles
        );

        String property = environment.getProperty("my.profile.specific.property");
        System.out.println("Profile-specific property: " + property);
    }
}

Spring Factories Registration

To register custom loaders and resolvers, add them to META-INF/spring.factories:

# META-INF/spring.factories

# ConfigDataLocationResolver implementations
org.springframework.boot.context.config.ConfigDataLocationResolver=\
com.example.config.EncryptedConfigDataLocationResolver,\
com.example.config.DatabaseConfigDataLocationResolver

# ConfigDataLoader implementations
org.springframework.boot.context.config.ConfigDataLoader=\
com.example.config.EncryptedConfigDataLoader,\
com.example.config.DatabaseConfigDataLoader

# PropertySourceLoader implementations
org.springframework.boot.env.PropertySourceLoader=\
com.example.config.JsonPropertySourceLoader,\
com.example.config.XmlPropertySourceLoader

Origin Tracking

Spring Boot provides origin tracking to record where configuration properties came from. This is useful for debugging and displaying helpful error messages that indicate the exact source file and line number of problematic configuration.

Origin Interface

The Origin interface represents the origin of a configuration item (e.g., a file with line/column numbers).

package org.springframework.boot.origin;

/**
 * Interface that uniquely represents the origin of an item.
 * @since 2.0.0
 */
public interface Origin {
    /**
     * Return the parent origin for this instance if there is one.
     * The parent origin provides the origin of the item that created this one.
     * @return the parent origin or null
     * @since 2.4.0
     */
    default Origin getParent();

    /**
     * Find the Origin that an object originated from.
     * Checks if the source object is an Origin or OriginProvider
     * and also searches exception stacks.
     * @param source the source object or null
     * @return an Origin or null
     */
    static Origin from(Object source);

    /**
     * Find the parents of the Origin that an object originated from.
     * Provides a list of all parents up to root Origin,
     * starting with the most immediate parent.
     * @param source the source object or null
     * @return a list of parents or an empty list
     * @since 2.4.0
     */
    static List<Origin> parentsFrom(Object source);
}

OriginProvider Interface

The OriginProvider interface allows objects to provide their origin information.

package org.springframework.boot.origin;

/**
 * Interface to provide access to the origin of an item.
 * @since 2.0.0
 */
@FunctionalInterface
public interface OriginProvider {
    /**
     * Return the source origin or null if the origin is not known.
     * @return the origin or null
     */
    Origin getOrigin();
}

TextResourceOrigin Class

TextResourceOrigin represents an origin from a text-based resource with location information.

package org.springframework.boot.origin;

import org.springframework.core.io.Resource;

/**
 * Origin from a text-based Resource.
 * Provides access to the resource and optionally to a location within it.
 * @since 2.0.0
 */
public class TextResourceOrigin implements Origin {
    /**
     * Create a new TextResourceOrigin for the given resource.
     * @param resource the resource
     * @param location the location within the resource (may be null)
     */
    public TextResourceOrigin(Resource resource, Location location);

    /**
     * Return the resource where the property originated.
     * @return the text resource origin
     */
    public Resource getResource();

    /**
     * Return the location within the resource where the property originated.
     * @return the location within the resource (may be null)
     */
    public Location getLocation();

    /**
     * Location within the resource (line and column numbers).
     */
    public static final class Location {
        /**
         * Create a new Location instance.
         * @param line the line number (0-indexed)
         * @param column the column number (0-indexed)
         */
        public Location(int line, int column);

        /**
         * Return the line number (0-indexed).
         * @return the line number
         */
        public int getLine();

        /**
         * Return the column number (0-indexed).
         * @return the column number
         */
        public int getColumn();
    }
}

PropertySourceOrigin Class

PropertySourceOrigin represents an origin from a Spring PropertySource.

package org.springframework.boot.origin;

import org.springframework.core.env.PropertySource;

/**
 * Origin from a PropertySource.
 * @since 2.0.0
 */
public class PropertySourceOrigin implements Origin {
    /**
     * Get an Origin for the given PropertySource and property name.
     * @param propertySource the property source
     * @param propertyName the property name
     * @return an Origin or null
     */
    public static Origin get(PropertySource<?> propertySource, String propertyName);
}

Usage Example

import org.springframework.boot.origin.Origin;
import org.springframework.boot.origin.OriginProvider;
import org.springframework.boot.origin.TextResourceOrigin;
import org.springframework.boot.context.properties.bind.BindException;
import org.springframework.core.env.PropertySource;

public class OriginTrackingExample {

    public void handleBindingError(BindException ex) {
        // Get origin from exception
        Origin origin = Origin.from(ex);

        if (origin instanceof TextResourceOrigin textOrigin) {
            Resource resource = textOrigin.getResource();
            TextResourceOrigin.Location location = textOrigin.getLocation();

            if (location != null) {
                System.err.printf("Error in %s at line %d, column %d%n",
                    resource.getDescription(),
                    location.getLine() + 1,  // Display as 1-indexed
                    location.getColumn() + 1
                );
            } else {
                System.err.printf("Error in %s%n", resource.getDescription());
            }
        }
    }

    public void checkPropertyOrigin(ConfigurableEnvironment environment, String propertyName) {
        // Get the property value
        Object value = environment.getProperty(propertyName);

        // Try to find its origin
        Origin origin = Origin.from(value);

        if (origin != null) {
            System.out.println("Property '" + propertyName + "' comes from: " + origin);

            // Check parent origins
            List<Origin> parents = Origin.parentsFrom(value);
            for (Origin parent : parents) {
                System.out.println("  Parent origin: " + parent);
            }
        } else {
            System.out.println("Property '" + propertyName + "' has unknown origin");
        }
    }
}

How Origin Tracking Works

  1. Property Source Reading: When Spring Boot reads property files (YAML, properties, etc.), it wraps property values in origin-tracking wrappers.

  2. OriginTrackedValue: Values are wrapped as OriginTrackedValue which implements OriginProvider.

  3. Bind Exceptions: When property binding fails, Spring Boot includes origin information in the exception message.

  4. ConfigData Integration: ConfigDataResource implementations can provide origins through the OriginTrackedResource interface.

Origin Tracking in Error Messages

Spring Boot automatically uses origin tracking to provide helpful error messages:

***************************
APPLICATION FAILED TO START
***************************

Description:

Binding to target org.springframework.boot.context.properties.bind.BindException: Failed to bind properties under 'server.port' to int failed:

    Property: server.port
    Value: "not-a-number"
    Origin: class path resource [application.properties] - 10:14
    Reason: failed to convert java.lang.String to int

Action:

Update your application's configuration

The Origin: class path resource [application.properties] - 10:14 line tells you exactly where the problematic property was defined (line 10, column 14).

Common Patterns

Pattern 1: Custom External Configuration Source (Vault Integration)

Load secrets from HashiCorp Vault using custom ConfigData components.

import org.springframework.boot.context.config.*;
import org.springframework.core.env.PropertySource;
import java.io.IOException;
import java.util.*;

// Custom ConfigData location for Vault
public class VaultConfigDataLocation extends ConfigDataLocation {
    private final String path;

    public VaultConfigDataLocation(String path) {
        this.path = path;
    }

    public String getPath() {
        return path;
    }
}

// Resolver for vault: prefix
public class VaultConfigDataLocationResolver
        implements ConfigDataLocationResolver<VaultConfigDataResource> {

    @Override
    public boolean isResolvable(ConfigDataLocationResolverContext context,
                               ConfigDataLocation location) {
        return location.toString().startsWith("vault:");
    }

    @Override
    public List<VaultConfigDataResource> resolve(
            ConfigDataLocationResolverContext context,
            ConfigDataLocation location) {
        String path = location.toString().substring(6); // Remove "vault:"
        return Collections.singletonList(new VaultConfigDataResource(path));
    }
}

// Loader for Vault resources
public class VaultConfigDataLoader
        implements ConfigDataLoader<VaultConfigDataResource> {

    private final VaultClient vaultClient;

    public VaultConfigDataLoader() {
        this.vaultClient = new VaultClient();
    }

    @Override
    public ConfigData load(ConfigDataLoaderContext context,
                          VaultConfigDataResource resource) throws IOException {
        Map<String, Object> secrets = vaultClient.readSecrets(resource.getPath());
        PropertySource<?> ps = new MapPropertySource("vault:" + resource.getPath(), secrets);
        return new ConfigData(Collections.singleton(ps));
    }
}

Register in META-INF/spring.factories:

org.springframework.boot.context.config.ConfigDataLocationResolver=\
com.example.config.VaultConfigDataLocationResolver
org.springframework.boot.context.config.ConfigDataLoader=\
com.example.config.VaultConfigDataLoader

Use in application.yml:

spring:
  config:
    import: vault://secret/myapp

Pattern 2: Kubernetes ConfigMap Integration

Load configuration from Kubernetes ConfigMaps using configtree.

spring:
  config:
    import:
      - "optional:configtree:/etc/config/"
      - "optional:configtree:/etc/secrets/"

Kubernetes ConfigMap:

apiVersion: v1
kind: ConfigMap
metadata:
  name: myapp-config
data:
  database.url: "jdbc:postgresql://db:5432/mydb"
  database.username: "appuser"
---
apiVersion: v1
kind: Secret
metadata:
  name: myapp-secrets
data:
  database.password: ZGJwYXNzd29yZA==

Pattern 3: Multi-Source Configuration Composition

spring:
  config:
    import:
      - "optional:file:./config/local.yml"
      - "classpath:defaults.yml"
      - "optional:configserver:http://config-server:8888"

Pattern 4: Profile-Specific Configuration Loading

Implement profile-aware custom configuration.

public class ProfileAwareResolver
        implements ConfigDataLocationResolver<ProfileAwareResource> {

    @Override
    public List<ProfileAwareResource> resolveProfileSpecific(
            ConfigDataLocationResolverContext context,
            ConfigDataLocation location,
            Profiles profiles) {

        List<ProfileAwareResource> resources = new ArrayList<>();
        resources.add(new ProfileAwareResource("base", false));

        for (String profile : profiles.getAccepted()) {
            resources.add(new ProfileAwareResource(profile, true));
        }

        return resources;
    }
}

Pattern 5: Resilient Configuration Loading with Retry

public class ResilientConfigDataLoader
        implements ConfigDataLoader<ResilientResource> {

    @Override
    public ConfigData load(ConfigDataLoaderContext context,
                          ResilientResource resource) throws IOException {
        try {
            return loadWithRetry(resource, 3);
        } catch (Exception e) {
            if (resource.isOptional()) {
                return ConfigData.EMPTY;
            }
            throw new ConfigDataNotFoundException(resource, e);
        }
    }

    private ConfigData loadWithRetry(ResilientResource resource, int maxAttempts)
            throws IOException {
        for (int attempt = 1; attempt <= maxAttempts; attempt++) {
            try {
                Map<String, Object> props = fetchConfiguration(resource);
                PropertySource<?> ps = new MapPropertySource(resource.toString(), props);
                return new ConfigData(Collections.singleton(ps));
            } catch (IOException e) {
                if (attempt < maxAttempts) {
                    Thread.sleep(1000 * attempt); // Exponential backoff
                } else {
                    throw e;
                }
            }
        }
        throw new IOException("Failed after " + maxAttempts + " attempts");
    }
}

Best Practices

  1. Resource Uniqueness: Ensure ConfigDataResource implementations have proper equals(), hashCode(), and toString() methods.

  2. Optional Locations: Use the optional: prefix for configuration locations that may not exist:

    spring.config.import=optional:encrypted:/etc/config/secrets.enc
  3. Profile-Specific Loading: Implement resolveProfileSpecific() in resolvers to support profile-based configuration.

  4. Error Handling: Throw appropriate exceptions:

    • ConfigDataLocationNotFoundException for missing locations
    • ConfigDataResourceNotFoundException for missing resources
    • IOException for I/O errors
  5. Bootstrap Context: Use ConfigurableBootstrapContext to share expensive objects between resolvers and loaders.

  6. Deferred Logging: Accept DeferredLogFactory in constructors to ensure logging is available at the right time.

  7. Ordering: Implement Ordered or use @Order to control when resolvers are consulted.

  8. Property Source Options: Use ConfigData.Option to control how property sources are processed:

    • IGNORE_IMPORTS: Don't process spring.config.import from this source
    • IGNORE_PROFILES: Don't process profile properties from this source
    • PROFILE_SPECIFIC: Mark source as profile-specific
  9. Thread Safety: All ConfigData components must be thread-safe as they're called concurrently

  10. Testing: Test custom ConfigData loaders with various scenarios including missing resources and malformed data

Common Pitfalls to Avoid

  1. Don't forget to register in spring.factories - Custom resolvers and loaders won't be found without registration
  2. Don't block in load() methods - Use timeouts for external calls to prevent hung startup
  3. Don't throw generic exceptions - Use ConfigDataNotFoundException for missing config
  4. Don't ignore the optional prefix - Always check if resources are optional before failing
  5. Don't forget equals/hashCode - Resources must properly implement identity methods
  6. Don't load large data - ConfigData is for configuration, not data storage
  7. Don't mutate shared state - Resolvers and loaders must be stateless
  8. Don't skip profile support - Implement resolveProfileSpecific for profile-aware config
  9. Don't forget origin tracking - Include origin information for debugging
  10. Don't ignore security - Sanitize and validate external configuration sources

Related Topics

  • Spring Boot Configuration Properties
  • Spring Boot Configuration Data
  • Spring Boot Configuration Annotations
  • Spring Boot Environment Property Sources

References

  • Spring Boot Reference Documentation - External Configuration
  • Spring Boot Reference Documentation - Config Data
  • Package: org.springframework.boot.context.config
  • Package: org.springframework.boot.env