MFormations
Modern Java Engineering

Chapitre 24

24 - Tendances

> Java moderne : Valhalla, Loom, Panama, Leyden, Amber, GraalVM, Quarkus, Spring 4.0

Chapitre 24 — Tendances

Java moderne : Valhalla, Loom, Panama, Leyden, Amber, GraalVM, Kotlin, Quarkus, Spring 4.0


Introduction

Le Java ecosysteme est en pleine transformation. Entre 2020 et 2025, Java a connu des changements plus importants que dans les 15 annees precedentes. Ce chapitre couvre les projets OpenJDK majeurs et les tendances de l'ecosysteme.


1. Project Valhalla : Value Types et Primitive Classes

Objectif

Ameliorer la performance et le modele de donnees en introduisant des types valeur (value types) qui se comportent comme des primitives mais avec la flexibilite des objets.

JEPs liees

JEPTitreStatut
401Primitive Classes (Preview)En cours
402Value Objects (Preview)En cours

Concepts

  • Value Type : Objet immutable sans identite, alloue sur la stack (ou dans les champs), pas de monitor lock.
  • Primitive Class : Classe declaree avec primitive class. Ex : primitive class Point(int x, int y).
  • Flat Memory Layout : Pas de header d'objet, stockage contigu en memoire.

Avantages

  • Performance : Jusqu'a 10x plus rapide pour les tableaux de value types
  • Cache locality : Pas de pointeurs, chargement contigu en cache CPU
  • GC friendly : Moins d'objets alloues sur le heap

Exemple conceptuel

// Hypothetique : primitive class
primitive class Complex {
    private final double re;
    private final double im;

    public Complex(double re, double im) {
        this.re = re;
        this.im = im;
    }

    public Complex add(Complex other) {
        return new Complex(this.re + other.re, this.im + other.im);
    }
}

// Pas d'identite → pas de synchronisation possible
// Allocation sur la stack → pas de GC pressure
// Tableau de Complex → stockage contigu dans un buffer double[] interne

Impact

  • Les streams sur des collections de value types seront beaucoup plus rapides
  • Les structures de donnees numeriques (matrices, vecteurs) seront optimales
  • Les DDD Value Objects deviennent "gratuits" en performance

2. Project Loom : Virtual Threads et Structured Concurrency

Objectif

Rendre la concurrence en Java plus simple, plus performante et plus scalable.

JEPs finalisees

JEPTitreJDK
425Virtual Threads (Preview)19, 20
444Virtual Threads (Final)21
453Structured Concurrency (Preview)21+
446Scoped Values (Preview)21+

Virtual Threads

Principe : Thread leger (fiber) multiplie par la JVM sur un petit pool de threads OS (carrier threads).

// Avant Loom (thread pool)
ExecutorService executor = Executors.newFixedThreadPool(100);
executor.submit(() -> handleRequest(request));

// Avec Loom (virtual threads)
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    executor.submit(() -> handleRequest(request));
}

// Ou directement
Thread.startVirtualThread(() -> handleRequest(request));

Avantages :

  • Millions de threads (vs milliers avec les threads OS)
  • cout de creation quasi nul
  • Pas besoin de pool de threads
  • Compatible avec le code synchrone existant

Limitations :

  • synchronized peut causer du pinning (blocage du carrier thread)
  • Pas adapte aux taches CPU-bound
  • Ne remplace pas les threads OS pour tout

Structured Concurrency

Principe : La duree de vie des threads virtuels est structuree par un scope.

Response handle() throws ExecutionException, InterruptedException {
    try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
        Future<String> user = scope.fork(() -> findUser());
        Future<Integer> orders = scope.fork(() -> fetchOrders());
        scope.join();            // Attend les deux taches
        scope.throwIfFailed();   // Propage la premiere exception
        return new Response(user.resultNow(), orders.resultNow());
    } // Toutes les taches sont terminees ici (ou annulees)
}

Benefices :

  • Annulation automatique des sous-taches en cas d'erreur
  • Propagation des exceptions
  • Garantie que toutes les sous-taches sont terminees a la sortie du scope
  • Empeche les fuites de threads (taches oubliees)

Scoped Values

Alternative a ThreadLocal, concue pour les virtual threads :

private static final ScopedValue<String> REQUEST_ID = ScopedValue.newInstance();

void handle() {
    ScopedValue.where(REQUEST_ID, "req-123")
        .run(() -> {
            // Dans tous les appels imbriques : REQUEST_ID.get() == "req-123"
            process();
        });
}

Avantages sur ThreadLocal :

  • Pas de mutation possible (immutable pendant le scope)
  • Pas de cout de copie pour les virtual threads
  • Pas de fuite memoire (vs ThreadLocal non nettoye)

3. Project Panama : Foreign Memory et Function API

Objectif

Remplacer JNI par une API sure, performante et ergonomique pour interagir avec du code natif (C/C++) et la memoire hors-heap.

JEPs

JEPTitreStatut
424Foreign Function & Memory API (Preview)JDK 21
442Foreign Function & Memory API (Third Preview)JDK 22
454Foreign Function & Memory API (Final)JDK 22
460Vector API (Seventh Incubator)JDK 22

Foreign Memory API

// Allouer de la memoire off-heap
try (Arena arena = Arena.ofConfined()) {
    MemorySegment segment = arena.allocate(100);
    segment.set(ValueLayout.JAVA_INT, 0, 42);  // ecrire un int
    int value = segment.get(ValueLayout.JAVA_INT, 0);  // lire
}

Foreign Function API

// Lier une fonction C
Linker linker = Linker.nativeLinker();
SymbolLookup stdlib = linker.defaultLookup();
MemorySegment strlen = stdlib.find("strlen").orElseThrow();

MethodHandle strlenHandle = linker.downcallHandle(
    strlen,
    FunctionDescriptor.of(ValueLayout.JAVA_LONG, ValueLayout.ADDRESS)
);

// Appel
try (Arena arena = Arena.ofConfined()) {
    MemorySegment str = arena.allocateFrom("Hello");
    long len = (long) strlenHandle.invoke(str);
    System.out.println(len); // 5
}

Vector API

// Avant : boucle scalaire
void scalarAdd(float[] a, float[] b, float[] c) {
    for (int i = 0; i < a.length; i++) {
        c[i] = a[i] + b[i];
    }
}

// Avec Vector API (SIMD)
void vectorAdd(float[] a, float[] b, float[] c) {
    int i = 0;
    int VLENGTH = FloatVector.SPECIES_256.length();
    for (; i < a.length - VLENGTH; i += VLENGTH) {
        var va = FloatVector.fromArray(FloatVector.SPECIES_256, a, i);
        var vb = FloatVector.fromArray(FloatVector.SPECIES_256, b, i);
        va.add(vb).intoArray(c, i);
    }
    // Reste scalaire
    for (; i < a.length; i++) {
        c[i] = a[i] + b[i];
    }
}

4. Project Leyden : AOT Compilation et Static Images

Objectif

Ameliorer les temps de demarrage, le pic de performance et l'empreinte memoire via la compilation ahead-of-time et des optimisations au link time.

Concepts

  • AOT Compilation : Compilation du bytecode en code natif avant l'execution
  • Static Images : Executables autonomes sans JVM
  • Closed-World Analysis : Optimisations basees sur la connaissance de toutes les classes chargeables

Comparaison

ApprocheDemarragePeak perfEmpreinte
JIT (HotSpot)Lent (secondes)ExcellentMoyenne
AOT (GraalVM)InstantaneBonFaible
Leyden (AOT+)InstantaneExcellentFaible

Lien avec GraalVM

GraalVM native-image est un precurseur. Leyden apporte une solution standardisee dans le JDK lui-meme.

# Concept : compilation AOT avec Leyden
javac --enable-preview --aot -o myapp.aot MyApp.java
./myapp.aot  # Demarrage instantane

Application Spring

Spring Boot 3.3+ commence a supporter les AOT optimizations pour le demarrage rapide (utilisees pour les lambdas, les proxies, et la configuration conditionnelle).


5. Project Amber : Pattern Matching, Records, Switch

Objectif

Rendre le langage Java plus expressif et concis avec des fonctionnalites de matching structurel.

JEPs finalisees

JEPTitreJDK
395Records16
440Record Patterns (Preview)19-20
441Record Patterns (Final)21
443Unnamed Patterns & Variables21
445String Templates (Preview)21
456Anonymous Classes Improvement22

Pattern Matching evolution

// Java 16 : instanceof pattern matching
if (obj instanceof String s) {
    System.out.println(s.length());
}

// Java 17+ : switch pattern matching
String result = switch (obj) {
    case String s when s.length() > 5 -> "Long string: " + s;
    case String s -> "Short string";
    case Integer i -> "Number: " + i;
    case null -> "null";
    default -> "Unknown";
};

// Java 21 : record patterns
if (obj instanceof Point(int x, int y)) {
    System.out.println(x + ", " + y);
}

// Java 21 : nested record patterns
if (obj instanceof Rectangle(Point(int x, int y), Size(int w, int h))) {
    System.out.println("Rectangle at " + x + "," + y);
}

// Java 21 : unnamed patterns
switch (obj) {
    case Point(int x, _) -> System.out.println("x = " + x);
    case Circle(_) -> System.out.println("A circle");
}

String Templates (Preview)

// Java 21 (preview) : string templates
String name = "World";
String greeting = STR."Hello \{name}!";  // "Hello World!"

// Templates avec expressions
int x = 10, y = 20;
String result = STR."\{x} + \{y} = \{x + y}"; // "10 + 20 = 30"

// Template processors personnalises
JSONObject json = JSON."""
{
    "name": "\{name}",
    "age": \{age}
}
""";

6. GraalVM : Native Image et Polyglot

Presentation

GraalVM est une VM haute-performance developpee par Oracle Labs. Elle permet :

  • La compilation native (native-image)
  • L'execution polyglotte (Java + JS + Python + Ruby)
  • La compilation JIT optimisee (Graal JIT)

Native Image

// Application standard Spring Boot
// Build : mvn -Pnative native:compile
// Result : executable natif de ~50 Mo (vs ~200 Mo JRE + ~20 Mo JAR)
// Demarrage : ~50ms (vs ~3s avec JVM)
// Empreinte : ~30 Mo RAM (vs ~200 Mo avec JVM)

@RestController
public class HelloController {
    @GetMapping("/hello")
    public String hello() {
        return "Hello, GraalVM!";
    }
}

Limitations :

  • Reflection necessite configuration (reflect-config.json)
  • Pas de chargement dynamique de classes
  • Pas de agents JMX/JVMTI
  • Certains patterns Spring ne marchent pas (proxy AOP sur des methodes non-publiques)

Outils :

  • GraalVM Tracing Agent (-agentlib:native-image-agent=config-output-dir=...)
  • Spring Boot AOT engine (automatise la configuration)
  • Hibernate compile-time proxies

Polyglot

// Java appelant JavaScript
try (Context context = Context.create("js")) {
    Value result = context.eval("js", "1 + 2");
    System.out.println(result.asInt()); // 3
}

// Java appelant Python
try (Context context = Context.create("python")) {
    context.eval("python", "import sys; print(sys.version)");
}

Truffle Framework

Framework pour implementer des langages sur GraalVM. Permet de creer des interpreters performants qui beneficient automatiquement du JIT compiler de GraalVM.

Langages sur Truffle : JavaScript (GraalJS), Python (GraalPython), Ruby (TruffleRuby), R (FastR), LLVM (Sulong), WASM (GraalWasm).


7. Kotlin pour Java Developers

Pourquoi Kotlin ?

  • Concise : 40% moins de code que Java
  • Null Safety : Types nullables explicites
  • Interoperable : Compatible Java a 100%
  • Coroutines : Async simplifie
  • Google official : Langage Android

Differences Java vs Kotlin

// Java
public class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() { return name; }
    public int getAge() { return age; }
}
// Kotlin
data class Person(val name: String, val age: Int)

Null Safety

var name: String = "Hello"    // Non-nullable
var nullable: String? = null  // Nullable

// Safe call
val length = nullable?.length  // null si nullable est null

// Elvis operator
val len = nullable?.length ?: 0

// Force unwrap (si vous etes sur)
val forced = nullable!!.length  // NullPointerException si null

Coroutines

suspend fun fetchUser(id: Int): User {
    delay(1000) // Non-bloquant
    return User(id, "John")
}

suspend fun fetchOrders(user: User): List<Order> {
    delay(500)
    return listOf(Order(1), Order(2))
}

fun main() = runBlocking {
    // Appels sequentiels
    val user = fetchUser(1)
    val orders = fetchOrders(user)

    // Appels paralleles
    val (user2, orders2) = async { fetchUser(2) } to async { fetchOrders(user2) }
}

Kotlin + Spring Boot

@SpringBootApplication
class Application

fun main(args: Array<String>) {
    runApplication<Application>(*args)
}

@RestController
@RequestMapping("/api/users")
class UserController(private val userService: UserService) {

    @GetMapping
    fun list() = userService.findAll()

    @PostMapping
    fun create(@Valid @RequestBody user: User) = userService.save(user)
}

8. Quarkus : Supersonic Subatomic Java

Presentation

Framework Java concu pour Kubernetes et le cloud, avec :

  • Build time processing (tout est resolu a la compilation)
  • GraalVM native integration
  • Container-first (petite empreinte)
  • Reactive par defaut (Vert.x)

Comparaison Spring Boot vs Quarkus

CritereSpring Boot 3Quarkus 3
Demarrage (JVM)~3s~0.5s
Demarrage (Native)~50ms~30ms
Empreinte (JVM)~200 Mo~100 Mo
Empreinte (Native)~50 Mo~30 Mo
First request (JVM)~5s~1s
First request (Native)~50ms~30ms
Dev modeLive reloadDev UI + Live reload
ReactifOptionnel (WebFlux)Par defaut (Vert.x)
FrameworkSpring ecosystemeExtension ecosysteme

Dev Mode

# Quarkus dev mode : changements de code visibles instantanement
mvn quarkus:dev

# Dev UI : interface web avec outils integres
# http://localhost:8080/q/dev/
# - Console de base de donnees
# - Endpoints REST
# - Configurations
# - Health checks

Exemple Quarkus

@Path("/hello")
public class HelloResource {

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String hello() {
        return "Hello Quarkus!";
    }

    @GET
    @Path("/greeting/{name}")
    @Produces(MediaType.APPLICATION_JSON)
    public Greeting greeting(@PathParam("name") String name) {
        return new Greeting("Hello " + name);
    }
}

9. Spring 4.0 et l'avenir

Ce que Spring 4.0 pourrait apporter

Spring Boot 3.x est la base. Spring 4.0 (probablement 2025-2026) pourrait inclure :

  • Virtual threads par defaut : Remplacement des thread pools par defaut
  • AOT compilation native : Integration plus profonde avec GraalVM / Leyden
  • Reactive et Imperatif unifies : Un modele de programmation unique
  • Spring Data 4.0 : Support renforce pour les BD modernes (CockroachDB, Spanner)
  • Spring Security 7.0 : Simplification de la configuration OAuth2/OIDC
  • Native-first : Les applications Spring compilees nativement par defaut

Tendances actuelles Spring

// @ServiceConnection (Spring Boot 3.2+)
// Connection automatique aux services Testcontainers
@Container
@ServiceConnection
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:16");

// RestClient (Spring Boot 3.2+)
// Alternative synchrone a WebClient et RestTemplate
RestClient client = RestClient.create();
String result = client.get()
    .uri("https://api.example.com/users/1")
    .retrieve()
    .body(String.class);

// Problem Details (RFC 9457)
// Standardisation des reponses d'erreur
@ExceptionHandler(EntityNotFoundException.class)
ProblemDetail handleNotFound(EntityNotFoundException ex) {
    return ProblemDetail.forStatusAndDetail(HttpStatus.NOT_FOUND, ex.getMessage());
}

RestTemplate → RestClient

// Ancien (deprecated depuis 3.x)
RestTemplate template = new RestTemplate();
User user = template.getForObject("/api/users/{id}", User.class, 1L);

// Nouveau (Spring Boot 3.2+)
RestClient client = RestClient.create();
User user = client.get()
    .uri("/api/users/{id}", 1L)
    .retrieve()
    .body(User.class);

// Avec configuration
RestClient client = RestClient.builder()
    .baseUrl("https://api.example.com")
    .defaultHeader("Authorization", "Bearer " + token)
    .defaultStatusHandler(HttpStatusCode::isError, (req, res) -> {
        throw new MyException(res.getStatusText());
    })
    .build();

10. Tendances additionnelles

Java sur Kubernetes

  • Dapr : Sidecar pour microservices (state, pub/sub, actors)
  • K8s Operators : Automatisation des operations
  • Serverless : AWS Lambda SnapStart, Google Cloud Run
  • Service Mesh : Istio, Linkerd pour la communication inter-services

AI/ML avec Java

  • DJL (Deep Java Library) : Framework DL pour Java (Amazon)
  • TensorFlow Java : API Java pour TensorFlow
  • ONNX Runtime : Inference ONNX en Java
  • LangChain4j : Integration LLM pour Java

Observabilite

  • OpenTelemetry : Standard pour traces, metrics, logs
  • eBPF : Observabilite au niveau kernel
  • Continuous Profiling : Profiling en production permanent

Build Tools

  • Maven 5 : Performance amelioree, parallel builds
  • Gradle 9 : Configuration cache, configuration avoidance
  • Bazel : Build scale pour les grands monorepos

Conclusion : Roadmap Java 2025+

PeriodeTechnologies a maitriser
2024-2025Virtual threads, Pattern matching, Records, GraalVM
2025-2026Value types (Valhalla), Foreign Memory API, Structured Concurrency
2026-2027Value types en production, AOT standard (Leyden), Vector API
2027+Java compile nativement par defaut, Zero-overhead abstractions

Recommandations

  1. Adoptez virtual threads des maintenant — C'est la plus grande avancee
  2. Utilisez les records partout — DTOs, Value Objects, events
  3. Migrez vers Java 21 LTS — La nouvelle base
  4. Explorez GraalVM native — Pour les microservices et serverless
  5. Suivez les JEPs — Valhalla et Panama arrivent
  6. Ne negligez pas Kotlin — Complementaire a Java

Java n'a jamais ete aussi dynamique. La technologie Java 2025+ est radicalement differente de Java 8. Les ingenieurs qui maitrisent ces nouvelles capacites seront les leaders de la prochaine decennie.