skip to content

How does ResourceLoader interpret location prefixes like classpath:, file:, and http:?

level: middleimportance: must knowfreq 60%

answer

  1. classpath: -> ClassPathResource
  2. file:/http:/jar: -> UrlResource (new URL)
  3. no prefix -> loader-specific (default=classpath)
  4. ApplicationContext IS a ResourceLoader
  5. getResource never null, never checks exists()

basics

~20 s

ResourceLoader.getResource(location) turns a string into a Resource. A classpath: prefix gives a ClassPathResource, file: and http: give a UrlResource, and a plain path with no prefix is interpreted by the specific loader (usually classpath in an ApplicationContext).

solid answer

~40 s

`ResourceLoader` has one method: `Resource getResource(String location)`. It maps the location string to a concrete Resource by inspecting an optional prefix. `classpath:foo.txt` -> `ClassPathResource`; any real URL scheme like `file:/etc/app.conf`, `http://host/x`, or `jar:...` -> `UrlResource` (it tries `new URL(location)`). A location with **no** prefix is resolved in a loader-specific way: the generic `DefaultResourceLoader` treats it as a classpath resource, while a filesystem context (`FileSystemXmlApplicationContext`) or `GenericApplicationContext` may treat it as a filesystem path. Every `ApplicationContext` *is* a `ResourceLoader`, which is why `@Value("classpath:...") Resource` injection works. The crucial detail is that `classpath:` (single colon) resolves exactly **one** resource — the first match on the classpath — whereas `classpath*:` enables wildcard scanning across all matches.

code

java · 17 lines
java
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Component;

@Component
class ConfigReader {
    private final ResourceLoader loader; // the ApplicationContext itself

    ConfigReader(ResourceLoader loader) { this.loader = loader; }

    Resource onClasspath() { return loader.getResource("classpath:config/app.yml"); }
    Resource onDisk()      { return loader.getResource("file:/etc/katajob/app.yml"); }
    Resource overHttp()    { return loader.getResource("https://cfg.example.com/app.yml"); }

    // Bare path: resolution depends on the concrete loader (classpath by default).
    Resource ambiguous()   { return loader.getResource("config/app.yml"); }
}

go deeper

for a junior

Should map classpath:/file:/http: to the right Resource type at a high level.

for a middle

Should know the no-prefix behavior is loader-specific and that getResource is lazy/non-null.

for a senior

Should connect ApplicationContext-is-a-ResourceLoader to @Value Resource injection and getResourceByPath override points.

for a principal

Reasons about deployment-portability implications of prefix choices and standardizes on explicit prefixes across the codebase.

## The ResourceLoader contract `org.springframework.core.io.ResourceLoader` is a strategy interface with essentially one method: ```java Resource getResource(String location); ClassLoader getClassLoader(); ``` It converts a **location string** into a `Resource`. It always returns a `Resource` handle — even for a location that doesn't exist (you call `exists()` to check). The mapping is driven by the location's prefix. ## Prefix rules (as implemented by DefaultResourceLoader) | Location example | Prefix | Resulting Resource | |---|---|---| | `classpath:com/app/x.xml` | `classpath:` | `ClassPathResource` | | `file:/data/x.xml` | `file:` (URL scheme) | `UrlResource` (via `new URL(...)`) | | `http://host/x.xml` | `http:` (URL scheme) | `UrlResource` | | `jar:file:/a.jar!/x.xml` | `jar:` | `UrlResource` | | `/data/x.xml` or `x.xml` | *(none)* | loader-specific default | The algorithm inside `DefaultResourceLoader.getResource`: 1. If it starts with `classpath:`, strip the prefix and build a `ClassPathResource`. 2. Else try to parse it as a `java.net.URL`. If that succeeds (there's a scheme like `file:`, `http:`, `ftp:`, `jar:`), build a `UrlResource`. 3. If URL parsing fails (no scheme — it's a bare path), fall back to `getResourceByPath(path)`, which the **default** implementation treats as a **classpath** resource (returns a `ClassPathContextResource`). Subclasses override this: `FileSystemXmlApplicationContext` / `FileSystemResourceLoader` treat the bare path as a **filesystem** path; `GenericWebApplicationContext` treats it relative to the servlet context. ## Every ApplicationContext is a ResourceLoader `ApplicationContext extends ResourcePatternResolver extends ResourceLoader`. So the container itself resolves locations. That's why: - `@Value("classpath:seed.json") Resource seed;` works — Spring's `ResourceEditor` / conversion uses the context as the loader. - `context.getResource("classpath:x")` and `applicationContext.getResources("classpath*:x")` work out of the box. - You can inject the loader itself: implement `ResourceLoaderAware` or just `@Autowired ResourceLoader loader;`. ## Why the default-no-prefix behavior bites people A bare `config/app.xml`: - In a `ClassPathXmlApplicationContext` / generic context -> resolved on the **classpath**. - In a `FileSystemXmlApplicationContext` -> resolved on the **filesystem**, relative to the working directory (and, confusingly, even a *leading slash* is still treated as relative for XML config contexts historically). To be unambiguous, **always use an explicit prefix** (`classpath:` or `file:`). ## file: absolute vs relative `file:/data/x` is absolute; `file:data/x` is relative to the JVM working directory. `UrlResource` just delegates to `java.net.URL`, so all normal URL rules apply, including that `getFile()` works only for `file:` URLs. ## classpath: is single-match `classpath:META-INF/x.txt` returns exactly one `Resource` — the first hit the `ClassLoader` finds. If the same path exists in multiple JARs and you need all of them, you need `classpath*:` with a `ResourcePatternResolver` (a separate question). ## Gotchas - `getResource` never returns null and never checks existence — always `resource.exists()` before reading. - A typo'd prefix like `classpath:/foo` (leading slash) is tolerated (the slash is stripped), but be consistent. - `http:`/`https:` resources depend on network availability and `contentLength()`/`lastModified()` may be unreliable. - Don't call `getFile()` on an `http:` or JAR-nested resource.

  • If getResource() is given a location that doesn't exist, what happens?
    It still returns a non-null Resource handle. Resolution is lazy — nothing is opened until you read it. You must call exists()/isReadable() yourself; reading a missing resource throws IOException.
  • How does @Value("classpath:x.json") Resource injection work under the hood?
    Spring registers a ResourceEditor/converter that uses the ApplicationContext (a ResourceLoader) to turn the string into a Resource, applying the same prefix rules as getResource().

saying these in an interview costs you the question

  • Saying getResource() returns null or throws when the resource is missing (it returns a lazy handle).
  • Assuming a bare path is always classpath (it depends on the concrete loader — filesystem for FileSystem contexts).
  • Claiming file: and http: produce ClassPathResource (they produce UrlResource).

context