Arriba el moment d'escriure codi. En aquesta lliçó generaràs el projecte ciclourbana amb Spring Initializr —tant des del web com des de la línia d'ordres—, l'obriràs al teu IDE i crearàs el primer endpoint real de la plataforma: GET /api/v1/estacions, que retornarà la llista d'estacions de la xarxa de Ribalta. Després l'executaràs de tres maneres diferents, el provaràs amb curl i l'empaquetaràs en un JAR executable que s'executa amb java -jar sense instal·lar cap servidor. En acabar tindràs una API funcionant de cap a cap.

Contingut

  1. Spring Initializr: què és i com es fa servir
  2. Generar el projecte des del web
  3. Generar el projecte des de la línia d'ordres
  4. Importar el projecte a l'IDE
  5. La classe d'arrencada generada
  6. El primer endpoint: GET /api/v1/estacions
  7. Executar l'aplicació
  8. Provar l'endpoint amb curl
  9. Empaquetar i executar el JAR
  10. Spring Boot DevTools i el reinici automàtic
  11. Errors Habituals i Consells
  12. Exercicis

  1. Spring Initializr: què és i com es fa servir

Spring Initializr (start.spring.io) és el generador oficial de projectes Spring Boot. Li indiques quatre dades i una llista de dependències, i et retorna un ZIP amb:

  • Un pom.xml ja configurat amb el parent de Spring Boot i les dependències triades.
  • La classe principal de l'aplicació, anotada i llesta per arrencar.
  • Una classe de prova bàsica.
  • El wrapper de Maven (mvnw, mvnw.cmd, .mvn/).
  • Un .gitignore adequat i un application.properties buit.

És la manera correcta de començar qualsevol projecte Spring Boot. Crear l'estructura a mà és possible, però és feina innecessària i propensa a errors.

  1. Generar el projecte des del web

Obre https://start.spring.io i emplena el formulari amb aquests valors exactes, que farem servir durant tot el curs:

Camp Valor Per què
Project Maven Decidit a la lliçó 01-02
Language Java —
Spring Boot L'última 3.x estable oferta Evita les versions marcades SNAPSHOT o M1 (preliminars)
Group com.ciclourbana Identifica l'organització
Artifact ciclourbana Nom del projecte i del JAR
Name ciclourbana Genera la classe CiclourbanaApplication
Description Gestió de la xarxa de bicicletes elèctriques de Ribalta Metadada del pom.xml
Package name com.ciclourbana Important: el paquet arrel del projecte
Packaging Jar JAR executable amb servidor incrustat
Java 21 Versió LTS del curs

Al plafó Dependencies, prem "ADD DEPENDENCIES" i afegeix-ne exactament dues:

  • Spring Web — Spring MVC, Jackson per al JSON i Tomcat incrustat. És el que converteix el projecte en una aplicació web.
  • Spring Boot DevTools — reinici automàtic en canviar codi i desactivació de memòries cau en desenvolupament.

Prem GENERATE. Es descarrega ciclourbana.zip. Descomprimeix-lo on desis els teus projectes.

Un consell abans de descarregar: el botó EXPLORE mostra el contingut del projecte sense baixar-lo, i SHARE genera una URL que reprodueix exactament aquesta configuració. És molt útil per compartir el punt de partida amb un company.

  1. Generar el projecte des de la línia d'ordres

Spring Initializr també és una API HTTP. Això permet generar el projecte sense obrir el navegador i —encara més important— deixar l'ordre desada a la documentació de l'equip perquè qualsevol reprodueixi el mateix punt de partida.

curl https://start.spring.io/starter.zip \
  -d type=maven-project \
  -d language=java \
  -d bootVersion=3.3.5 \
  -d groupId=com.ciclourbana \
  -d artifactId=ciclourbana \
  -d name=ciclourbana \
  -d description="Gestio de la xarxa de bicicletes electriques de Ribalta" \
  -d packageName=com.ciclourbana \
  -d packaging=jar \
  -d javaVersion=21 \
  -d dependencies=web,devtools \
  -o ciclourbana.zip

# Descomprimir en una carpeta amb el nom del projecte
unzip ciclourbana.zip -d ciclourbana
cd ciclourbana

Desglossament de les opcions de curl:

  • -d clau=valor envia un paràmetre de formulari. En fer servir -d, curl fa automàticament una petició POST.
  • dependencies=web,devtools és la llista d'identificadors de dependències separats per comes. web correspon a "Spring Web" i devtools a "Spring Boot DevTools".
  • -o ciclourbana.zip desa la resposta en un fitxer en comptes d'abocar-la al terminal.

Dues ordres exploratòries molt útils:

# Veure tota la configuració disponible: versions, dependències i identificadors
curl https://start.spring.io

# Consultar les metadades en JSON (versions de Boot disponibles, etc.)
curl -H "Accept: application/json" https://start.spring.io | head -40

Si ometes bootVersion, Initializr fa servir l'última estable, que és el que sol interessar.

  1. Importar el projecte a l'IDE

El projecte és un projecte Maven estàndard, així que qualsevol IDE l'entén.

IntelliJ IDEA: File → Open i selecciona la carpeta ciclourbana (no el pom.xml solt, encara que també funciona). IntelliJ detecta el pom.xml, indexa el projecte i descarrega les dependències. Veuràs una barra de progrés a baix a la dreta; espera que acabi.

Eclipse / STS: File → Import → Maven → Existing Maven Projects, selecciona la carpeta arrel i prem Finish.

VS Code: File → Open Folder sobre ciclourbana. L'extensió de Java arrenca sola i apareix un plafó Spring Boot Dashboard a la barra lateral.

En els tres casos, la primera importació triga: Maven descarrega les dependències de Spring a ~/.m2/repository. Són uns quants centenars de megabytes la primera vegada i pràcticament instantani a partir d'aleshores.

  1. La classe d'arrencada generada

Obre src/main/java/com/ciclourbana/CiclourbanaApplication.java. Conté això:

package com.ciclourbana;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class CiclourbanaApplication {

    public static void main(String[] args) {
        SpringApplication.run(CiclourbanaApplication.class, args);
    }
}

Són deu línies, però cadascuna importa:

  • @SpringBootApplication és l'anotació que ho activa tot. Agrupa tres anotacions que estudiarem al mòdul 2: habilita l'autoconfiguració, marca la classe com a font de configuració i activa l'escaneig de components a partir d'aquest paquet.
  • main és un mètode main de Java corrent. Una aplicació Spring Boot és, literalment, un programa Java que arrenca pel seu main. No hi ha cap contenidor extern que la invoqui.
  • SpringApplication.run(...) crea el context de Spring, registra els beans i —en detectar Spring Web— arrenca Tomcat. Retorna el context ja en marxa. El que passa a dins, pas a pas, és el tema de la lliçó 01-05.
  • L'argument args es propaga: permet passar paràmetres des de la línia d'ordres, com ara --server.port=9090.

Per coherència amb la resta del curs, reanomena la classe a CicloUrbanaApplication (amb la U majúscula). Fes servir la funció de reanomenat de l'IDE (Shift+F6 a IntelliJ) perquè actualitzi també el fitxer de prova.

  1. El primer endpoint: GET /api/v1/estacions

Exposarem les estacions de Ribalta. Necessitem dues peces: un tipus que representi una estació i un controlador que la retorni en JSON.

El record Estacio

Java 21 ofereix els records, ideals per a dades immutables. Crea el fitxer src/main/java/com/ciclourbana/estacions/Estacio.java:

package com.ciclourbana.estacions;

/**
 * Representa una estació d'ancoratge de la xarxa de Ribalta.
 * De moment és un simple record en memòria; al mòdul 4
 * es convertirà en una entitat JPA persistida a base de dades.
 */
public record Estacio(
        Long id,
        String nom,
        String adreca,
        int capacitat,
        double latitud,
        double longitud
) {
}

Per què un record i no una classe normal:

  • Genera automàticament el constructor, els mètodes d'accés (nom(), capacitat()...), equals, hashCode i toString.
  • És immutable: un cop creat no canvia, cosa que evita errors en entorns concurrents.
  • Jackson, la llibreria que Spring Boot fa servir per convertir objectes a JSON, entén els records des de fa diverses versions. Cada component es converteix en una propietat JSON amb el mateix nom.

El controlador

Crea src/main/java/com/ciclourbana/estacions/EstacioController.java:

package com.ciclourbana.estacions;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
@RequestMapping("/api/v1/estacions")
public class EstacioController {

    // Dades fixes en memòria. Al mòdul 4 vindran de PostgreSQL.
    private static final List<Estacio> ESTACIONS = List.of(
            new Estacio(1L, "Plaça Major",   "Plaça Major, 1",        24, 40.4168, -3.7038),
            new Estacio(2L, "Estació Nord",  "Avinguda Estació 3",    30, 40.4290, -3.7020),
            new Estacio(3L, "Parc del Riu",  "Passeig Fluvial 12",    18, 40.4105, -3.6950),
            new Estacio(4L, "Universitat",   "Campus Sud, accés B",   36, 40.4402, -3.7255)
    );

    @GetMapping
    public List<Estacio> llistarEstacions() {
        return ESTACIONS;
    }
}

Què fa cada anotació, al nivell just per a aquesta lliçó:

  • @RestController marca la classe com a component de Spring que atén peticions HTTP i els mètodes de la qual retornen directament el cos de la resposta (no el nom d'una vista HTML). Spring detecta la classe en escanejar el paquet i la registra.
  • @RequestMapping("/api/v1/estacions") fixa la ruta base de tots els mètodes de la classe. Així el prefix s'escriu una sola vegada.
  • @GetMapping sense arguments associa el mètode a GET sobre la ruta base exacta, és a dir GET /api/v1/estacions.
  • El mètode retorna una List<Estacio>. Spring Boot, gràcies a Jackson, la converteix automàticament a un array JSON i fixa la capçalera Content-Type: application/json. No cal serialitzar res a mà.

El detall d'aquestes anotacions, la negociació de contingut, els codis d'estat i el maneig de paràmetres es tracten en profunditat al mòdul 3. Aquí ens n'hi ha prou que funcioni.

Com encaixen les peces

sequenceDiagram
    participant C as curl
    participant T as Tomcat incrustat
    participant D as DispatcherServlet
    participant E as EstacioController
    participant J as Jackson

    C->>T: GET /api/v1/estacions
    T->>D: Petició HTTP
    D->>D: Buscar el mètode que atén la ruta
    D->>E: llistarEstacions()
    E-->>D: Llista d'Estacio
    D->>J: Serialitzar a JSON
    J-->>D: Array JSON d'estacions
    D-->>T: 200 OK + cos JSON
    T-->>C: Resposta HTTP

  1. Executar l'aplicació

Hi ha tres maneres, i convé conèixer-les totes tres.

Amb el plugin de Maven

És la forma canònica des del terminal:

./mvnw spring-boot:run

spring-boot:run compila el projecte i arrenca l'aplicació a la mateixa JVM que Maven, sense necessitat d'empaquetar. És el més còmode durant el desenvolupament.

La sortida acaba amb una cosa semblant a:

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/

2026-08-31T10:22:41.118+02:00  INFO 18422 --- [ciclourbana] [  restartedMain] c.c.CicloUrbanaApplication : Starting CicloUrbanaApplication using Java 21.0.5
2026-08-31T10:22:42.905+02:00  INFO 18422 --- [ciclourbana] [  restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port 8080 (http) with context path '/'
2026-08-31T10:22:42.918+02:00  INFO 18422 --- [ciclourbana] [  restartedMain] c.c.CicloUrbanaApplication : Started CicloUrbanaApplication in 2.174 seconds

Les tres línies clau: la versió de Java utilitzada, el port 8080 on escolta Tomcat i el temps d'arrencada. Interpretarem aquest log línia a línia a la lliçó 01-05.

Per aturar l'aplicació, Ctrl+C.

Des de l'IDE

Obre CicloUrbanaApplication i executa el mètode main com qualsevol programa Java: el triangle verd a IntelliJ, Run As → Java Application a Eclipse, o Run sobre el main a VS Code.

L'avantatge de l'IDE és el depurador: pots posar un punt d'interrupció a llistarEstacions() i veure com l'execució s'atura quan arribi la petició.

STS i VS Code ofereixen a més un Spring Boot Dashboard per arrencar, aturar i reiniciar sense buscar la classe.

Des del JAR empaquetat

És la forma de producció, i la veiem a l'apartat 9.

  1. Provar l'endpoint amb curl

Amb l'aplicació en marxa, obre un altre terminal:

curl http://localhost:8080/api/v1/estacions

Resposta:

[{"id":1,"nom":"Plaça Major","adreca":"Plaça Major, 1","capacitat":24,"latitud":40.4168,"longitud":-3.7038},{"id":2,"nom":"Estació Nord",...}]

Per veure-ho llegible i amb les capçaleres:

# Amb capçaleres i codi d'estat
curl -i http://localhost:8080/api/v1/estacions

# Formatat amb jq (si el tens instal·lat)
curl -s http://localhost:8080/api/v1/estacions | jq

La resposta amb -i comença així:

HTTP/1.1 200
Content-Type: application/json
Transfer-Encoding: chunked
Date: Sun, 31 Aug 2026 08:24:11 GMT

Fixa't que no has escrit ni una sola línia sobre JSON ni sobre el codi 200. Spring Boot ha deduït el Content-Type a partir del tipus de retorn i ha aplicat el 200 per defecte. Això és l'autoconfiguració treballant.

Prova també una ruta inexistent per veure el comportament per defecte davant d'errors:

curl -i http://localhost:8080/api/v1/bicicletes
# HTTP/1.1 404
# {"timestamp":"...","status":404,"error":"Not Found","path":"/api/v1/bicicletes"}

Spring Boot retorna un cos d'error estructurat sense que hagis configurat res. Personalitzar-lo és l'objectiu de la lliçó 03-06.

  1. Empaquetar i executar el JAR

Construir l'artefacte

./mvnw clean package

Aquesta ordre neteja la carpeta target, compila, executa les proves i genera el JAR. En acabar:

ls -lh target/*.jar
# -rw-r--r-- 1 usuari usuari  22M  ciclourbana-0.0.1-SNAPSHOT.jar
# -rw-r--r-- 1 usuari usuari  12K  ciclourbana-0.0.1-SNAPSHOT.jar.original

Apareixen dos fitxers, i la diferència ho explica tot:

  • .jar.original (12 KB) és el JAR que produeix Maven de manera estàndard: només les teves classes compilades.
  • .jar (22 MB) és el fat jar o uber jar: les teves classes més totes les dependències —Spring, Jackson, Tomcat— més un carregador especial.

El spring-boot-maven-plugin és qui fa aquesta transformació a la fase package.

Què és un JAR executable i un fat jar

Un JAR normal és un ZIP amb classes .class. Per executar-lo calen totes les seves dependències al classpath, cosa que obliga a distribuir desenes de fitxers o a instal·lar un servidor d'aplicacions.

Spring Boot ho resol amb un format enginyós:

ciclourbana-0.0.1-SNAPSHOT.jar
├── META-INF/
│   └── MANIFEST.MF          ← declara Main-Class i Start-Class
├── org/springframework/boot/loader/   ← el carregador de Spring Boot
├── BOOT-INF/
│   ├── classes/             ← LES TEVES classes compilades
│   │   └── com/ciclourbana/...
│   └── lib/                 ← TOTES les dependències, com a .jar imbricats
│       ├── spring-web-6.x.jar
│       ├── tomcat-embed-core-10.x.jar
│       └── jackson-databind-2.x.jar
└── ...

Pots verificar-ho:

unzip -l target/ciclourbana-0.0.1-SNAPSHOT.jar | head -20
unzip -p target/ciclourbana-0.0.1-SNAPSHOT.jar META-INF/MANIFEST.MF

El manifest mostra la clau del truc:

Main-Class: org.springframework.boot.loader.launch.JarLauncher
Start-Class: com.ciclourbana.CicloUrbanaApplication

Quan executes el JAR, Java invoca el JarLauncher de Spring Boot, no la teva classe. Aquest llançador instal·la un carregador de classes capaç de llegir JAR imbricats dins de BOOT-INF/lib —cosa que Java estàndard no sap fer— i només aleshores crida la teva Start-Class.

La conseqüència pràctica és enorme: un únic fitxer conté l'aplicació completa, amb el seu servidor inclòs. Això és el que es copia a un servidor, es fica en una imatge Docker o es puja a una plataforma cloud.

Executar el JAR

java -jar target/ciclourbana-0.0.1-SNAPSHOT.jar

Arrenca exactament igual que amb spring-boot:run. Comprova que continua responent:

curl -s http://localhost:8080/api/v1/estacions | jq '.[0].nom'
# "Plaça Major"

Pots passar propietats per la línia d'ordres:

# Canviar el port sense recompilar
java -jar target/ciclourbana-0.0.1-SNAPSHOT.jar --server.port=9090

# Limitar la memòria de la JVM (útil en contenidors)
java -Xmx256m -jar target/ciclourbana-0.0.1-SNAPSHOT.jar

Fixa't en la diferència: -Xmx256m va abans de -jar perquè és una opció de la JVM, mentre que --server.port=9090 va després perquè és un argument de l'aplicació.

  1. Spring Boot DevTools i el reinici automàtic

En generar el projecte hi vam afegir spring-boot-devtools:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <scope>runtime</scope>
    <optional>true</optional>
</dependency>

Els atributs <optional>true</optional> i <scope>runtime</scope> fan que DevTools no es propagui a qui depengui del teu projecte i que es desactivi automàticament quan l'aplicació s'executa des d'un fat jar. És a dir: ajuda en desenvolupament i desapareix en producció sense que hi facis res.

Què aporta:

Funció Efecte
Reinici automàtic En detectar classes recompilades, reinicia el context de Spring en menys d'un segon
LiveReload Refresca el navegador automàticament en canviar recursos estàtics
Memòries cau desactivades Desactiva les memòries cau de plantilles per veure els canvis a l'instant
Propietats de desenvolupament Aplica valors per defecte sensats per a desenvolupament

Com funciona el reinici ràpid

DevTools fa servir dos carregadors de classes: un base amb les dependències (Spring, Tomcat, Jackson), que no canvien mai, i un restart amb les teves classes, que sí que canvien. En reiniciar només llença i torna a carregar el segon. Per això triga dècimes de segon en lloc dels dos segons de l'arrencada completa.

Aquest és també el motiu que el fil principal aparegui al log com a restartedMain en lloc de main.

Provocar un reinici

El reinici es dispara quan canvien els fitxers .class a target/classes, no quan deses el .java. Per tant cal una recompilació:

  • IntelliJ: Ctrl+F9 (Build Project), o activa Settings → Build → Compiler → Build project automatically.
  • Eclipse/STS: compila en desar per defecte, així que n'hi ha prou amb Ctrl+S.
  • VS Code: també compila en desar.

Prova-ho. Amb l'aplicació en marxa mitjançant ./mvnw spring-boot:run, afegeix una cinquena estació:

new Estacio(5L, "Mercat Vell", "Carrer del Mercat, 8", 20, 40.4211, -3.7101)

Recompila i observa al log:

2026-08-31T10:31:08.442+02:00  INFO --- [  restartedMain] c.c.CicloUrbanaApplication : Started CicloUrbanaApplication in 0.612 seconds

Ara curl retorna cinc estacions sense que hagis aturat res.

Si el reinici et molesta, pots desactivar-lo sense treure la dependència:

# src/main/resources/application.properties
spring.devtools.restart.enabled=false

Errors Habituals i Consells

  • Web server failed to start. Port 8080 was already in use. Tens una altra instància arrencada, molt típic si has oblidat aturar la de l'IDE abans de llançar ./mvnw spring-boot:run. Localitza el procés amb lsof -i :8080 (Linux/macOS) o netstat -ano | findstr :8080 (Windows), o arrenca en un altre port amb --server.port=9090.
  • L'endpoint retorna 404 encara que el codi sembli correcte. Gairebé sempre és perquè el controlador és fora del paquet arrel com.ciclourbana. L'escaneig de components només mira aquest paquet i els seus subpaquets; ho veurem a la lliçó 01-04.
  • L'aplicació arrenca i acaba immediatament. Falta spring-boot-starter-web. Sense dependència web no hi ha servidor que mantingui viu el procés, així que el main acaba i la JVM es tanca.
  • DevTools no reinicia res. Estàs desant el .java però no es recompila. Activa la construcció automàtica a l'IDE o compila a mà.
  • Executar el .jar.original per error. Dona no main manifest attribute. El fitxer bo és el .jar gran, sense sufix.
  • Consell — el JAR és autocontingut, no autònom. Conté totes les dependències Java, però continua necessitant una JVM instal·lada allà on s'executi. Per això les imatges Docker de Spring Boot parteixen d'una imatge amb JDK o JRE (mòdul 7).
  • Consell — inicialitza Git ja. El projecte porta un .gitignore correcte. Amb git init && git add . && git commit -m "Projecte inicial de CicloUrbana" tindràs un punt de retorn segur des del primer dia.

Exercicis

Exercici 1

Genera el projecte ciclourbana per línia d'ordres amb curl, executa'l i comprova amb curl que GET /api/v1/estacions retorna les quatre estacions de Ribalta. Anota el temps d'arrencada que apareix al log.

Exercici 2

Afegeix a l'EstacioController un segon endpoint GET /api/v1/estacions/resum que retorni un objecte JSON amb el nombre total d'estacions i la capacitat total sumada de la xarxa. Fes servir un record ResumXarxa.

Exercici 3

Empaqueta l'aplicació, inspecciona el contingut del fat jar per localitzar-hi les teves classes compilades i el JAR de Tomcat incrustat, i executa-la al port 9090 comprovant que respon.

Solucions

Solució 1

curl https://start.spring.io/starter.zip \
  -d type=maven-project -d language=java \
  -d groupId=com.ciclourbana -d artifactId=ciclourbana \
  -d name=ciclourbana -d packageName=com.ciclourbana \
  -d packaging=jar -d javaVersion=21 \
  -d dependencies=web,devtools \
  -o ciclourbana.zip

unzip ciclourbana.zip -d ciclourbana
cd ciclourbana
chmod +x mvnw

Després de crear Estacio i EstacioController tal com es descriu a la lliçó:

./mvnw spring-boot:run

En un altre terminal:

curl -s http://localhost:8080/api/v1/estacions | jq 'length'
# 4

curl -s http://localhost:8080/api/v1/estacions | jq '.[].nom'
# "Plaça Major"
# "Estació Nord"
# "Parc del Riu"
# "Universitat"

El temps d'arrencada apareix a la línia Started CicloUrbanaApplication in X seconds. En una màquina normal és d'entre 1,5 i 3 segons.

Solució 2

Primer el record del resum, al mateix paquet:

package com.ciclourbana.estacions;

/**
 * Resum agregat de la xarxa d'estacions de Ribalta.
 */
public record ResumXarxa(
        int totalEstacions,
        int capacitatTotal
) {
}

I el nou mètode al controlador:

@GetMapping("/resum")
public ResumXarxa obtenirResum() {
    int capacitatTotal = ESTACIONS.stream()
            .mapToInt(Estacio::capacitat)   // referència a l'accessor del record
            .sum();
    return new ResumXarxa(ESTACIONS.size(), capacitatTotal);
}

Explicació detallada:

  • @GetMapping("/resum") es concatena amb el @RequestMapping de la classe, produint GET /api/v1/estacions/resum.
  • Estacio::capacitat és una referència a mètode. En un record, l'accessor s'anomena igual que el component (capacitat(), no getCapacitat()).
  • mapToInt(...).sum() converteix el flux a un IntStream i suma els valors, evitant l'autoboxing.
  • Retornar un objecte (no una llista) fa que Jackson generi un objecte JSON, no un array.

Comprovació:

curl -s http://localhost:8080/api/v1/estacions/resum | jq
# {
#   "totalEstacions": 4,
#   "capacitatTotal": 108
# }

Solució 3

./mvnw clean package

# Localitzar les classes pròpies dins del fat jar
unzip -l target/ciclourbana-0.0.1-SNAPSHOT.jar | grep "com/ciclourbana"
#   BOOT-INF/classes/com/ciclourbana/CicloUrbanaApplication.class
#   BOOT-INF/classes/com/ciclourbana/estacions/Estacio.class
#   BOOT-INF/classes/com/ciclourbana/estacions/EstacioController.class

# Localitzar Tomcat incrustat
unzip -l target/ciclourbana-0.0.1-SNAPSHOT.jar | grep tomcat
#   BOOT-INF/lib/tomcat-embed-core-10.1.x.jar
#   BOOT-INF/lib/tomcat-embed-el-10.1.x.jar
#   BOOT-INF/lib/tomcat-embed-websocket-10.1.x.jar

# Executar al port 9090
java -jar target/ciclourbana-0.0.1-SNAPSHOT.jar --server.port=9090

Verificació en un altre terminal:

curl -s http://localhost:9090/api/v1/estacions | jq 'length'
# 4

Conclusió de l'exercici: les teves classes viuen a BOOT-INF/classes i les dependències a BOOT-INF/lib com a JAR imbricats. Aquesta separació és exactament la que permet a Spring Boot construir imatges Docker per capes eficients, cosa que aprofitarem al mòdul 7.

Conclusió

Ja tens CicloUrbana en marxa. Has generat el projecte amb Spring Initializr des del web i des del terminal, has entès les deu línies de la classe d'arrencada, has creat el record Estacio i l'EstacioController que exposa GET /api/v1/estacions amb les quatre estacions de Ribalta, i l'has executat amb Maven, amb l'IDE i com a fat jar autocontingut amb java -jar. També saps per què aquest JAR pesa 22 MB i com el JarLauncher carrega els JAR imbricats de BOOT-INF/lib.

A la lliçó següent, Entenent l'Estructura del Projecte, obrirem la caixa: recorrerem l'arbre de directoris generat, llegirem el pom.xml línia a línia, entendrem què és exactament un starter i per què el paquet arrel determina quins components troba Spring, i dissenyarem l'organització de paquets que CicloUrbana mantindrà durant tot el curs.

Curs de Spring Boot

Mòdul 1: Introducció a Spring Boot

Mòdul 2: Conceptes bàsics de Spring Boot

Mòdul 3: Construint serveis web RESTful

Mòdul 4: Accés a dades amb Spring Boot

Mòdul 5: Seguretat a Spring Boot

Mòdul 6: Proves a Spring Boot

Mòdul 7: Funcions avançades de Spring Boot

Mòdul 8: Desplegament d'aplicacions Spring Boot

Mòdul 9: Rendiment i monitoratge

Mòdul 10: Millors pràctiques i consells

© Copyright 2026. Tots els drets reservats