Mastering Resource Loading in Spring Boot 3: From @Value to ResourcePatternResolver

This guide explains how to load files in Spring Boot 3 using the Resource abstraction, covering classpath, filesystem, and URL prefixes, @Value injection, direct Resource instantiation, dynamic loading with ResourceLoader, and bulk loading with ResourcePatternResolver, plus common pitfalls and best‑practice code examples.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Mastering Resource Loading in Spring Boot 3: From @Value to ResourcePatternResolver

Introduction

When developing Spring Boot applications you often need to load external files such as JSON data, properties, or SQL scripts. These files may reside on the classpath (e.g., src/main/resources), on the file system, or at a remote URL.

Spring provides the Resource interface to abstract away the location details and offers utilities for loading resources uniformly.

1. Resource Interface and Prefixes

The prefix of a resource location determines which Resource implementation Spring uses. Common prefixes include: classpath:ClassPathResource (e.g., src/main/resources) file:FileSystemResource (absolute path, e.g., /var/config/app.json) file:./FileUrlResource (relative to the working directory) https://UrlResource (remote HTTP/HTTPS file)

Choosing the correct prefix lets the same code load resources from different locations without modification.

2. Loading Resources with @Value

The simplest way is to combine the @Value annotation with a Resource field. This works when the path is known at compile time.

@RestController
@RequestMapping("/value/loader")
public class ValueResourceController {
  @Value("classpath:data.txt")
  private Resource data;

  @GetMapping
  public String data() throws IOException {
    return new String(data.getContentAsByteArray(), StandardCharsets.UTF_8);
  }
}

Examples of loading from different locations:

// classpath
@Value("classpath:data.txt")
private Resource data;

// filesystem (relative to working directory)
@Value("file:./data/data.txt")
private Resource fileResource;

// remote URL
@Value("https://example.com/file.txt")
private Resource urlResource;

A common mistake is omitting the prefix (e.g., @Value("/data.txt")), which causes Spring to be unable to resolve the resource.

3. Directly Instantiating Resource Implementations

If you prefer not to use @Value, you can create a ClassPathResource (or other implementation) directly:

@GetMapping("/classpath")
public String getClasspathResource() throws IOException {
  Resource resource = new ClassPathResource("data.txt");
  return new String(resource.getContentAsByteArray(), StandardCharsets.UTF_8);
}

When running from a packaged JAR, avoid calling resource.getFile() because the resource is not a real file. Use getInputStream() or getContentAsByteArray() instead.

4. Dynamic Loading with ResourceLoader

When the resource path is determined at runtime (e.g., based on user input), ResourceLoader allows you to build the path dynamically:

@RestController
@RequestMapping("/resource")
public class ResourceLoaderController {
  private static final String JSON = ".json";
  private final ResourceLoader resourceLoader;

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

  @GetMapping("/{name}")
  public String getJsonFile(@PathVariable String name) throws IOException {
    String path = "classpath:json/" + name + JSON;
    Resource resource = resourceLoader.getResource(path);
    if (!resource.exists()) {
      return "Resource not found: " + name + JSON;
    }
    return new String(resource.getContentAsByteArray(), StandardCharsets.UTF_8);
  }
}

Use @Value for static paths and ResourceLoader for dynamic ones.

5. Loading Multiple Resources with ResourcePatternResolver

To load several files matching a pattern (e.g., all SQL migration scripts), use ResourcePatternResolver:

@RestController
@RequestMapping("/sql")
public class ResourcePatternController {
  private static final String SQL_FILE_PATTERN = "classpath:sql/*.sql";
  private final ResourcePatternResolver resolver;

  public ResourcePatternController(ResourcePatternResolver resolver) {
    this.resolver = resolver;
  }

  @GetMapping
  public String getFiles() throws IOException {
    Resource[] resources = resolver.getResources(SQL_FILE_PATTERN);
    Arrays.sort(resources, Comparator.comparing(r -> Optional.ofNullable(r.getFilename()).orElse("")));
    StringBuilder sb = new StringBuilder();
    sb.append("Found ").append(resources.length).append(" SQL files:

");
    for (Resource r : resources) {
      sb.append("=== ").append(r.getFilename()).append(" ===
")
        .append(new String(r.getContentAsByteArray(), StandardCharsets.UTF_8))
        .append("
");
    }
    return sb.toString();
  }
}

Pattern syntax examples: classpath:sql/*.sql – all .sql files in sql folder classpath:config/**/*.xml – all .xml files in config and sub‑folders classpath*:META-INF/*.xml – all .xml files under META-INF in every JAR on the classpath

Typical use cases include ordered database migrations, bulk processing of template files, and scanning plugin descriptors across JARs.

6. Choosing the Right Approach

@Value + Resource : Use when the path is known at compile time and you need a single resource.

ResourceLoader : Use when the path is determined at runtime (user input, config, DB query).

ResourcePatternResolver : Use when you need to load multiple resources matching a pattern.

By selecting the appropriate method you can reliably load resources in any environment—IDE, packaged JAR, or production server.

Javaresource-loadingbackend-developmentSpring Frameworkspring-boot
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.