El mòdul anterior va acabar amb una promesa: «Has escrit versions en miniatura de gairebé tots ells amb les teves pròpies mans. Ara faràs servir els de veritat.»
Aquesta lliçó és el pont. Abans d'escriure la primera anotació de Spring o la primera entitat d'Hibernate, cal entendre què és exactament un framework, en què es diferencia d'una llibreria, què et dóna i què et treu i —sobretot— com funciona per dins, perquè la resposta a aquesta última pregunta ja la saps: anotacions, reflexió, proxies dinàmics i generació de codi. Exactament allò que vas construir al mòdul 10.
Això importa per una raó molt pràctica. La diferència entre un desenvolupador que fa servir Spring i un que entén Spring no està en quantes anotacions es coneix de memòria: està en el fet que quan alguna cosa falla —i fallarà— el primer busca en un fòrum i el segon raona sobre què està fent el contenidor. Quan vegis que una crida a un mètode @Transactional des d'un altre mètode de la mateixa classe no obre transacció, tu sabràs per què: perquè el proxy dinàmic que vas escriure a 10-03 té exactament el mateix forat, i el vas veure amb els teus ulls.
A més, hi ha una decisió professional que ningú t'ensenya i que prendràs desenes de vegades a la teva carrera: afegir o no una dependència. És una decisió amb conseqüències de seguretat, de manteniment i de pes. Aquesta lliçó et dóna criteris reals per prendre-la.
En acabar sabràs distingir llibreria de framework i explicar la inversió de control amb un exemple que vas escriure tu, tindràs un mapa de l'ecosistema Java per categories, sabràs com s'avalua una dependència abans de ficar-la al teu pom.xml, entendràs què és Maven Central i què signifiquen les coordenades groupId:artifactId:version, i coneixeràs el risc de la cadena de subministrament de programari amb un cas real que va paralitzar la indústria durant setmanes.
Contingut
- El punt de partida: BiblioTech fet a mà
- Llibreria enfront de framework: qui crida qui
- La inversió del control, explicada amb el teu propi
ContenidorSimple - Què aporta un framework
- Què costa un framework
- Com se sosté la màgia: els quatre mecanismes
- El mapa: allò que vas escriure a mà enfront d'allò que fa el framework de veritat
- Mapa de l'ecosistema Java per categories
- Com es tria una dependència
- La pregunta prèvia: la necessito de veritat?
- Maven Central i les coordenades
- Versionat semàntic
- El cost de la seguretat de la cadena de subministrament
- Log4Shell: què va passar de veritat
- Auditar dependències a la pràctica
- Quin framework aprendre primer
- Per què aquest curs tria Spring Boot
- El pla del mòdul 11
- Errors comuns i consells
- Exercicis
- El punt de partida: BiblioTech fet a mà
Abans de mirar endavant, mira un cop més el que tens. BiblioTech, en acabar el mòdul 10, és un projecte respectable: domini segellat, repositoris genèrics, streams, java.time, fils virtuals, un contenidor d'injecció de dependències casolà i tres proxies dinàmics.
I té sis deutes declarats:
| Deute | Estat actual | Què falta |
|---|---|---|
| Injecció de dependències | ContenidorSimple, 150 línies |
Àmbits, cicle de vida, configuració per entorn, transaccions |
| Persistència | Fitxers CSV | Consultes, índexs, transaccions, integritat referencial |
| JSON | indexOf a mà (09-06) |
Un analitzador de veritat |
| Codi repetitiu | Getters, equals, hashCode, toString a mà |
Generació automàtica |
| Logging | java.util.logging |
Una façana estàndard de l'ecosistema |
| Construcció | javac a mà |
Dependències, fases, empaquetatge, reproductibilitat |
| Proves | Cap | Tot |
Cadascuna d'aquestes files és una lliçó d'aquest mòdul. Però fixa't en una cosa: cap d'aquests deutes és de lògica de negoci. Cap no té a veure amb préstecs, multes, reserves o catàlegs. Tots són lampisteria: coses que tota aplicació necessita i que cap aplicació no vol escriure.
Aquesta observació és, literalment, la definició del problema que resolen els frameworks.
- Llibreria enfront de framework: qui crida qui
La distinció se sol explicar malament. No és una qüestió de mida ni de complexitat. Hi ha llibreries enormes i frameworks minúsculs. La diferència és de direcció de les crides.
- Una llibreria és codi que crides tu. Tu tens el control del flux: decideixes quan, on i amb quins arguments.
java.util.Collections, Jackson o Apache Commons són llibreries: cridesCollections.sort(llista)quan et convé. - Un framework és codi que et crida a tu. El framework té el control del flux: defineix l'estructura de l'aplicació, arrenca i, en determinats punts, invoca el teu codi. Tu omples buits.
Aquesta inversió té un nom clàssic, el Principi de Hollywood: "No ens truquis, ja et trucarem nosaltres."
graph LR
subgraph "LLIBRERIA"
A1["El teu codi<br/>(controla el flux)"] -->|crida| B1["Jackson<br/>Commons<br/>Guava"]
B1 -->|retorna| A1
end
subgraph "FRAMEWORK"
A2["Spring<br/>JUnit<br/>Servlet API"] -->|crida| B2["El teu codi<br/>(omple buits)"]
B2 -->|retorna| A2
end
Un exemple concret que ja coneixes, de 09-06. Quan vas escriure el ClientMetadades amb HttpClient:
// LLIBRERIA: tu decideixes quan cridar
HttpResponse<String> resposta = client.send(peticio, BodyHandlers.ofString());
String cos = resposta.body();Tu ho controles tot. HttpClient és una llibreria.
I ara, un exemple de framework que veuràs a 11-04:
On és el main? Qui crida multaDeCincDiesSonCincEuros()? Tu no. El crida JUnit. Tu només has escrit un mètode i li has posat una etiqueta. JUnit escaneja les teves classes, troba els mètodes anotats amb @Test, crea una instància per a cadascun i els invoca. Això és un framework.
Compara els dos models:
| Aspecte | Llibreria | Framework |
|---|---|---|
| Control del flux | Teu | Del framework |
| Qui crida qui | Tu → llibreria | Framework → tu |
| Estructura del teu codi | Lliure | Imposada (convencions, anotacions, interfícies) |
| Cost de substituir-la | Baix (aïlles la crida) | Alt (impregna l'arquitectura) |
| Exemples | Jackson, Guava, Commons, SLF4J | Spring, JUnit, Hibernate, Quarkus |
| Punt d'entrada | Mètodes que invoques | Hooks: anotacions, interfícies, fitxers de configuració |
Un matís honest: la frontera no sempre és nítida. Spring és un framework, però RestTemplate dins de Spring és una llibreria. Hibernate és un framework quan gestiona el cicle de vida de les teves entitats, i una llibreria quan crides entityManager.find(...). L'útil no és classificar, sinó preguntar-se a cada punt: qui controla el flux aquí?
- La inversió del control, explicada amb el teu propi
ContenidorSimple
ContenidorSimpleLa inversió de control (IoC, Inversion of Control) és el principi general: cedir el control del flux a un altre component. La injecció de dependències (DI, Dependency Injection) és la forma concreta d'IoC que més faràs servir: en comptes que un objecte creï les seves pròpies dependències, li les donen fetes.
Això ho vas escriure tu a 10-03. Abans del ContenidorSimple, GestorPrestecs feia això:
// ABANS: l'objecte crea les seves propies dependencies. Control NO invertit.
public class GestorPrestecs {
private final Repositori<Prestec> repositori;
private final CalculadoraMultes calculadora;
private final ServeiAvisos avisos;
public GestorPrestecs() {
// El propi gestor decideix QUINA implementacio fa servir i COM construir-la.
this.repositori = new MagatzemPrestecs(Path.of("dades/prestecs.csv"));
this.calculadora = new CalculadoraMultes(Clock.systemDefaultZone());
this.avisos = new ServeiAvisos("smtp.nexussoftware.com", 587);
}
}Problemes d'aquest codi, tots reals:
- No es pot provar. Per provar el càlcul d'una multa necessites un fitxer CSV en disc i un servidor SMTP.
- No es pot canviar d'implementació. Si demà el magatzem és una base de dades, cal editar
GestorPrestecs. - No es pot configurar per entorn. L'amfitrió SMTP de desenvolupament i el de producció són diferents, i estan escrits a foc.
- El rellotge és el del sistema. Aquest
Clock.systemDefaultZone()fa impossible provar un venciment sense esperar 15 dies. Just el que 10-05 volia evitar.
Després del ContenidorSimple:
// DESPRES: l'objecte DECLARA el que necessita. Control invertit.
public class GestorPrestecs {
private final Repositori<Prestec> repositori;
private final CalculadoraMultes calculadora;
private final ServeiAvisos avisos;
public GestorPrestecs(Repositori<Prestec> repositori,
CalculadoraMultes calculadora,
ServeiAvisos avisos) {
this.repositori = repositori;
this.calculadora = calculadora;
this.avisos = avisos;
}
}GestorPrestecs ja no sap d'on surten els seus col·laboradors. Només sap què necessita. Algú de fora —el contenidor— resol el graf de dependències, construeix tot en l'ordre correcte i li passa les peces.
Visualment, el canvi és aquest:
graph TD
subgraph "SENSE inversio de control"
G1["GestorPrestecs"] -->|new| R1["MagatzemPrestecs"]
G1 -->|new| C1["CalculadoraMultes"]
G1 -->|new| A1["ServeiAvisos"]
end
subgraph "AMB inversio de control"
CT["Contenidor IoC"] -->|construeix| R2["MagatzemPrestecs"]
CT -->|construeix| C2["CalculadoraMultes"]
CT -->|construeix| A2["ServeiAvisos"]
CT -->|construeix i injecta| G2["GestorPrestecs"]
R2 -.->|injectat| G2
C2 -.->|injectat| G2
A2 -.->|injectat| G2
end
El teu ContenidorSimple feia això: escanejava les classes anotades amb @Component, mirava els seus constructors amb reflexió, resolia recursivament cada paràmetre, detectava cicles i emmagatzemava els singletons en memòria cau. Cent cinquanta línies.
L'ApplicationContext de Spring fa exactament el mateix. I unes quantes coses més:
| Capacitat | El teu ContenidorSimple |
ApplicationContext de Spring |
|---|---|---|
| Escanejar components | Sí, @Component |
Sí, @Component i estereotips |
| Injecció per constructor | Sí | Sí (recomanada) |
| Injecció per setter i camp | No | Sí |
| Detecció de cicles | Sí, amb excepció | Sí, amb missatge molt millor |
| Singleton | Sí, un Map |
Sí, més 5 àmbits addicionals |
| Àmbit prototip / petició / sessió | No | Sí |
Cicle de vida (@PostConstruct / @PreDestroy) |
No | Sí |
| Configuració per entorn (perfils) | No | Sí, @Profile |
| Propietats externalitzades | No | Sí, @Value, @ConfigurationProperties |
| Resolució d'ambigüitat | No (fallava amb 2 implementacions) | Sí, @Qualifier, @Primary |
Injectar totes les implementacions en una List<T> |
No | Sí |
| Transaccions declaratives | No | Sí, @Transactional |
| Programació orientada a aspectes | Tres proxies escrits a mà | Sí, integrada |
| Creació mandrosa | No | Sí, @Lazy |
| Esdeveniments entre components | No | Sí, ApplicationEventPublisher |
| Arrencada d'aplicació web | No | Sí |
Cap d'aquestes files addicionals no és lògica de negoci. Totes són coses que necessitaries escriure si la teva aplicació creixés. Això és el que compres.
- Què aporta un framework
Quatre coses concretes, per ordre d'importància real.
1. Codi no diferenciador ja resolt. Hi ha una distinció útil entre codi diferenciador (el que resol el problema pel qual existeix la teva empresa: a BiblioTech, les regles de préstecs, multes i reserves) i codi de lampisteria (el que necessita qualsevol aplicació: arrencar, configurar-se, parlar amb una base de dades, atendre HTTP, registrar traces). El codi de lampisteria no aporta valor a ningú i tanmateix s'emporta la major part del temps quan l'escrius tu. Un framework et torna aquest temps.
2. Convencions compartides. Si contractes un desenvolupador Java amb experiència en Spring, entén l'estructura del teu projecte en una tarda. Si BiblioTech fes servir el teu ContenidorSimple casolà, trigaria una setmana a entendre un contenidor que només existeix a la teva empresa i del qual no hi ha documentació, cursos ni respostes en fòrums. Les convencions són un actiu econòmic.
3. Ecosistema. Quan tries Spring, no tries només un contenidor: tries Spring Data (persistència), Spring Security (autenticació), Spring Cloud (sistemes distribuïts), Actuator (observabilitat) i centenars d'integracions que ja funcionen juntes. La integració ja està feta i provada, i això val més que qualsevol característica individual.
4. Seguretat mantinguda. Quan apareix una vulnerabilitat en la gestió de peticions HTTP, l'equip de Spring publica un pedaç i tu actualitzes una versió. Si aquesta lògica és teva, la vulnerabilitat la descobreixes quan te l'exploten. Aquest punt se sol infravalorar i és probablement el més important en producció.
- Què costa un framework
Un framework no és gratis. Quatre costos, també reals.
1. Corba d'aprenentatge. Spring té més de vint anys de superfície acumulada. Ser productiu porta setmanes; ser competent, mesos. I bona part d'aquest aprenentatge no és transferible: aprendre @Transactional no t'ensenya res sobre bases de dades, t'ensenya sobre Spring.
2. Acoblament. Un framework impregna l'arquitectura. Migrar una aplicació de Spring a Quarkus no és canviar una dependència: és reescriure la capa de configuració, la d'arrencada, la de proves i probablement part del domini. Per això convé mantenir el domini net d'anotacions del framework sempre que sigui possible — una idea que es desenvolupa a 12-02.
3. Màgia difícil de depurar. Quan un @Autowired no injecta, o una transacció no fa rollback, o una entitat es desa sola sense que cridis save, la causa és en codi que tu no vas escriure, invocat per reflexió des d'un punt que no apareix a la teva traça. Aquesta és exactament la raó que el mòdul 10 anés abans que aquest: si saps com funciona la màgia, pots depurar-la.
4. Pes. Un jar executable de Spring Boot amb web i JPA ronda els 40-50 MB i arrenca en 1-3 segons. Per a un servei de llarga vida és irrellevant. Per a una funció serverless que arrenca a cada petició o un CLI que ha de respondre en 50 ms, és desqualificador — i aquí és on entren Quarkus, Micronaut o la compilació nativa amb GraalVM que es va esmentar a 10-07.
Resumit:
| Guanyes | Pagues |
|---|---|
| Setmanes de lampisteria resolta | Setmanes de corba d'aprenentatge |
| Convencions que qualsevol entén | Acoblament a una arquitectura concreta |
| Un ecosistema integrat i provat | Comportament implícit difícil de depurar |
| Pedaços de seguretat mantinguts | Superfície d'atac de tercers |
| Menys codi propi per mantenir | Més pes, més arrencada, més memòria |
La conclusió professional no és "els frameworks són bons" ni "són dolents": és que la decisió depèn del context, i que qui no entén el que hi ha a sota no pot prendre-la.
- Com se sosté la màgia: els quatre mecanismes
Aquí és on el mòdul 10 es cobra del tot. Tot el que un framework Java fa "per art de màgia" es recolza en quatre mecanismes, i els quatre els coneixes.
Mecanisme 1: anotacions (10-02)
Les anotacions són metadades: etiquetes que no fan res per si soles. @Service no arrenca res. @Entity no crea cap taula. @Test no executa res. Són marques inertes que algú ha de llegir.
Ho vas aprendre construint @CampCsv i @Auditable. I vas aprendre la condició imprescindible:
@Retention(RetentionPolicy.RUNTIME) // sense aixo, l'anotacio NO existeix en execucio
@Target(ElementType.TYPE)
public @interface Component { }Tota anotació de framework que es llegeixi en execució porta @Retention(RUNTIME). Obre el codi font de @Service de Spring o d'@Entity de Jakarta Persistence: hi és.
Mecanisme 2: reflexió (10-03)
Algú ha de llegir les etiquetes. Aquest algú fa servir reflexió: Class.forName, getDeclaredFields, isAnnotationPresent, getDeclaredConstructor().newInstance(), invoke, setAccessible(true).
El teu ExportadorAnotat recorria els camps de qualsevol objecte buscant @CampCsv. Hibernate recorre els camps de qualsevol entitat buscant @Column. És el mateix bucle.
Mecanisme 3: proxies dinàmics (10-03)
Quan el framework necessita afegir comportament al voltant del teu mètode sense tocar el teu codi —transaccions, seguretat, memòria cau, reintents, mètriques—, no modifica la teva classe: l'embolcalla. En vas escriure tres:
// El teu ProxyAuditoria de 10-03
Object proxy = Proxy.newProxyInstance(
interficie.getClassLoader(),
new Class<?>[] { interficie },
(p, metode, args) -> {
if (metode.isAnnotationPresent(Auditable.class)) {
registre.anotarInici(metode.getName());
}
Object resultat = metode.invoke(objectiu, args); // crida real
// ... post-processat
return resultat;
});Quan a 11-02 vegis @Transactional, no veuràs res de nou: veuràs aquest mateix InvocationHandler amb iniciar transacció abans i confirmar o revertir després. I veuràs també el mateix forat: si l'objectiu es crida a si mateix internament, la crida no passa pel proxy i l'aspecte no s'aplica. Aquest detall és la causa d'un percentatge enorme dels "el meu @Transactional no funciona" del món.
Nota tècnica: els proxies JDK que vas escriure només funcionen sobre interfícies. Quan la classe no n'implementa cap, Spring fa servir CGLIB, que genera en temps d'execució una subclasse de la teva classe i sobreescriu els seus mètodes. D'aquí una conseqüència pràctica: una classe o un mètode final no es pot proxiar amb CGLIB, i per això de vegades el teu @Transactional sobre un mètode final no fa absolutament res.
Mecanisme 4: generació de codi
Hi ha dos moments possibles.
- En temps de compilació, mitjançant un processador d'anotacions (
javax.annotation.processing, vist a 10-02). És el que fa Lombok: no hi ha màgia en execució, el.classja conté els getters. També MapStruct i Micronaut. - En temps d'execució, generant bytecode al vol amb ASM, ByteBuddy o CGLIB. És el que fan Spring, Hibernate i Mockito.
Cada opció té les seves conseqüències:
| Mecanisme | Quan actua | Cost en arrencada | Depurable | Exemples |
|---|---|---|---|---|
| Anotacions + reflexió | Execució | Mitjà (escaneig) | Regular | Spring, Hibernate, JUnit, Jackson |
| Proxy dinàmic JDK | Execució | Baix | Regular (traces amb $Proxy0) |
@Transactional, Spring Data |
| Generació de subclasse (CGLIB/ByteBuddy) | Execució | Mitjà | Difícil | Spring AOP sobre classes, Mockito |
| Processador d'anotacions | Compilació | Cap | Fàcil (el codi existeix) | Lombok, MapStruct, Micronaut |
La tendència actual, precisament pel cost d'arrencada i per la compatibilitat amb la compilació nativa de GraalVM, és moure feina de l'execució a la compilació. Quarkus i Micronaut van néixer amb aquesta idea, i Spring 6 l'ha adoptada parcialment amb AOT.
- El mapa: allò que vas escriure a mà enfront d'allò que fa el framework de veritat
Aquesta taula és el resum del mòdul 10 llegit des del mòdul 11. És, probablement, la taula més important d'aquesta lliçó.
| Allò que vas escriure a mà | Lliçó | Allò que fa el framework de veritat | Es veu a |
|---|---|---|---|
ContenidorSimple amb @Component i @Injectar |
10-03 | ApplicationContext de Spring amb @Component i @Autowired |
11-02 |
ProxyAuditoria, ProxyReintents, ProxyCronometre |
10-03 | Spring AOP, @Transactional, @Retryable, @Cacheable |
11-02 |
ExportadorAnotat que llegeix @CampCsv per reflexió |
10-03 | Hibernate llegint @Column, Jackson llegint @JsonProperty |
11-03, 11-07 |
ValidadorAnotat amb @Validar |
10-03 | Jakarta Bean Validation (@NotNull, @Size, @Email) |
11-02 |
LectorCsv / EscriptorCsv a mà |
07-07 | OpenCSV, Commons CSV — i, encara millor, una base de dades | 11-03, 11-07 |
MagatzemPrestecs sobre fitxers |
07-06 | Hibernate / JPA sobre una base de dades relacional | 11-03 |
Repositori<T extends Identificable> genèric |
10-01 | JpaRepository<T, ID> de Spring Data, implementat amb proxies |
11-03 |
Anàlisi de JSON amb indexOf |
09-06 | Jackson ObjectMapper |
11-07 |
Getters, equals, hashCode, toString a mà |
03-09 | Lombok (o record, que ja fas servir) |
11-07 |
ConfiguracioLog amb java.util.logging |
06-07 | SLF4J + Logback | 11-07 |
Configuracio / ReglesNegoci amb Properties |
07-07 | application.yml, @ConfigurationProperties, perfils |
11-02 |
main que imprimeix i un humà que mira |
totes | JUnit 5 + AssertJ + Mockito | 11-04, 11-06 |
javac amb classpath a mà |
01-02 | Maven | 11-05 |
AturadaOrdenada amb shutdown hooks |
08-05 | Cicle de vida del contenidor, @PreDestroy |
11-02 |
Llegeix-la en les dues direccions. D'esquerra a dreta, és el teu full de ruta. De dreta a esquerra, és la resposta a la pregunta «què està fent Spring aquí?».
- Mapa de l'ecosistema Java per categories
L'ecosistema Java és enorme i a un nouvingut li resulta aclaparador. En realitat s'ordena bé en vuit categories, i a cadascuna hi ha dues o tres opcions dominants.
Construcció i dependències
| Eina | Què és | Quan | Es veu a |
|---|---|---|---|
| Maven | Estàndard de facto. XML declaratiu, cicle de vida fix | Per defecte a l'empresa | 11-05 |
| Gradle | DSL en Groovy o Kotlin, memòria cau i incremental | Projectes grans, Android | 11-05 (comparat) |
| Ant | Històric, imperatiu | Només llegat | — |
Injecció de dependències i aplicacions
| Framework | Què és | Fort en | Es veu a |
|---|---|---|---|
| Spring / Spring Boot | L'ecosistema dominant | Tot; comunitat enorme | 11-02 |
| Quarkus | Natiu al núvol, arrencada en mil·lisegons | Contenidors, serverless | esmentat |
| Micronaut | DI resolta en compilació | Arrencada i memòria mínimes | esmentat |
| Jakarta EE | L'estàndard (CDI, JAX-RS, JPA) | Servidors d'aplicacions | 11-03 (JPA) |
Persistència
| Llibreria | Enfocament | Quan | Es veu a |
|---|---|---|---|
| JPA / Hibernate | ORM: objectes ↔ taules | Domini ric, CRUD | 11-03 |
| Spring Data JPA | Repositoris per convenció sobre JPA | Amb Spring | 11-03 |
| jOOQ | SQL amb tipus verificats en compilació | SQL complex i control total | esmentat |
| MyBatis | SQL en XML o anotacions, mapatge explícit | Consultes a mida | esmentat |
| JDBC / JdbcTemplate | Directe, sense ORM | Consultes puntuals, rendiment | 11-03 (línia base) |
Proves
| Llibreria | Per a què | Es veu a |
|---|---|---|
| JUnit 5 | Marc de proves | 11-04 |
| Mockito 5 | Dobles de prova | 11-06 |
| AssertJ | Assercions fluides i llegibles | 11-04, 11-06 |
| Testcontainers | Base de dades i serveis reals a Docker | 11-06, 12-05 |
| WireMock | Simular APIs HTTP | esmentat |
| JaCoCo | Cobertura | 12-05 |
Serialització
| Llibreria | Notes | Es veu a |
|---|---|---|
| Jackson 2.x | L'estàndard; integrat a Spring | 11-07 |
| Gson | Google; més simple, menys potent | 11-07 (esmentat) |
| JSON-B | Estàndard de Jakarta | 11-07 (esmentat) |
Logging
| Peça | Paper | Es veu a |
|---|---|---|
| SLF4J | Façana: l'API contra la qual programes | 11-07 |
| Logback | Implementació per defecte de Spring Boot | 11-07 |
| Log4j2 | Implementació alternativa, molt ràpida | 11-07 (i §14) |
java.util.logging |
El del JDK; sense dependències, limitat | 06-07 |
Utilitats
| Llibreria | Què aporta | Es veu a |
|---|---|---|
| Lombok | Genera codi repetitiu en compilació | 11-07 |
| Guava | Col·leccions, memòria cau, utilitats (Google) | 11-07 (panorama) |
| Apache Commons Lang / IO | Utilitats de String, fitxers, reflexió |
11-07 (panorama) |
| MapStruct | Mapejadors entitat ↔ DTO en compilació | 11-07 (panorama) |
| Caffeine | Memòria cau en memòria d'alt rendiment | 11-07 (panorama) |
Documentació i operació
| Llibreria | Què aporta | Es veu a |
|---|---|---|
| springdoc-openapi | Documentació OpenAPI/Swagger automàtica | 11-07, 12-04 |
| Micrometer | Mètriques | 11-07, 12-07 |
| Resilience4j | Reintents, curtcircuits, limitadors | 11-07, 12-07 |
| Flyway / Liquibase | Migracions d'esquema versionades | 11-03, 12-06 |
Amb aquest mapa al davant, l'ecosistema deixa de ser una llista infinita de noms i passa a ser vuit decisions, cadascuna amb una opció per defecte raonable.
- Com es tria una dependència
Afegir una línia al pom.xml costa cinc segons i compromet el teu equip durant anys. Aquests són els criteris que es fan servir de veritat.
| Criteri | Què mirar | Senyal d'alarma |
|---|---|---|
| Manteniment actiu | Data de l'última versió, incidències obertes i tancades, ritme de publicació | Sense versions des de fa >2 anys; incidències sense resposta |
| Llicència | Apache 2.0, MIT, BSD són segures. GPL/AGPL contaminen. Revisa-ho sempre | GPL en producte comercial tancat; llicència absent |
| Adopció | Nombre d'usos a Maven Central, estrelles, preguntes resoltes en fòrums | Deu usuaris al món; ningú no ha tingut el teu problema |
| Mida i transitivitat | Quants jars arrossega? mvn dependency:tree |
Una utilitat de 20 KB que arrossega 30 MB |
| Alternativa al JDK | Ho fa ja java.util, java.time, java.net.http? |
Sí, i tot i així afegeixes la dependència |
| Compatibilitat | Versió mínima de Java, jakarta.* enfront de javax.* |
Requereix Java 8 i no ha migrat a jakarta |
| Seguretat | Té CVE oberts? Publica avisos? Amb quina rapidesa pedaça? | Vulnerabilitats conegudes sense corregir |
| Documentació | Hi ha guia d'inici, javadoc, exemples? | Només un README de tres línies |
| Cost de sortida | Si demà desapareix, quant costa substituir-la? | Impregna tot el codi |
Una llista de comprovació pràctica abans d'escriure la dependència:
[ ] La necessito de veritat? No ho fa ja el JDK o alguna cosa que ja tinc?
[ ] La llicencia es compatible amb el meu producte?
[ ] Ha publicat versio en els ultims 12 mesos?
[ ] Te CVE oberts sense corregir?
[ ] Quantes dependencies transitives arrossega?
[ ] Es compatible amb la meva versio de Java i amb jakarta.*?
[ ] Hi ha una alternativa mes petita o mes estandard?
[ ] Se com la trauria si calgues?Si dubtes en més de dues caselles, no l'afegeixis encara.
- La pregunta prèvia: la necessito de veritat?
Abans de tots els criteris anteriors hi ha una pregunta que es salta massa gent.
El JDK modern és molt més ric del que la gent recorda. Moltíssimes dependències històriques ja no calen:
| Se solia afegir | Avui ja és al JDK | Des de |
|---|---|---|
| Joda-Time | java.time |
Java 8 |
| Apache HttpClient (per al bàsic) | java.net.http.HttpClient |
Java 11 |
| Commons IO (per al bàsic) | java.nio.file.Files |
Java 7 |
Guava Lists.newArrayList |
List.of, new ArrayList<>() |
Java 9 |
Commons Lang StringUtils.isBlank |
String.isBlank() |
Java 11 |
| Una llibreria per llegir un fitxer sencer | Files.readString(path) |
Java 11 |
record-like generat per Lombok |
record |
Java 16 |
| Base64 de Commons Codec | java.util.Base64 |
Java 8 |
I el contrapès: tampoc no escriguis tu allò que és notòriament difícil de fer bé. Criptografia, anàlisi de dates amb zones horàries, anàlisi de JSON, anàlisi de CSV amb cometes i salts de línia incrustats (recorda el teu LectorCsv de 07-07 i tots els casos límit que no cobria), i qualsevol cosa relacionada amb seguretat. Aquí la dependència madura és sempre millor que la teva implementació.
La regla pràctica: escriu tu allò que és específic del teu negoci; delega allò que és genèric i difícil.
- Maven Central i les coordenades
Quan afegeixes una dependència, d'on surt el jar?
Maven Central és el repositori públic d'artefactes Java, mantingut per Sonatype. Conté milions de versions de centenars de milers de llibreries, i és la destinació per defecte de Maven i Gradle. Quan escrius una dependència i executes mvn package, Maven la descarrega de Central i la desa al teu repositori local (~/.m2/repository) per no tornar-la a baixar.
Cada artefacte s'identifica amb tres coordenades:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.17.1</version>
</dependency>| Coordenada | Què és | Convenció |
|---|---|---|
groupId |
L'organització o projecte | Domini invertit: org.springframework.boot |
artifactId |
El mòdul concret | Nom curt: spring-boot-starter-web |
version |
La versió publicada | Versionat semàntic: 3.3.2 |
S'escriuen abreujades com groupId:artifactId:version, per exemple com.fasterxml.jackson.core:jackson-databind:2.17.1. Veuràs aquesta notació a les traces de Maven, als informes de seguretat i a tota la documentació.
I el camí físic és predictible: groupId amb els punts convertits en carpetes, després artifactId, després version. El jar anterior viu a:
Saber això és útil de veritat el dia que Maven et digui que no troba un artefacte: pots anar a aquella carpeta i veure si hi és, si està corrupte, o si només hi ha un fitxer .lastUpdated (símptoma clàssic de descàrrega fallida que s'arregla esborrant la carpeta i executant mvn -U).
Tot això es desenvolupa a 11-05. Aquí només necessites el vocabulari.
- Versionat semàntic
La majoria de l'ecosistema Java segueix el versionat semàntic (SemVer), amb el format MAJOR.MENOR.PEDAÇ:
| Part | S'incrementa quan | Compatibilitat | Exemple |
|---|---|---|---|
| MAJOR | Hi ha canvis que trenquen | Trenca | 2.7.18 → 3.0.0 |
| MENOR | S'afegeix funcionalitat compatible | Compatible | 3.2.0 → 3.3.0 |
| PEDAÇ | Es corregeixen errors | Compatible | 3.3.1 → 3.3.2 |
Els sufixos habituals, de menys a més estable:
| Sufix | Significat |
|---|---|
-SNAPSHOT |
En desenvolupament, canvia sense avisar. Mai en producció |
-M1, -M2 |
Fita (milestone), molt preliminar |
-RC1 |
Candidata a versió, gairebé definitiva |
| (sense sufix) | Versió alliberada (GA), immutable per sempre |
Dues conseqüències pràctiques:
- Pujar de PEDAÇ o MENOR hauria de ser segur, i és el que fas contínuament per incorporar correccions de seguretat.
- Pujar de MAJOR requereix llegir les notes de migració. L'exemple canònic i molt actual: Spring Boot 2.x → 3.x va canviar tots els paquets
javax.*perjakarta.*i exigeix Java 17. És el motiu que en aquest curs tot el de persistència siguijakarta.persistencei nojavax.persistence. Si copies un exemple d'internet ambjavax.persistence, és de Spring Boot 2 i no et compilarà.
- El cost de la seguretat de la cadena de subministrament
Aquí ve la part incòmoda.
Quan afegeixes una dependència, no afegeixes un jar: afegeixes un arbre. Un spring-boot-starter-web porta de l'ordre de trenta artefactes transitius. Un projecte empresarial típic té entre 100 i 400 jars dels quals tu n'has escrit zero.
Cadascun d'aquests jars executa codi amb els mateixos permisos que la teva aplicació. Pot llegir els teus fitxers, obrir connexions de xarxa i accedir a les teves variables d'entorn. I cap no l'has revisat.
Això s'anomena risc de la cadena de subministrament de programari (software supply chain), i té tres formes:
| Risc | En què consisteix | Exemple |
|---|---|---|
| Vulnerabilitat coneguda | Una fallada publicada (CVE) en una versió que tu fas servir | Log4Shell, Spring4Shell |
| Dependència abandonada | Ningú no pedaça; la vulnerabilitat no es corregirà mai | Llibreries sense versió des del 2018 |
| Compromís de l'artefacte | Algú publica codi maliciós en una llibreria legítima | Casos documentats a npm i PyPI |
- Log4Shell: què va passar de veritat
El desembre del 2021 es va publicar CVE-2021-44228, sobrenomenada Log4Shell, a Apache Log4j 2, una de les llibreries de logging més usades del món Java.
El resum tècnic, sense dramatisme: Log4j 2 admetia als missatges de log una substitució de variables, i una de les formes admeses permetia fer una cerca JNDI contra un servidor remot. Conseqüència: si una aplicació registrava al log un text controlat per l'atacant —una capçalera HTTP, un nom d'usuari, un User-Agent— l'atacant podia provocar que el servidor descarregués i executés codi seu. Execució remota de codi amb una línia de text.
Per què va ser tan greu:
- Trivial d'explotar: n'hi havia prou amb enviar una cadena en un camp qualsevol que acabés en un log.
- Omnipresent: Log4j 2 era en desenes de milers de productes, moltes vegades com a dependència transitiva que l'equip ni sabia que tenia.
- Difícil d'inventariar: la pregunta «tenim Log4j?» va resultar no ser fàcil de respondre a la majoria de les organitzacions. I aquesta va ser la lliçó real.
El que la indústria va aprendre, i que a tu t'afecta directament:
- Has de saber què tens. D'aquí l'auge del SBOM (Software Bill of Materials), l'inventari de components d'una aplicació.
- Has de poder actualitzar de pressa. Si actualitzar una dependència requereix tres setmanes de proves manuals perquè no hi ha proves automàtiques, la teva finestra d'exposició són tres setmanes. Això connecta directament amb el mòdul 11 al complet: sense Maven no pots canviar una versió amb una línia, i sense JUnit no pots verificar que el canvi no ha trencat res.
- Has de vigilar contínuament. Un projecte segur avui té vulnerabilitats d'aquí a sis mesos sense canviar una línia de codi, perquè les vulnerabilitats es descobreixen en codi que ja existia.
Avís important. El que s'explica aquí és el que un desenvolupador ha de saber per no prendre decisions ingènues. En un projecte real, la política de dependències, l'anàlisi de vulnerabilitats i la resposta davant d'incidents són responsabilitat de l'equip o el responsable de seguretat de l'organització, amb eines i processos propis. El teu paper és mantenir l'arbre de dependències sota control, actualitzar quan t'ho indiquin i no introduir dependències sense criteri. Les pràctiques de seguretat de l'aplicació es tracten a 12-07.
- Auditar dependències a la pràctica
Tres eines que faràs servir des de 11-05.
1. Veure l'arbre complet. Aquesta és la primera i la que més es fa servir:
Sortida (retallada) d'un projecte Spring Boot amb web:
[INFO] com.nexussoftware:bibliotech:jar:1.0.0-SNAPSHOT
[INFO] +- org.springframework.boot:spring-boot-starter-web:jar:3.3.2:compile
[INFO] | +- org.springframework.boot:spring-boot-starter:jar:3.3.2:compile
[INFO] | | +- org.springframework.boot:spring-boot-starter-logging:jar:3.3.2:compile
[INFO] | | | +- ch.qos.logback:logback-classic:jar:1.5.6:compile
[INFO] | | | | \- ch.qos.logback:logback-core:jar:1.5.6:compile
[INFO] | | | \- org.slf4j:jul-to-slf4j:jar:2.0.13:compile
[INFO] | +- org.springframework.boot:spring-boot-starter-json:jar:3.3.2:compile
[INFO] | | \- com.fasterxml.jackson.core:jackson-databind:jar:2.17.2:compile
[INFO] | \- org.springframework.boot:spring-boot-starter-tomcat:jar:3.3.2:compileAquí veus de cop tres coses que aquest mòdul explicarà: que Spring Boot ja porta Logback i SLF4J (11-07), que ja porta Jackson (11-07) i que ja porta Tomcat incrustat (12-04). I veus que un jar que tu no vas declarar, logback-core, és a la teva aplicació.
Per buscar qui arrossega un artefacte concret:
2. Buscar vulnerabilitats conegudes. El plugin d'OWASP contrasta el teu arbre amb la base de dades pública de vulnerabilitats:
Genera un informe HTML a target/ amb els CVE trobats i la seva gravetat. En un projecte real això s'executa a la integració contínua, no a mà.
3. Veure què està desactualitzat.
Et llista, dependència a dependència, quina versió fas servir i quina és l'última disponible. Executar-lo cada poques setmanes i actualitzar els pedaços és una de les pràctiques d'higiene més rendibles que existeixen.
- Quin framework aprendre primer
Si has arribat fins aquí, la llista d'opcions pot paralitzar-te. El criteri per triar el primer no és tècnic, és d'aprenentatge:
- Aprèn el que més es faci servir. No perquè sigui el millor, sinó perquè quan t'encallis —i t'encallaràs— hi haurà algú que ja es va encallar igual i ho va escriure. La documentació, els cursos i les respostes són un recurs real.
- Aprèn-ne un bé abans que tres a mitges. Els conceptes —IoC, DI, ORM, AOP, context de persistència— es transfereixen. La sintaxi, no. Qui domina Spring aprèn Quarkus en dues setmanes; qui ha tocat els tres per sobre no en domina cap.
- Aprèn amb un projecte real, no amb exemples aïllats. Per això aquest mòdul va sobre BiblioTech, i per això hi ha un mòdul 12 sencer per integrar-ho tot.
- Per què aquest curs tria Spring Boot
Amb honestedat, tres raons i un advertiment.
- És el que hi ha al mercat. Una majoria molt àmplia de les ofertes de feina de back-end Java esmenten Spring o Spring Boot. És el coneixement amb més valor immediat.
- Cobreix totes les categories del mapa. Amb Spring Boot toques DI, web, persistència, proves, configuració, seguretat i observabilitat amb un model coherent. Aprens vuit coses en un ecosistema en comptes de en vuit.
- La seva màgia és exactament la que ja entens. Anotacions, reflexió, proxies dinàmics. Estàs en la millor posició possible per aprendre'l sense que sigui màgia.
L'advertiment: Spring no és la resposta a tot. Per a un CLI petit és un canó per matar mosques, i fins a 12-03 veuràs que una aplicació de consola ben estructurada no el necessita. Per a funcions serverless amb arrencada en fred, Quarkus o Micronaut són millors. I per a un servei que només fa tres consultes SQL, jOOQ o JdbcTemplate poden ser més adequats que Hibernate. Triar bé és part de l'ofici; i per triar cal conèixer.
- El pla del mòdul 11
Aquest mòdul té una estructura deliberada: cada lliçó salda un deute del mòdul 10 i presenta una eina de forma completa i autònoma. La integració en un projecte real és el mòdul 12.
graph TD
L1["11-01<br/>Introduccio<br/>als frameworks"] --> L2["11-02<br/>Spring<br/>IoC, AOP, Boot"]
L2 --> L3["11-03<br/>Hibernate<br/>JPA sobre H2"]
L3 --> L4["11-04<br/>JUnit 5<br/>les primeres proves"]
L4 --> L5["11-05<br/>Maven<br/>construccio"]
L5 --> L6["11-06<br/>Mockito<br/>proves avancades"]
L6 --> L7["11-07<br/>Jackson, Lombok<br/>SLF4J i ecosistema"]
L7 --> M12["Modul 12<br/>Aplicacio real"]
| Lliçó | Eina | Deute que salda |
|---|---|---|
| 11-02 | Spring Framework i Spring Boot | El ContenidorSimple casolà i la configuració per entorn |
| 11-03 | Hibernate / JPA | El CSV com a persistència |
| 11-04 | JUnit 5 | L'absència total de proves |
| 11-05 | Maven | El javac a mà i les dependències |
| 11-06 | Mockito | Provar allò que depèn de xarxa, disc i rellotge |
| 11-07 | Jackson, Lombok, SLF4J | El JSON amb indexOf, el codi repetitiu, java.util.logging |
Al final del mòdul, BiblioTech serà un projecte Maven amb Spring, persistència JPA sobre H2, proves amb JUnit i Mockito, JSON amb Jackson i logging amb SLF4J. Encara no serà una aplicació completa —això és el mòdul 12—, però totes les seves peces seran les de veritat.
- Errors comuns i consells
Error: creure que un framework t'eximeix d'entendre el que hi ha a sota. És exactament al revés. El framework automatitza; quan l'automatització falla, només pot arreglar-ho qui entén el mecanisme. Si @Transactional no reverteix, necessites saber de proxies i de transaccions de base de dades.
Error: afegir dependències sense avaluar-les. «Necessito ordenar una llista, afegiré Guava.» No. Mira primer el JDK, després el que ja tens a l'arbre, i només després Maven Central.
Error: copiar exemples d'internet sense mirar la versió. El símptoma inequívoc: javax.persistence en comptes de jakarta.persistence, o WebSecurityConfigurerAdapter, que es va eliminar. Són de Spring Boot 2 i no compilen al 3. Comprova sempre la data i la versió de l'exemple.
Error: fixar versions de dependències que gestiona el BOM. Quan facis servir spring-boot-starter-parent, no posis <version> a les dependències que ell gestiona: li estàs traient la feina de garantir que les versions són compatibles entre si. S'explica a 11-05.
Error: fer servir rangs de versions o LATEST. Trenca la reproductibilitat: la mateixa etiqueta de git compila diferent segons el dia. Fixa sempre versions concretes.
Error: no actualitzar mai «perquè funciona». Una aplicació que no es toca durant tres anys acumula desenes de vulnerabilitats conegudes. I quan per fi cal actualitzar, el salt és tan gran que es converteix en un projecte.
Consell: distingeix el nucli de la lampisteria. Mantén el teu domini (entitats, regles de negoci) com més net millor d'anotacions del framework. La lògica de les multes de BiblioTech ha de poder-se provar sense arrencar Spring. Aquest principi s'aprofundeix a 12-02.
Consell: aprèn a llegir un dependency:tree. És l'eina que més vegades et traurà d'un compromís: conflictes de versions, jars duplicats, classes que apareixen dues vegades, i la pregunta de seguretat «tinc aquesta llibreria?».
Consell: quan alguna cosa del framework et sembli màgia, busca el mecanisme. Pregunta't: és una anotació llegida per reflexió? És un proxy? És codi generat en compilació? Gairebé sempre és una de les tres, i llavors deixa de ser màgia.
Consell: mira les dependències d'un starter abans d'afegir-lo. Un spring-boot-starter-web porta Tomcat, Jackson, validació i logging. Saber-ho evita afegir Jackson pel teu compte amb una altra versió i provocar un conflicte.
- Exercicis
Exercici 1: classificar i justificar
Per a cada element de la llista, indica si és llibreria o framework, i justifica la resposta amb el criteri de "qui crida qui". En els casos ambigus, explica per què ho són.
java.util.Collections- JUnit 5
- Jackson
ObjectMapper - Hibernate
- SLF4J
- Spring Boot
- El
ContenidorSimpleque vas escriure a 10-03 - El
ProxyAuditoriaque vas escriure a 10-03
Exercici 2: avaluar una dependència real
Nexus Software necessita generar codis de barres per a les etiquetes dels llibres de BiblioTech. Un company proposa afegir una llibreria que va trobar. Escriu l'anàlisi que faries abans d'acceptar-la, fent servir la llista de comprovació de la lliçó, i decideix. Dades disponibles:
groupId:artifactId—com.exemple:barcode-magic- Última versió:
0.4.1, publicada fa 3 anys - Llicència: no consta al POM
- Usos a Maven Central: 7 projectes
- Dependències transitives: 14 jars, inclosos un motor gràfic complet i una llibreria XML
- Documentació: un README de 8 línies
- Un CVE de gravetat mitjana obert des de fa 14 mesos
Exercici 3: el mapa invers
Per a cadascun d'aquests comportaments "màgics" que veuràs a les properes lliçons, indica quin dels quatre mecanismes de l'apartat 6 el fa possible i què vas escriure tu al mòdul 10 que s'hi assembla.
- Escrius
@ServicesobreGestorPrestecsi Spring l'instancia sol. - Escrius
@Transactionali la base de dades reverteix en llançar una excepció. - Escrius
@Getterde Lombok i apareix ungetTitol()que tu no vas escriure. - Declares
interface PrestecRepository extends JpaRepository<Prestec, Long>sense implementar-la i funciona. - Escrius
@Testi el mètode s'executa sensemain. - Escrius
@JsonProperty("isbn_13")i Jackson fa servir aquest nom al JSON.
Solucions
Solució 1
| Element | Tipus | Justificació |
|---|---|---|
java.util.Collections |
Llibreria | Tu crides Collections.sort(...) quan vols. Control totalment teu. |
| JUnit 5 | Framework | No hi ha main. JUnit descobreix els teus @Test per reflexió i els invoca. Principi de Hollywood pur. |
Jackson ObjectMapper |
Llibreria | Tu crides writeValueAsString(...). Encara que llegeixi anotacions teves, el flux el controles tu. |
| Hibernate | Tots dos | És llibreria quan crides entityManager.find(...); és framework quan la comprovació de canvis automàtica detecta que vas modificar una entitat gestionada i emet un UPDATE sense que tu cridis res. Aquest segon comportament és control invertit (es veu a 11-03). |
| SLF4J | Llibreria (façana) | Tu crides log.info(...). A més és una façana: la implementació es tria en temps d'execució, però el flux continua sent teu. |
| Spring Boot | Framework | Arrenca ell, escaneja les teves classes, les instancia, les injecta i crida el teu CommandLineRunner o el teu controlador quan arriba una petició. |
El teu ContenidorSimple |
Framework (en miniatura) | Escaneja @Component, decideix l'ordre de construcció i instancia les teves classes. Tu no crides new: el crida ell. |
El teu ProxyAuditoria |
Framework (en miniatura) | Intercepta la crida, executa codi abans i després, i decideix quan invocar el teu mètode. El flux passa per ell. |
La conclusió que importa: vas escriure dos frameworks en miniatura sense anomenar-los així.
Solució 2
Aplicant la llista de comprovació:
| Casella | Resultat | Comentari |
|---|---|---|
| La necessito de veritat? | Sí | Generar un codi de barres correcte (checksums EAN-13, quiet zones) és difícil de fer bé a mà. És un cas legítim de dependència. |
| Llicència compatible? | NO | Sense llicència declarada, per defecte no hi ha permís d'ús. Això per si sol bloqueja la decisió. |
| Versió en els últims 12 mesos? | NO | 3 anys sense publicar. Morta a efectes pràctics. |
| CVE oberts? | NO | Un de gravetat mitjana, sense corregir en 14 mesos: confirma l'abandonament. |
| Transitivitat raonable? | NO | 14 jars, amb un motor gràfic i una llibreria XML, per generar una imatge de codi de barres. Desproporcionat i superfície d'atac enorme. |
| Compatible amb el meu Java? | Desconegut | Versió 0.x: l'API pot canviar sense previ avís, encara que l'abandonament ho fa irrellevant. |
| Alternativa millor? | Sí | ZXing (Apache 2.0, de Google, mantinguda, molt usada) o Barcode4J són opcions madures per al mateix. |
| Cost de sortida? | Baix | Es faria servir des d'un únic punt, darrere d'una interfície pròpia. És l'única cosa positiva. |
Decisió: rebutjar. Quatre caselles crítiques fallen, i la llicència sola ja bastaria. Contraproposta: fer servir ZXing, i fer-ho darrere d'una interfície pròpia (GeneradorEtiquetes), de manera que la llibreria concreta quedi aïllada en una sola classe i el cost de canviar-la en el futur sigui d'una implementació. I afegir l'anàlisi de dependències a la integració contínua perquè el proper CVE es detecti sol.
Nota professional: el punt de la llicència no es negocia per criteri tècnic. En una empresa, un jar sense llicència declarada és un problema legal, i aquesta conversa l'ha de tenir el responsable corresponent, no el desenvolupador pel seu compte.
Solució 3
| Comportament | Mecanisme | El teu equivalent del mòdul 10 |
|---|---|---|
1. @Service instancia sol |
Anotació + reflexió. Spring escaneja el classpath, troba la classe anotada, llegeix el seu constructor amb getDeclaredConstructors(), resol els paràmetres i crida newInstance |
El ContenidorSimple amb @Component, exactament el mateix bucle (10-03) |
2. @Transactional reverteix |
Proxy dinàmic. L'objecte que hi ha al contenidor no és la teva classe: és un embolcall que obre transacció, invoca el teu mètode i confirma o reverteix segons hi hagi excepció | ProxyAuditoria i ProxyReintents: l'InvocationHandler que executa codi abans i després de metode.invoke(...) (10-03) |
3. @Getter de Lombok |
Generació de codi en compilació (processador d'anotacions). No hi ha reflexió ni proxy: el .class ja conté el mètode. Pots veure-ho amb javap |
El processador d'anotacions estudiat a 10-02 |
4. JpaRepository sense implementació |
Proxy dinàmic sobre interfície més anàlisi del nom del mètode. Proxy.newProxyInstance genera una implementació al vol que tradueix findByIsbn a una consulta |
Proxy.newProxyInstance sobre interfícies, tal qual el vas escriure (10-03) |
5. @Test s'executa sense main |
Anotació + reflexió. JUnit Platform descobreix les classes, filtra els mètodes amb isAnnotationPresent(Test.class) i els invoca amb Method.invoke |
L'ExportadorAnotat, que recorria membres buscant una anotació i actuava (10-03) |
6. @JsonProperty("isbn_13") |
Anotació + reflexió. Jackson introspecciona la classe, llegeix l'anotació del camp i fa servir el seu valor com a clau del JSON | @CampCsv("nom") llegida per l'ExportadorAnotat — és literalment el mateix disseny (10-02, 10-03) |
Si has pogut completar aquesta taula, l'objectiu de la lliçó està assolit: no queda màgia, queden quatre mecanismes que ja coneixes aplicats a escala industrial.
Conclusió
Els frameworks han deixat de ser una caixa negra abans fins i tot de fer servir el primer.
Saps distingir llibreria de framework per l'única pregunta que importa —qui crida qui— i reconeixes el principi de Hollywood quan el veus: un mètode @Test sense main, un @Service que s'instancia sol, una entitat que es desa sense que cridis save. I entens la inversió de control no com una definició de manual sinó com el canvi concret que vas fer a GestorPrestecs: de crear les seves pròpies dependències amb new a declarar-les al constructor i deixar que un altre les resolgui — amb les quatre conseqüències que això va tenir (es pot provar, es pot canviar d'implementació, es pot configurar per entorn i el rellotge deixa de ser el del sistema).
Saps què compres i què pagues. Compres codi no diferenciador ja resolt, convencions que qualsevol desenvolupador entén, un ecosistema integrat i —el més subestimat— pedaços de seguretat mantinguts per altres. Pagues corba d'aprenentatge, acoblament arquitectònic, comportament implícit difícil de depurar i pes. I saps que la resposta correcta a «framework sí o no?» sempre comença per «depèn del context».
I sobretot, saps com se sosté la màgia: anotacions amb @Retention(RUNTIME) que són etiquetes inertes; reflexió que les llegeix i actua; proxies dinàmics que embolcallen els teus objectes per afegir comportament sense tocar el teu codi —amb el detall, que ja et trobaràs, que la crida interna no passa pel proxy i que CGLIB no pot amb allò que és final—; i generació de codi, en compilació amb processadors d'anotacions o en execució amb bytecode. Quatre mecanismes. Els quatre els has escrit o estudiat. I tens el mapa complet d'allò que vas fer a mà → allò que fa el framework de veritat, que és alhora el teu full de ruta del mòdul i el teu manual de diagnòstic quan alguna cosa falli.
Tens el mapa de l'ecosistema ordenat en vuit categories amb una opció per defecte a cadascuna, de manera que la llista infinita de noms es converteix en vuit decisions. I tens criteri per a la decisió que prendràs desenes de vegades: afegir o no una dependència. Amb la pregunta prèvia al davant —no ho fa ja el JDK?, perquè java.time, HttpClient, Files.readString, String.isBlank i els record han deixat obsoletes moltes dependències clàssiques— i amb els criteris reals al darrere: manteniment, llicència, adopció, transitivitat, seguretat i cost de sortida.
Coneixes Maven Central, les coordenades groupId:artifactId:version, el camí predictible dins de ~/.m2 i el versionat semàntic amb la seva conseqüència més pràctica avui: el salt de Spring Boot 2 a 3 va canviar javax.* per jakarta.*, i per això un exemple copiat d'internet amb javax.persistence senzillament no et compilarà.
I entens el risc de la cadena de subministrament: que un projecte mitjà té centenars de jars que ningú no ha revisat executant-se amb els teus permisos, que Log4Shell va demostrar que la pregunta difícil no era pedaçar sinó saber què tens, i que la capacitat d'actualitzar de pressa depèn exactament de les dues eines d'aquest mòdul —Maven per canviar una versió amb una línia i JUnit per verificar que no has trencat res—. Amb l'advertiment clar que, en un projecte real, la política de dependències i la resposta davant de vulnerabilitats corresponen al responsable de seguretat, i que les pràctiques de seguretat de l'aplicació arriben a 12-07.
El pla està fixat i cada lliçó salda un deute. El primer és el més gran: aquell ContenidorSimple de cent cinquanta línies que vas escriure per no cridar new, i al qual li falta absolutament tota la resta — àmbits, cicle de vida, perfils, propietats externalitzades, resolució d'ambigüitat i transaccions.
A la propera lliçó, BiblioTech llença aquest contenidor a les escombraries i adopta el que porten vint anys polint deu mil desenvolupadors. Veuràs l'ApplicationContext, les tres formes d'injecció i per què només una és recomanable, els estereotips, el cicle de vida d'un bean, els àmbits, els perfils dev i prod, les propietats tipades que substitueixen la teva Configuracio de 07-07, i l'autoconfiguració de Spring Boot explicada de veritat — no com a màgia, sinó com un conjunt de condicions que pots llegir i depurar.
I quan arribis a @Transactional, no veuràs una anotació misteriosa: veuràs el teu propi ProxyAuditoria, amb vint anys de poliment a sobre.
Comencem per Spring.
Curs de Programació en Java
Mòdul 1: Introducció a Java
- Introducció a Java
- Configuració de l'entorn de desenvolupament
- Sintaxi i estructura bàsica
- Variables i tipus de dades
- Operadors
- Entrada i sortida per consola
- El teu primer programa complet: BiblioTech
Mòdul 2: Flux de control
- Sentències condicionals
- Bucles
- Sentències switch
- Break i continue
- Depuració i traces d'execució
- Projecte: menú interactiu de BiblioTech
Mòdul 3: Programació orientada a objectes
- Introducció a la POO
- Classes i objectes
- Mètodes
- Constructors
- Herència
- Polimorfisme
- Encapsulament
- Abstracció
- La classe Object: equals, hashCode i toString
Mòdul 4: Programació orientada a objectes avançada
- Interfícies
- Classes abstractes
- Classes internes
- Classes anònimes
- Expressions lambda
- Interfícies funcionals i referències a mètodes
- Enumeracions i registres
Mòdul 5: Estructures de dades i col·leccions
- Arrays
- El framework de col·leccions
- ArrayList
- LinkedList
- HashMap
- HashSet
- Cua i Deque
- Pila
- Ordenació i cerca en col·leccions
Mòdul 6: Gestió d'excepcions
- Introducció a les excepcions
- Bloc try-catch
- Throw i throws
- Excepcions personalitzades
- Bloc finally
- Try-with-resources i AutoCloseable
- Estratègies de gestió d'errors i logging
Mòdul 7: Entrada/sortida de fitxers
- Lectura de fitxers
- Escriptura de fitxers
- Fluxos de fitxers
- BufferedReader i BufferedWriter
- Serialització
- L'API NIO.2: Path i Files
- Formats d'intercanvi: CSV i Properties
Mòdul 8: Multifil i concurrència
- Introducció al multifil
- Creació de fils
- Cicle de vida d'un fil
- Sincronització
- Utilitats de concurrència
- Col·leccions concurrents i variables atòmiques
- Tasques asíncrones amb CompletableFuture
Mòdul 9: Xarxes
- Introducció a les xarxes
- Sockets
- ServerSocket
- DatagramSocket i DatagramPacket
- URL i HttpURLConnection
- El client HTTP modern
Mòdul 10: Temes avançats
- Genèrics
- Anotacions
- Reflexió
- Característiques de Java 8: Streams i Optional
- Dates i hores amb java.time
- Java 9 i més enllà
- Memòria, recol·lecció de brossa i rendiment
Mòdul 11: Frameworks i llibreries de Java
- Introducció als frameworks de Java
- Spring Framework
- Hibernate
- JUnit
- Maven
- Proves avançades amb Mockito
- Llibreries essencials de l'ecosistema
