What is the difference between the classpath: and classpath*: prefixes, and when do you need classpath*:?
answer
- single colon = getResource = first match
- star = getResources = ALL roots/JARs
- star != filename wildcard; it means 'all roots'
- must use ResourcePatternResolver.getResources()
- root-dir wildcard + classpath*: = fragile/slow
basics
~20 sclasspath: resolves a single resource — the first match found by the classloader. classpath*: (with a star) finds ALL resources with that path across every classpath entry and JAR. Use classpath*: to aggregate the same-named file from multiple modules.
solid answer
~40 s`classpath:` (single colon) uses `ClassLoader.getResource`, returning **one** resource — the first match on the classpath. `classpath*:` scans **every** classpath root and JAR via `ClassLoader.getResources`, returning **all** matches with that path. You need `classpath*:` when the same relative path exists in multiple JARs/modules and you want to load them all — the classic case is aggregating `META-INF/spring.factories`-style files or per-module config like `classpath*:META-INF/app-context.xml`. Both forms support Ant-style wildcards in the *filename* part (`classpath:config/*.xml`), but only `classpath*:` scans across *all roots*. To use either wildcard form you must call `ResourcePatternResolver.getResources(...)` (which returns `Resource[]`), not the single-valued `getResource(...)`. Gotcha: `classpath*:` with directory wildcards can miss resources in some JAR/classloader setups and is slower because it walks every jar.
code
java · 18 linesimport org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
public class ModuleConfigScanner {
public Resource[] findAllModuleConfigs() throws Exception {
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
// Aggregates the file from EVERY module JAR on the classpath.
Resource[] configs = resolver.getResources("classpath*:META-INF/module-beans.xml");
for (Resource r : configs) {
System.out.println(r.getDescription() + " exists=" + r.exists());
}
return configs;
// Contrast: resolver.getResource("classpath:META-INF/module-beans.xml")
// would return only the FIRST module's file.
}
}go deeper
May only know classpath: exists; classpath*: is beyond junior scope.
Should state that classpath*: finds all matches across JARs and requires getResources().
Should explain getResource vs getResources, Ant patterns, and the modular-aggregation use case.
Should articulate the classloader/nested-JAR reliability limits and performance trade-offs, and set conventions (anchor under a package, filename wildcards only).
## The core distinction - **`classpath:`** — backed by `ClassLoader.getResource(path)`, which returns the **first** matching URL. One resource, deterministic-but-order-dependent. - **`classpath*:`** — backed by `ClassLoader.getResources(path)`, which returns an **Enumeration of all** matching URLs across every classpath entry (every directory root and every JAR on the classpath). Zero-to-many resources. The star does **not** mean a filename wildcard. It specifically means "search **all** classpath roots, not just the first." You can still combine it with Ant-style path wildcards. ## Ant-style patterns Spring's `PathMatchingResourcePatternResolver` (the default `ResourcePatternResolver` in every `ApplicationContext`) supports Ant patterns in the location: - `?` — one character - `*` — any characters within a single path segment - `**` — any number of path segments (recursive) Examples: - `classpath:config/*.xml` — all `.xml` under `config/` on the **first** classpath root only. - `classpath*:config/*.xml` — all `.xml` under `config/` across **every** root/JAR. - `classpath*:META-INF/**/module-beans.xml` — recursive scan across all JARs. - `file:/data/**/seed-*.json` — filesystem recursive. ## Why you MUST use getResources() `ResourceLoader.getResource(String)` returns a single `Resource` and does **not** understand `classpath*:` or resolve wildcards to a set. To expand a pattern you need: ```java ResourcePatternResolver resolver = ...; // the ApplicationContext, or new PathMatchingResourcePatternResolver() Resource[] all = resolver.getResources("classpath*:META-INF/module-beans.xml"); ``` `ResourcePatternResolver extends ResourceLoader` and adds `Resource[] getResources(String locationPattern)`. Every `ApplicationContext` implements it. ## When you actually need classpath*: - **Modular aggregation**: each module JAR ships its own `META-INF/spring/*.xml` or a marker/config file, and you want to import *all* of them (`<import resource="classpath*:META-INF/spring/module-*.xml"/>`). - **Plugin discovery**: collect every `META-INF/services`-style or `spring.factories`-style descriptor. - **Property fragments**: merge per-module `application-*.properties` shipped in different JARs. If you use plain `classpath:` here, you'd silently pick up only the first module's file and miss the rest — a subtle, hard-to-debug bug. ## Important gotchas & limitations 1. **`classpath*:` with a root directory wildcard can be unreliable.** Spring's own docs warn that `classpath*:` combined with wildcards in the *directory* portion may fail to find resources in some environments, because it depends on `ClassLoader.getResources("")` returning JAR roots, which not all classloaders do (notably some app servers and certain packaging like Spring Boot's nested-JAR loader). A pattern like `classpath*:*.xml` (wildcard at the root) is the fragile case. 2. **Performance**: `classpath*:` walks every JAR and may open/scan them; it's markedly slower than a single lookup. Avoid it on hot paths. 3. **Ordering is not guaranteed**: the returned `Resource[]` order follows classpath/classloader order, which you should not rely on for precedence. 4. **Non-pattern `classpath*:` locations** (no wildcard, e.g. `classpath*:META-INF/x.txt`) still aggregate all matches via `getResources` — the star's aggregation applies even without a `*` in the filename. 5. **Only the last path segment is truly pattern-matched via the filesystem** in some JAR cases; deeply recursive `**` scans inside nested JARs have historically been limited. 6. **Spring Boot fat JARs**: because Boot repackages dependencies as nested JARs, `classpath*:` behavior relies on Boot's `LaunchedURLClassLoader`; simple `classpath*:META-INF/...` patterns work, but root-level wildcards are still risky. ## Rule of thumb Use `classpath:` for a single known file. Use `classpath*:` only when you deliberately want to collect the same-named resource from multiple modules, keep the wildcard in the *filename*, and prefer a concrete parent package (`classpath*:META-INF/spring/...`) over a bare-root wildcard.
- Can you use getResource() (singular) with a classpath*: pattern?No. getResource() returns a single Resource and doesn't expand classpath*: or wildcards into a set. You must use a ResourcePatternResolver's getResources(), which returns Resource[]. Every ApplicationContext is such a resolver.
- Why can classpath*:*.xml (wildcard at the classpath root) fail to find files?It relies on ClassLoader.getResources("") returning all classpath root URLs so Spring can enumerate them, but many classloaders (some app servers, Boot's nested-JAR loader) don't return JAR roots for the empty path. Anchoring under a concrete package like classpath*:META-INF/*.xml is reliable.
saying these in an interview costs you the question
- Believing the * in classpath*: is just a filename wildcard (it means 'all classpath roots').
- Using getResource() and expecting it to return multiple matches for classpath*:.
- Relying on the order of the returned Resource[] for precedence.
- Assuming classpath*: is free — it scans every JAR and is comparatively slow.