Introduction
When using Spring Boot @Value annotation to load resources like XML files in a JavaFX project with Java 9 modules, you might encounter a FileNotFoundException. The issue arises when the resource is placed under src/main/resources/test/
instead of src/main/resources/static/test/
. Understanding Java module encapsulation rules is key to resolving this problem.
This article explores the root cause of the issue, why placing files under static/
works, and how to properly configure your project for seamless resource loading.
Understanding Java 9+ Module Resource Encapsulation
Java 9 introduced the module system, which encapsulates resources and restricts access to them. According to Java’s resource encapsulation rules:
- If a resource resides in a package inside a named module, it is encapsulated unless explicitly opened.
- A resource outside a package structure (e.g., in
META-INF/
orstatic/
) is not encapsulated and remains accessible. - Classloader-based resource retrieval methods require unconditional opens to work properly.
Why Resource Loading Fails in Java Modules
Consider the following project structure:
src/main/
├── java/com/example/
│ ├── App.java
│ ├── TestLoader.java
│ └── module-info.java
│
├── resources/
│ ├── application.properties
│ ├── test/test.xml ❌ (Encapsulated, leads to FileNotFoundException)
│ ├── static/test/test.xml ✅ (Accessible, loads successfully)
In this scenario:
test/test.xml
is encapsulated becausetest/
is interpreted as a package inside the module.- Spring Boot cannot access encapsulated resources unless explicitly allowed via
module-info.java
. - Files in
static/
work becausestatic
is not a valid package name, making its contents accessible.
Fixing the Issue: Solutions and Best Practices
1. Use static/
for Resource Storage
The easiest solution is to move your resource files into src/main/resources/static/
. This avoids encapsulation and ensures Spring Boot can load them correctly:
@Value("classpath:static/test/test.xml")
private Resource testXml;
2. Open the Package in module-info.java
If you want to keep the original structure (test/test.xml
), you must open the package to Spring Boot modules in module-info.java
:
module com.example {
requires spring.boot;
requires spring.boot.autoconfigure;
requires spring.context;
requires spring.core;
requires spring.beans;
opens com.example to spring.beans, spring.context, spring.core;
opens test; // Allow access to test package resources
}
3. Remove module-info.java
(Recommended for Spring Boot Projects)
Spring Boot is not fully modularized and relies on the classpath. If your application doesn’t require Java modules, deleting module-info.java
resolves the issue:
rm src/main/java/module-info.java
4. Use an Explicit ClassLoader
Another alternative is manually loading resources using the ClassLoader API, bypassing encapsulation restrictions:
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
InputStream inputStream = classLoader.getResourceAsStream("test/test.xml");
Conclusion
Java 9+ modules introduce resource encapsulation, impacting Spring Boot @Value resource loading. The safest workaround is to place resources in static/
, but you can also modify module-info.java
or remove it altogether.
Key Takeaways:
- Spring Boot struggles with encapsulated resources in named modules.
- Files in
static/
work because they are outside the Java package structure. - You can open packages in
module-info.java
to make resources accessible. - For non-modular projects, removing
module-info.java
is the easiest fix.
By following these best practices, you can seamlessly integrate Spring Boot, JavaFX, and Java 9+ modules while avoiding resource loading pitfalls.
Keywords: Spring Boot @Value Resource Loading, Java 9 Modules, JavaFX Resource Encapsulation, Spring Boot FileNotFoundException, Spring Boot JavaFX Integration, Spring Boot Java 17 Modules