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.
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.
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.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
