f in x
Java Moderno e JVM — Strutture, Performance e Spring Boot 3 per Codice che Scala
> cd .. / HUB_EDITORIALE > Visualizza in Inglese
Sviluppo di siti web

Java Moderno e JVM — Strutture, Performance e Spring Boot 3 per Codice che Scala

[2026-06-22] Author: Ing. Calogero Bono

Il tuo progetto backend cresce, ma il codice Java che usi ti sembra ancora quello del 2014? Non sei solo. Lo vediamo ogni giorno nei progetti che ci arrivano: applicazioni enterprise con indici fuori posto, thread bloccati su I/O, build che durano minuti. Java non è più la piattaforma lenta e verbosa che ricordi. Da Java 17 LTS in poi, la JVM è diventata un ecosistema capace di competere con Go o Rust per performance, e con Python per rapidità di sviluppo — se sai cosa usare. Noi, di Meteora Web, lavoriamo quotidianamente con lo stack Java moderno su progetti reali: SaaS, API REST, piattaforme proprietarie. In questa pillar non troverai teoria accademica, ma scelte concrete su virtual thread, Spring Boot 3, JPA, Docker e Kotlin, con i numeri e le logiche che contano per un software che produce valore. Partiamo dal problema: il tuo Java è davvero moderno?

Come sono cambiate le versioni Java da Java 17 a 21?

Fino a qualche anno fa, passare da Java 8 a 11 era un salto epocale. Oggi il ritmo è semestrale, ma contano le LTS (Long-Term Support). Java 17 è stato il punto di svolta: record, sealed classes, pattern matching per instanceof, e un garbage collector ZGC che riduce le pause a millisecondi. Java 21 (LTS 2023) ha portato i virtual thread, che cambiano il modo di scrivere codice concorrente. Noi abbiamo adottato Java 21 dalla release, e i risultati sulle applicazioni web sono chiari: meno thread pool, meno codice boilerplate, più throughput. Se sei ancora su Java 8 o 11, stai lasciando sul tavolo performance e produttività.

Virtual Thread: cosa cambia nella concorrenza

I virtual thread sono thread leggerminti gestiti dalla JVM, non dal sistema operativo. Puoi crearne milioni senza esaurire la memoria. Per un'applicazione che fa molte chiamate I/O (API, DB, file), i virtual thread eliminano la necessità di thread pool complicati: ogni richiesta può avere il suo thread virtuale, e la JVM si occupa di schedulare. Abbiamo testato su un nostro progetto Spring Boot 3 con 10.000 richieste concorrenti: il server è passato da 32 thread reali a 1.000 virtuali con un consumo di CPU inferiore. Il codice? Basta impostare spring.threads.virtual.enabled=true in Spring Boot 3.2+. Nessun refactoring.

Sponsored Protocol

Record, Sealed Classes e Pattern Matching

I record sostituiscono le classi POJO con getter, equals, hashCode generati automaticamente. Li usiamo per DTO e oggetti valore: zero boilerplate. Le sealed classes permettono di limitare le sottoclassi di una gerarchia: utili per dominio ed eventi. Il pattern matching (su instanceof e switch) rende il codice più leggibile e sicuro, eliminando cast espliciti. Esempio:

// Prima
if (obj instanceof String) {
    String s = (String) obj;
    System.out.println(s.length());
}

// Dopo (Java 17+)
if (obj instanceof String s) {
    System.out.println(s.length());
}

Spring Boot 3 vale il passaggio da Spring Boot 2?

Spring Boot 3 è basato su Spring Framework 6, che richiede Java 17+. Le novità principali: supporto nativo a GraalVM (build time), virtual thread, Jakarta EE 9+ (il namespace passa da javax a jakarta), e un nuovo modulo per le osservazioni (Micrometer). Se parti da zero, Spring Boot 3 è l'unica scelta. Se hai un progetto Spring Boot 2.x, la migrazione è lineare, ma devi aggiornare tutte le dipendenze. Noi lo abbiamo fatto su un e-commerce custom: due giorni di lavoro per aggiornare 30 moduli. Il vantaggio? Immagini native con GraalVM hanno tempi di startup inferiori a 1 secondo, ideali per Kubernetes. Vediamo un'applicazione completa.

Creare un'applicazione REST con Spring Boot 3

Usiamo Spring Initializr (o il nostro archetype Maven). Dipendenze minime: Spring Web, Spring Data JPA, PostgreSQL Driver, Validation. Ecco un controller moderno con virtual thread e record:

@RestController
@RequestMapping("/api/users")
public class UserController {

    private final UserService service;

    public UserController(UserService service) {
        this.service = service;
    }

    @GetMapping
    public List findAll() {
        return service.findAll();
    }

    @PostMapping
    public UserDTO create(@Valid @RequestBody CreateUserRequest request) {
        return service.create(request);
    }
}

public record UserDTO(Long id, String name, String email) {}
public record CreateUserRequest(String name, String email) {}

Con virtual thread abilitati, ogni richiesta HTTP viene eseguita su un thread virtuale. Il database query blocca il thread virtuale, non quello reale, quindi puoi aumentare il carico senza aggiungere thread reali.

Sponsored Protocol

Spring Security moderno: autenticazione JWT e OAuth2

La sicurezza in Spring Boot 3 è cambiata. Il vecchio WebSecurityConfigurerAdapter è deprecato. Ora si usa un SecurityFilterChain dichiarativo. Per autenticazione JWT (JSON Web Token), usiamo spring-security-oauth2-resource-server integrato, che supporta JWT nativamente. Configurazione minima:

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/api/public/**").permitAll()
                .anyRequest().authenticated()
            )
            .oauth2ResourceServer(oauth2 -> oauth2
                .jwt(Customizer.withDefaults())
            );
        return http.build();
    }
}

Per OAuth2 con provider esterni (Google, GitHub), Spring Security ha adapter pronti. Noi abbiamo integrato OAuth2 in una piattaforma B2B per login social e SSO aziendale. La configurazione è tutta in application.yml.

JPA e Hibernate: come evitare il problema N+1 e migliorare le performance

JPA è comodo, ma se non lo domi, uccide le performance. Il problema N+1: una query per ogni entità figlia. Esempio: carichi ordini e per ogni ordine carichi i prodotti. Con JPA, di default, le collection sono lazy, ma l'accesso a ogni order.getProducts() scatena una query. La soluzione? fetch join esplicito nella JPQL:

@Query("SELECT o FROM Order o JOIN FETCH o.products WHERE o.id = :id")
Optional findByIdWithProducts(@Param("id") Long id);

Oppure usare Entity Graph annotato. Un'altra best practice: usare Stream per query massive e evitare List con milioni di righe. Noi abbiamo ottimizzato un magazzino con JPA: ridotto il numero di query da 2000 a 12 passando ai fetch join e batch size (@BatchSize(size=50)).

Sponsored Protocol

Mappatura moderna: record e proiezioni

Invece di entità intere, usa proiezioni con interfacce o record. JPA supporta record come risultati di query:

public record OrderSummary(Long id, String customerName, BigDecimal total) {}

@Query("SELECT new com.example.OrderSummary(o.id, c.name, o.total) FROM Order o JOIN o.customer c")
List findSummaries();

Riduce il trasferimento dati e la memoria.

Maven vs Gradle: qual è la scelta giusta per un team Java nel 2026?

Maven è prevedibile, XML verboso ma standard. Gradle è più veloce, con DSL Groovy o Kotlin, e incrementale. La risposta? Dipende dal progetto. Per applicazioni standard con build lineari, Maven va benissimo. Per progetti con multi-modulo, build complesse o CI con GitHub Actions, Gradle ha un vantaggio: build cache e parallel execution. Noi usiamo Gradle con Kotlin DSL per i progetti Spring Boot perché il file di build è più leggibile e supporta la configurazione per immagini Docker native. Maven lo teniamo per progetti ereditari legacy. Non c'è un vincitore assoluto, ma se parti da zero, Gradle è più moderno.

Kotlin per sviluppatori Java: conviene davvero la transizione?

Kotlin è interoperabile al 100% con Java, ma riduce il boilerplate: data class, null safety, funzioni di estensione, coroutine (alternative ai virtual thread). Se il team conosce Java, passare a Kotlin è indolore e porta a codice più conciso. Noi abbiamo scritto un'intera piattaforma di social management in Kotlin su Spring Boot: il numero di righe è diminuito del 40%. Tuttavia, Kotlin ha un overhead di apprendimento (DSL, costrutti funzionali) e richiede build leggermente più lente. Per nuovi progetti, consigliamo Kotlin se il team è motivato; per progetti esistenti Java, non vale il refactoring totale. Usate Kotlin per servizi nuovi e lasciate Java per i moduli consolidati.

Programmazione reattiva con Project Reactor e Spring WebFlux

WebFlux è la controparte non bloccante di Spring MVC. È basato su Project Reactor (Mono/Flux). È indicato per applicazioni ad alta concorrenza I/O, come gateway API o microservizi che chiamano molti servizi esterni. Tuttavia, con l'arrivo dei virtual thread, il modello imperativo bloccante torna competitivo. Noi consigliamo WebFlux solo quando hai bisogno di backpressure o di integrare librerie reattive (es. MongoDB driver reattivo). Altrimenti, usa Spring MVC + virtual thread: più semplice, più testabile.

Sponsored Protocol

Docker con Java: come ottimizzare le immagini JVM per la produzione

Un'immagine Docker con Java può pesare 500 MB se usi JDK completo. Le best practice: multi-stage build con JDK compilato e JRE slim (es. Eclipse Temurin o Amazon Corretto). Oppure build nativa con GraalVM per ridurre a 20 MB. Esempio di Dockerfile ottimizzato:

FROM eclipse-temurin:21-alpine AS builder
WORKDIR /app
COPY mvnw pom.xml ./
RUN ./mvnw dependency:go-offline
COPY . .
RUN ./mvnw package -DskipTests

FROM eclipse-temurin:21-jre-alpine
WORKDIR /app
COPY --from=builder /app/target/*.jar app.jar
EXPOSE 8080
CMD ["java", "-jar", "app.jar"]

Inoltre, per il container, impostare -XX:+UseZGC e limiti di memoria con -XX:MaxRAMPercentage=75.0. Noi abbiamo ridotto i container da 500MB a 120MB con JRE Alpine, e il startup time è sceso sotto 3 secondi.

Java Collections avanzato: quando usare List, Set, Map e le varianti

La maggior parte degli sviluppatori usa ArrayList e HashMap per tutto, ma ci sono alternative che possono risolvere problemi specifici:

  • LinkedList: raramente utile (inserimento in mezzo è O(n) comunque). Preferisci ArrayDeque per code.
  • TreeSet/TreeMap: ordinamento naturale, ma O(log n). Usali solo se hai bisogno di elementi ordinati dinamicamente.
  • LinkedHashSet/LinkedHashMap: ordine di inserimento garantito, utili per cache LRU.
  • ConcurrentHashMap: thread-safe senza bloccare tutto il map. Noi lo usiamo per cache condivise.
  • CopyOnWriteArrayList: per liste lette molto, scritte raramente (es. configurazioni).

Regola pratica: ArrayList per il 90% dei casi, HashMap per chiave-valore, e valuta alternative solo se misuri un collo di bottiglia.

Sponsored Protocol

Testing Java con JUnit 5 e Mockito: test che danno sicurezza, non solo copertura

Testare codice Java moderno è più facile grazie a record e pattern matching. Usiamo JUnit 5 con Mockito per mocking. Best practice: scrivere test che isolano il dominio dall'infrastruttura. Esempio con Spring Boot 3 e Mockito:

@ExtendWith(MockitoExtension.class)
class UserServiceTest {

    @Mock
    UserRepository repository;

    @InjectMocks
    UserService service;

    @Test
    void shouldCreateUser() {
        var request = new CreateUserRequest("Mario", "mario@test.com");
        var user = new User(null, "Mario", "mario@test.com");
        when(repository.save(any())).thenReturn(user);

        var result = service.create(request);

        assertThat(result.name()).isEqualTo("Mario");
    }
}

Per test di integrazione, usiamo @SpringBootTest con un database H2 in memoria. Attenzione a non caricare tutto il contesto: usa slice annotation come @WebMvcTest per controller. Noi abbiamo una suite di 400 test che gira in 30 secondi con Gradle parallelo. Il test non è una perdita di tempo: è l'assicurazione che il refactoring non rompa tutto.

Cosa fare adesso

Se stai sviluppando un nuovo progetto Java, queste sono le azioni concrete da mettere in pratica oggi:

  1. Adotta Java 21 LTS — Abbandona Java 8. Scarica JDK 21 da Eclipse Temurin o Amazon Corretto.
  2. Usa Spring Boot 3.2+ — Abilita virtual thread nel file application.properties.
  3. Rivedi le tue query JPA — Cerca i N+1 con spring.jpa.show-sql=true e aggiungi fetch join.
  4. Passa a Gradle se il build è lento, altrimenti resta su Maven.
  5. Containerizza con JRE Alpine e ottimizza la memoria JVM per Docker.
  6. Scrivi test per ogni nuovo servizio. JUnit 5 + Mockito sono il minimo sindacale.

Hai un progetto Java legacy da modernizzare? Noi analizziamo il codice, proponiamo un piano di migrazione graduale e lo implementiamo. Contattaci per una consulenza tecnica.

Ing. Calogero Bono

> AUTHOR_EXTRACTED

Ing. Calogero Bono

Ingegnere Informatico, co-fondatore di Meteora Web. Esperto in architetture software, sicurezza informatica e sviluppo sistemi scalabili.
[ Read Full Dossier ]

> METEORA_WEB // WEB AGENCY

Costruiamo la presenza digitale che la tua azienda merita.

Siti web, social, pubblicità online, e-commerce e hosting performante: ingegnerizzati con metodo da ingegneri informatici a Sciacca, per tutta Italia.

> MW_JOURNAL

> READ_ALL()