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

resource-loading.mddocs/

Spring Boot Resource Loading Package

Package: org.springframework.boot.io

The io package provides support for loading resources with enhanced protocol resolution and file system integration.

ApplicationResourceLoader

ApplicationResourceLoader Class

Custom resource loader that supports additional protocol resolvers and prefers file system resources.

public class ApplicationResourceLoader extends DefaultResourceLoader {

    @Override
    protected Resource getResourceByPath(String path);

    public static ResourceLoader get();

    public static ResourceLoader get(ClassLoader classLoader);

    public static ResourceLoader get(ClassLoader classLoader,
                                    SpringFactoriesLoader springFactoriesLoader);

    public static ResourceLoader get(ClassLoader classLoader,
                                    SpringFactoriesLoader springFactoriesLoader,
                                    Path workingDirectory);

    public static ResourceLoader get(ResourceLoader resourceLoader);

    public static ResourceLoader get(ResourceLoader resourceLoader,
                                    boolean preferFileResolution);

    public static ResourceLoader get(ResourceLoader resourceLoader,
                                    SpringFactoriesLoader springFactoriesLoader);
}

Key Features

  • Resolves plain paths (without prefix) to file system resources by default
  • Different from DefaultResourceLoader which resolves to classpath resources
  • Supports protocol resolvers registered in spring.factories
  • Optional file path resolution for supported resources
  • Works with custom working directories

Factory Methods

// Get with default class loader
ResourceLoader loader = ApplicationResourceLoader.get();

// Get with specific class loader
ResourceLoader loader = ApplicationResourceLoader.get(classLoader);

// Get with custom SpringFactoriesLoader
ResourceLoader loader = ApplicationResourceLoader.get(
    classLoader,
    springFactoriesLoader
);

// Get with working directory
ResourceLoader loader = ApplicationResourceLoader.get(
    classLoader,
    springFactoriesLoader,
    workingDirectory
);

// Wrap existing resource loader
ResourceLoader loader = ApplicationResourceLoader.get(existingLoader);

// Wrap with file resolution preference
ResourceLoader loader = ApplicationResourceLoader.get(
    existingLoader,
    true  // prefer file resolution
);

FilePathResolver Interface

Strategy interface for determining file paths of resources.

public interface FilePathResolver {

    String resolveFilePath(String location, Resource resource);
}

Implementations can be registered in spring.factories to customize how resources are resolved to file system paths.

Usage Examples

Basic Resource Loading

@Service
public class ConfigService {

    private final ResourceLoader resourceLoader;

    public ConfigService() {
        // Get application resource loader
        this.resourceLoader = ApplicationResourceLoader.get();
    }

    public Properties loadConfig(String path) throws IOException {
        // Loads from file system by default
        Resource resource = resourceLoader.getResource(path);

        Properties props = new Properties();
        try (InputStream is = resource.getInputStream()) {
            props.load(is);
        }
        return props;
    }
}

Resource Loading with Explicit Protocols

public class ResourceLoadingExamples {

    private final ResourceLoader loader = ApplicationResourceLoader.get();

    public void loadResources() throws IOException {
        // File system (default for plain paths)
        Resource fileResource = loader.getResource("config/app.properties");

        // Classpath
        Resource classpathResource = loader.getResource("classpath:config/app.properties");

        // URL
        Resource urlResource = loader.getResource("https://example.com/config.json");

        // File URL
        Resource fileUrlResource = loader.getResource("file:/etc/config/app.properties");
    }
}

Difference from DefaultResourceLoader

// DefaultResourceLoader behavior
DefaultResourceLoader defaultLoader = new DefaultResourceLoader();
Resource resource1 = defaultLoader.getResource("config.properties");
// → Returns ClassPathResource("config.properties")

// ApplicationResourceLoader behavior
ResourceLoader appLoader = ApplicationResourceLoader.get();
Resource resource2 = appLoader.getResource("config.properties");
// → Returns FileSystemResource("config.properties")

// Both loaders respect explicit prefixes
Resource cp1 = defaultLoader.getResource("classpath:config.properties");
Resource cp2 = appLoader.getResource("classpath:config.properties");
// → Both return ClassPathResource

Working with Custom Working Directory

public class WorkingDirectoryExample {

    public ResourceLoader createLoader(String workingDir) {
        Path workingDirectory = Paths.get(workingDir);

        return ApplicationResourceLoader.get(
            null,  // use default class loader
            SpringFactoriesLoader.forDefaultResourceLocation(null),
            workingDirectory
        );
    }

    public void loadRelativeResource() throws IOException {
        ResourceLoader loader = createLoader("/app/config");

        // Loads from /app/config/settings.yaml
        Resource resource = loader.getResource("settings.yaml");

        String content = new String(resource.getInputStream().readAllBytes());
        System.out.println(content);
    }
}

Integration with Spring Boot Application

@SpringBootApplication
public class MyApplication {

    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(MyApplication.class);

        // Set custom resource loader
        app.setResourceLoader(ApplicationResourceLoader.get());

        app.run(args);
    }
}

Custom Protocol Resolver

// Implement ProtocolResolver
public class CustomProtocolResolver implements ProtocolResolver {

    @Override
    public Resource resolve(String location, ResourceLoader resourceLoader) {
        if (location.startsWith("custom:")) {
            String path = location.substring(7);
            return new CustomResource(path);
        }
        return null;
    }
}

// Register in META-INF/spring.factories
// org.springframework.core.io.ProtocolResolver=\
// com.example.CustomProtocolResolver

// Usage
ResourceLoader loader = ApplicationResourceLoader.get();
Resource resource = loader.getResource("custom:my-resource");

Custom FilePathResolver

// Implement FilePathResolver
public class CustomFilePathResolver implements ApplicationResourceLoader.FilePathResolver {

    @Override
    public String resolveFilePath(String location, Resource resource) {
        if (location.startsWith("s3:")) {
            // Download S3 resource to local cache and return path
            String localPath = downloadFromS3(location);
            return localPath;
        }
        return null;
    }

    private String downloadFromS3(String s3Url) {
        // Implementation details
        return "/tmp/cache/" + extractKey(s3Url);
    }

    private String extractKey(String s3Url) {
        return s3Url.substring(3);
    }
}

// Register in META-INF/spring.factories
// org.springframework.boot.io.ApplicationResourceLoader$FilePathResolver=\
// com.example.CustomFilePathResolver

// Usage with file resolution preference
ResourceLoader loader = ApplicationResourceLoader.get(
    existingLoader,
    true  // prefer file resolution
);

Wrapping Existing ResourceLoader

@Configuration
public class ResourceLoaderConfig {

    @Bean
    public ResourceLoader resourceLoader(ApplicationContext context) {
        // Wrap application context's resource loader
        return ApplicationResourceLoader.get(
            context,
            false  // don't prefer file resolution
        );
    }
}

@Service
public class DataService {

    private final ResourceLoader resourceLoader;

    public DataService(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

    public String loadData(String location) throws IOException {
        Resource resource = resourceLoader.getResource(location);
        return new String(resource.getInputStream().readAllBytes());
    }
}

Loading Resources in Tests

@SpringBootTest
class ResourceLoadingTest {

    @Test
    void testLoadApplicationResource() throws IOException {
        ResourceLoader loader = ApplicationResourceLoader.get();

        // Create test file
        Path testFile = Files.createTempFile("test", ".txt");
        Files.writeString(testFile, "test content");

        // Load using absolute path
        Resource resource = loader.getResource(testFile.toString());

        assertTrue(resource.exists());
        assertEquals("test content",
            new String(resource.getInputStream().readAllBytes()));

        // Cleanup
        Files.delete(testFile);
    }

    @Test
    void testLoadClasspathResource() throws IOException {
        ResourceLoader loader = ApplicationResourceLoader.get();

        // Load from test resources
        Resource resource = loader.getResource("classpath:test-data.json");

        assertTrue(resource.exists());
        assertNotNull(resource.getInputStream());
    }
}

Resource Loading Patterns

@Component
public class ResourcePatternService {

    private final ResourceLoader resourceLoader;

    public ResourcePatternService() {
        this.resourceLoader = ApplicationResourceLoader.get();
    }

    public Resource loadConfig(String environment) {
        // Load environment-specific config
        String location = String.format("config/application-%s.yml", environment);
        return resourceLoader.getResource(location);
    }

    public Resource loadFromProfile(String profile, String filename) {
        // Load from profile directory
        return resourceLoader.getResource(
            String.format("profiles/%s/%s", profile, filename)
        );
    }

    public Resource loadWithFallback(String primary, String fallback) {
        Resource resource = resourceLoader.getResource(primary);
        if (!resource.exists()) {
            resource = resourceLoader.getResource(fallback);
        }
        return resource;
    }
}

Advanced Configuration Loading

@Service
public class ConfigurationLoader {

    private final ResourceLoader resourceLoader;
    private final Environment environment;

    public ConfigurationLoader(Environment environment) {
        this.resourceLoader = ApplicationResourceLoader.get();
        this.environment = environment;
    }

    public Map<String, Object> loadConfiguration() {
        Map<String, Object> config = new HashMap<>();

        // Load base configuration
        loadIntoMap(config, "config/base.properties");

        // Load environment-specific configuration
        String profile = environment.getProperty("spring.profiles.active", "default");
        loadIntoMap(config, String.format("config/%s.properties", profile));

        // Load local overrides if present
        Resource localConfig = resourceLoader.getResource("config/local.properties");
        if (localConfig.exists()) {
            loadIntoMap(config, localConfig);
        }

        return config;
    }

    private void loadIntoMap(Map<String, Object> map, String location) {
        Resource resource = resourceLoader.getResource(location);
        loadIntoMap(map, resource);
    }

    private void loadIntoMap(Map<String, Object> map, Resource resource) {
        if (!resource.exists()) {
            return;
        }

        try (InputStream is = resource.getInputStream()) {
            Properties props = new Properties();
            props.load(is);
            props.forEach((k, v) -> map.put(k.toString(), v));
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }
}

Resource Resolution Order

When using ApplicationResourceLoader:

  1. Protocol Resolvers - Registered via spring.factories
  2. Explicit Protocols - classpath:, file:, http:, etc.
  3. File System - Default for paths without protocol prefix
  4. File Path Resolvers - When preferFileResolution is enabled

Best Practices

Use Explicit Protocols

// Good - explicit
Resource resource = loader.getResource("classpath:config.properties");
Resource fileResource = loader.getResource("file:/etc/config.properties");

// Avoid ambiguity when intention is unclear
Resource ambiguous = loader.getResource("config.properties");

Handle Resource Existence

Resource resource = loader.getResource("optional-config.properties");
if (resource.exists() && resource.isReadable()) {
    // Use resource
} else {
    // Use defaults or throw exception
}

Use Working Directory for Relative Paths

// Configure working directory for consistent relative path resolution
ResourceLoader loader = ApplicationResourceLoader.get(
    classLoader,
    springFactoriesLoader,
    Paths.get("/app/data")
);

Import Statements

import org.springframework.boot.io.ApplicationResourceLoader;
import org.springframework.boot.io.ApplicationResourceLoader.FilePathResolver;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.ProtocolResolver;
import org.springframework.core.io.support.SpringFactoriesLoader;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.FileSystemResource;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.io.IOException;
import java.io.InputStream;