Key Takeaways
- Java 25 LTS, released in September 2025, is the new baseline. It is the first LTS to include the virtual thread pinning fix and finalized Scoped Values.
- Oracle’s no-fee terms for JDK 21 end in September 2026, which makes migration a budget item rather than a backlog item.
- Virtual threads became genuinely production-ready, and the reason is JDK 24’s JEP 491, which removed the carrier-thread pinning problem that blocked adoption.
- Spring Boot 4.0 shipped in November 2025 on Spring Framework 7, with Java 17 as the minimum and Java 25 recommended.
- Java is still one of the most prominent languages of banking infrastructure, which is why most of these changes land hardest in financial services.
Java turned thirty in 2025, and the version most enterprise teams are running still behaves like it is 2018.
The language has changed substantially between Java 8 and Java 25, concurrency works differently now, Spring Boot had a major version bump, and a licensing deadline in September 2026 is about to force a decision on anyone still sitting on Java 21.
Here are the nine trends worth your attention, with the version numbers attached, including:
- Java 25 LTS becomes the production baseline
- The September 2026 licensing deadline
- Virtual threads reach production maturity
- Spring Boot 4 and Spring Framework 7
- AOT compilation closes the startup gap
- Data-oriented programming lands in everyday code
- Java becomes AI infrastructure
- The framework field broadens
- The Jakarta EE namespace migration tail
At Trio, we have a variety of Java specialists on hand, including those with expertise in massive enterprise applications in industries like fintech.
1. Java 25 LTS becomes the production baseline
Java 25 arrived in September 2025 as the latest long-term support release, following Java 21 in September 2023 and Java 17 in September 2021.
If your team is choosing between the two current LTS options, Java 25 is the more sensible target. There are a couple of really prominent reasons for that.
JDK 24 shipped JEP 491, which fixed the carrier-thread pinning problem in virtual threads, and Java 25 is the first LTS release to include it.
Scoped Values were also finalized in Java 25 through JEP 506, giving a proper replacement for ThreadLocal in a virtual-threaded world. Structured concurrency remains in preview.
Practically, this means that the concurrency story is meaningfully different on either side of the 21-to-25 gap. If your team is adopting virtual threads on Java 21, it inherits a known limitation that a team on Java 25 does not.
What to do: if you are on Java 17, the jump to 25 is largely a version bump plus dependency checks. If you are on Java 8 or 11, budget for a longer project, since the module system and the javax-to-jakarta namespace change both bite on that path.
2. The September 2026 licensing deadline
Oracle’s No-Fee Terms and Conditions license for JDK 21 ends in September 2026.
After that, continuing to use Oracle’s JDK 21 builds in production without a commercial subscription becomes a licensing question rather than a technical one.
There are a couple of routes that exist for you here.
- Migrating to Java 25 LTS keeps you on no-fee Oracle terms for a further period.
- Switching to an alternative distribution such as Eclipse Temurin, Amazon Corretto, Azul Zulu, or Red Hat’s build removes the Oracle licensing question entirely, and these are built from the same OpenJDK source.
- Buying an Oracle Java SE subscription is the third option, and the per-employee pricing model Oracle moved to in 2023 made it considerably more expensive for large organizations than the older per-processor model.
3. Virtual threads reach production maturity
Virtual threads were finalized in Java 21 through JEP 444, and the early reaction was enthusiastic, but we noticed that the production reality took another two years to settle.
The problem was carrier-thread pinning.
When a virtual thread hit a synchronized block and then blocked on I/O, it pinned the underlying platform thread rather than unmounting, which could exhaust the carrier pool and stall throughput.
The workaround was replacing synchronized with ReentrantLock across your codebase and your dependencies, which nobody wanted to do.
JEP 491 in JDK 24 removed that limitation, and synchronized now works correctly with virtual threads. That is what moved them from promising to practical.
In Spring Boot, enabling them is one line:
spring.threads.virtual.enabled=true
You still need to keep in mind that this is still opt-in and defaults to false in both Spring Boot 3.5 and 4.0. Spring’s documentation requires Java 21 or later and recommends Java 24 or later for the best experience.
Be very careful not to jump in before taking the risks into account, though.
Virtual threads help I/O-bound concurrency and do nothing for CPU-bound work. And they relocate bottlenecks rather than removing them, since a service that can suddenly handle far more concurrent requests will push that load onto a database connection pool or a downstream API that was never sized for it.
Bounded concurrency still needs to exist somewhere.
4. Spring Boot 4 and Spring Framework 7
Spring Boot 4.0 reached general availability in November 2025, built on Spring Framework 7, nd remains the dominant choice in enterprise Java by a wide margin.
The new release brought a Java 17 minimum with Java 25 recommended, alignment with Jakarta EE 11, null safety through JSpecify annotations, updated API versioning, deeper Project Leyden integration, and stronger virtual thread support.
How you can upgrade is going to depend on where you are starting.
For example, Java 17 with Spring Boot 3.x to Java 21 or 25 with Spring Boot 4 is largely a version bump, typically a matter of weeks. Java 11 with Spring Boot 2.x is a longer project, since you pass through the Spring Boot 3 javax-to-jakarta migration on the way.
5. AOT compilation closes the startup gap
Java’s slow startup was a real disadvantage in serverless and container environments, where a cold start measured in seconds costs money and user patience, but now that gap has narrowed considerably.
GraalVM native image compiles Java ahead of time into a native executable, cutting startup from seconds to milliseconds and reducing memory footprint substantially.
The trade-off is build complexity, longer build times, and constraints around reflection that require configuration or framework support.
Project Leyden takes a different approach inside the JDK itself, shifting work earlier through ahead-of-time class loading and linking without requiring a separate native image toolchain. Spring Framework 7 integrates with it.
If your team is running Java in Kubernetes with aggressive autoscaling, or in serverless functions, this changes the calculation. Java might have been a poor fit for those environments for a decade and is now a reasonable one.
6. Data-oriented programming lands in everyday code
The language additions since Java 8 mostly point in one direction: describing data without ceremony.
- Records give you immutable data carriers in a line.
- Sealed classes let you close a type hierarchy so the compiler knows every possible subtype.
- Pattern matching for switch and record patterns, both standard since Java 21, let you destructure and branch on those types directly.
Together they support a style that reads far closer to functional languages than Java traditionally allowed:
sealed interface PaymentEvent permits Authorised, Declined, Settled {}
record Authorised(String id, BigDecimal amount) implements PaymentEvent {}
record Declined(String id, String reasonCode) implements PaymentEvent {}
record Settled(String id, Instant at) implements PaymentEvent {}
String describe(PaymentEvent event) {
return switch (event) {
case Authorised(var id, var amount) -> “Authorised %s for %s”.formatted(id, amount);
case Declined(var id, var reason) -> “Declined %s: %s”.formatted(id, reason);
case Settled(var id, var at) -> “Settled %s at %s”.formatted(id, at);
};
}
The compiler verifies exhaustiveness, so adding a fourth event type produces a compile error rather than a runtime surprise.
If you’re modeling state transitions, which describes a large share of financial software, this is a meaningful improvement over enums and instanceof chains.
7. Java becomes AI infrastructure
The assumption that AI work happens in Python holds for model training and breaks down for application integration.
Most enterprises wanting to add LLM features have their business logic, data access, and security model already written in Java, and rewriting that in Python to call an API makes little sense.
Two projects address this directly.
Spring AI brings model integration, embeddings, vector store abstractions, and retrieval-augmented generation into the Spring programming model. LangChain4j offers a similar toolkit independent of Spring.
Model Context Protocol support has also arrived in the Java ecosystem, letting Java services expose tools and data to AI agents through a standard interface.
Realistically, Java is not competing with Python for data science but is becoming the integration layer where AI capability meets existing enterprise systems.
8. The framework field broadens
Spring Boot dominates, and the alternatives have found genuine niches rather than fading.
- Quarkus optimizes for container and serverless deployment, with fast startup, low memory use, and strong GraalVM integration built in from the start. Red Hat backs it, and it shows up often in OpenShift environments.
- Micronaut takes a similar performance position with compile-time dependency injection, avoiding the reflection that slows startup and complicates native compilation.
- Helidon targets microservices with both a reactive and a virtual-thread-based programming model.
Each makes the same bet that startup time and memory footprint matter more in modern deployment than they did when applications ran on long-lived application servers.
9. The Jakarta EE namespace migration tail
Several years after the javax-to-jakarta package rename, a surprising number of codebases have not finished it.
Jakarta EE 11 is the current target, and Spring Boot 4 aligns with it.
For anyone still on Spring Boot 2.x, this migration is unavoidable on the way to anything current, and it tends to be the step that turns a version upgrade into a project, because third-party dependencies have to move too.
Tooling helps.
OpenRewrite automates a large share of the mechanical changes, and running it early gives you a realistic estimate rather than a guess.
Java in fintech: why these changes land hardest here
Java runs a substantial share of global financial infrastructure.
Core banking platforms, trading systems, payment processors, card networks and risk engines are disproportionately Java, partly because the JVM delivered the stability and tooling that regulated environments require, and partly because thirty years of accumulated systems do not get rewritten cheaply.
That concentration means the trends above have sharper consequences in financial services than elsewhere.
- Virtual threads matter more where latency budgets are tight: A payment authorization path that handles far more concurrent requests per instance changes capacity planning directly.
- The licensing deadline is a compliance item: Running unlicensed software in a regulated environment is the kind of finding that shows up in an audit report.
- Pattern matching and sealed types suit financial domain modeling well: Payment states, order lifecycles, and settlement flows all have a fixed set of legal states, and compiler-enforced exhaustiveness beats a runtime check.
- AOT compilation changes the economics of scaling: Risk calculations and batch reconciliation that spin up compute on demand benefit directly from startup measured in milliseconds.
- Older Java versions carry security exposure: Running an LTS past its support window means no patches for newly disclosed vulnerabilities.
A practical migration sequence
If you are planning modernization work, the order tends to matter more than the destination:
- Audit what you are running: Java version, Spring version, and which dependencies block an upgrade. The blockers are usually two or three libraries rather than the application itself.
- Run OpenRewrite against a branch to size the javax-to-jakarta work with real numbers.
- Move the runtime before the framework where you can. Running existing code on a newer JDK is lower risk than changing both at once.
- Enable virtual threads behind a flag and load test before committing. Measure the downstream resources, not just the service.
- Bound your concurrency explicitly before virtual threads remove the implicit bound your thread pool was providing.
- Defer native image until the rest is stable, since it adds build complexity that complicates debugging everything else.
Hiring for modern Java
The gap between Java-the-language-people-learned and Java-as-it-now-works is the practical constraint on most modernization projects.
While there are plenty of Java engineers, few have carried a production system through a virtual thread rollout or a Jakarta migration and know where the problems hide.
At Trio, we place senior Java developers who have done this work inside regulated financial environments, available through staff augmentation or as a dedicated team.
If you are planning a migration or scaling a Java team, request a consult.

Alex Kugell




