El módulo anterior terminó con una promesa: «Has escrito versiones en miniatura de casi todos ellos con tus propias manos. Ahora vas a usar los de verdad.»

Esta lección es el puente. Antes de escribir la primera anotación de Spring o la primera entidad de Hibernate, hay que entender qué es exactamente un framework, en qué se diferencia de una librería, qué te da y qué te quita, y —sobre todo— cómo funciona por dentro, porque la respuesta a esa última pregunta ya la sabes: anotaciones, reflexión, proxies dinámicos y generación de código. Exactamente lo que construiste en el módulo 10.

Esto importa por una razón muy práctica. La diferencia entre un desarrollador que usa Spring y uno que entiende Spring no está en cuántas anotaciones se sabe de memoria: está en que cuando algo falla —y va a fallar— el primero busca en un foro y el segundo razona sobre qué está haciendo el contenedor. Cuando veas que una llamada a un método @Transactional desde otro método de la misma clase no abre transacción, tú sabrás por qué: porque el proxy dinámico que escribiste en 10-03 tiene exactamente ese mismo agujero, y lo viste con tus ojos.

Además, hay una decisión profesional que nadie te enseña y que vas a tomar decenas de veces en tu carrera: añadir o no una dependencia. Es una decisión con consecuencias de seguridad, de mantenimiento y de peso. Esta lección te da criterios reales para tomarla.

Al terminar sabrás distinguir librería de framework y explicar la inversión de control con un ejemplo que escribiste tú, tendrás un mapa del ecosistema Java por categorías, sabrás cómo se evalúa una dependencia antes de meterla en tu pom.xml, entenderás qué es Maven Central y qué significan las coordenadas groupId:artifactId:version, y conocerás el riesgo de la cadena de suministro de software con un caso real que paralizó la industria durante semanas.

Contenido

  1. El punto de partida: BiblioTech hecho a mano
  2. Librería frente a framework: quién llama a quién
  3. La inversión del control, explicada con tu propio ContenedorSimple
  4. Qué aporta un framework
  5. Qué cuesta un framework
  6. Cómo se sostiene la magia: los cuatro mecanismos
  7. El mapa: lo que escribiste a mano frente a lo que hace el framework de verdad
  8. Mapa del ecosistema Java por categorías
  9. Cómo se elige una dependencia
  10. La pregunta previa: ¿lo necesito de verdad?
  11. Maven Central y las coordenadas
  12. Versionado semántico
  13. El coste de la seguridad de la cadena de suministro
  14. Log4Shell: qué pasó de verdad
  15. Auditar dependencias en la práctica
  16. Qué framework aprender primero
  17. Por qué este curso elige Spring Boot
  18. El plan del módulo 11
  19. Errores Comunes y Consejos
  20. Ejercicios

  1. El punto de partida: BiblioTech hecho a mano

Antes de mirar hacia adelante, mira una vez más lo que tienes. BiblioTech, al terminar el módulo 10, es un proyecto respetable: dominio sellado, repositorios genéricos, streams, java.time, hilos virtuales, un contenedor de inyección de dependencias casero y tres proxies dinámicos.

Y tiene seis deudas declaradas:

Deuda Estado actual Qué falta
Inyección de dependencias ContenedorSimple, 150 líneas Ámbitos, ciclo de vida, configuración por entorno, transacciones
Persistencia Ficheros CSV Consultas, índices, transacciones, integridad referencial
JSON indexOf a mano (09-06) Un parseador de verdad
Código repetitivo Getters, equals, hashCode, toString a mano Generación automática
Logging java.util.logging Una fachada estándar del ecosistema
Construcción javac a mano Dependencias, fases, empaquetado, reproducibilidad
Pruebas Ninguna Todo

Cada una de esas filas es una lección de este módulo. Pero fíjate en algo: ninguna de esas deudas es de lógica de negocio. Ninguna tiene que ver con préstamos, multas, reservas o catálogos. Todas son fontanería: cosas que toda aplicación necesita y que ninguna aplicación quiere escribir.

Esa observación es, literalmente, la definición del problema que resuelven los frameworks.

  1. Librería frente a framework: quién llama a quién

La distinción se suele explicar mal. No es una cuestión de tamaño ni de complejidad. Hay librerías enormes y frameworks minúsculos. La diferencia es de dirección de las llamadas.

  • Una librería es código que tú llamas. Tú tienes el control del flujo: decides cuándo, dónde y con qué argumentos. java.util.Collections, Jackson o Apache Commons son librerías: llamas a Collections.sort(lista) cuando te conviene.
  • Un framework es código que te llama a ti. El framework tiene el control del flujo: define la estructura de la aplicación, arranca, y en determinados puntos invoca tu código. Tú rellenas huecos.

Esta inversión tiene un nombre clásico, el Principio de Hollywood: "No nos llames, ya te llamaremos nosotros."

graph LR
    subgraph "LIBRERÍA"
        A1["Tu código<br/>(controla el flujo)"] -->|llama| B1["Jackson<br/>Commons<br/>Guava"]
        B1 -->|devuelve| A1
    end
    subgraph "FRAMEWORK"
        A2["Spring<br/>JUnit<br/>Servlet API"] -->|llama| B2["Tu código<br/>(rellena huecos)"]
        B2 -->|devuelve| A2
    end

Un ejemplo concreto que ya conoces, de 09-06. Cuando escribiste el ClienteMetadatos con HttpClient:

// LIBRERÍA: tú decides cuándo llamar
HttpResponse<String> respuesta = cliente.send(peticion, BodyHandlers.ofString());
String cuerpo = respuesta.body();

Tú controlas todo. HttpClient es una librería.

Y ahora, un ejemplo de framework que verás en 11-04:

class CalculadoraMultasTest {
    @Test
    void multaDeCincoDiasSonCincoEuros() {
        // ...
    }
}

¿Dónde está el main? ¿Quién llama a multaDeCincoDiasSonCincoEuros()? Tú no. Lo llama JUnit. Tú solo has escrito un método y le has puesto una etiqueta. JUnit escanea tus clases, encuentra los métodos anotados con @Test, crea una instancia por cada uno y los invoca. Eso es un framework.

Compara los dos modelos:

Aspecto Librería Framework
Control del flujo Tuyo Del framework
Quién llama a quién Tú → librería Framework → tú
Estructura de tu código Libre Impuesta (convenciones, anotaciones, interfaces)
Coste de sustituirla Bajo (aíslas la llamada) Alto (impregna la arquitectura)
Ejemplos Jackson, Guava, Commons, SLF4J Spring, JUnit, Hibernate, Quarkus
Punto de entrada Métodos que invocas Hooks: anotaciones, interfaces, ficheros de configuración

Un matiz honesto: la frontera no siempre es nítida. Spring es un framework, pero RestTemplate dentro de Spring es una librería. Hibernate es un framework cuando gestiona el ciclo de vida de tus entidades, y una librería cuando llamas a entityManager.find(...). Lo útil no es clasificar, sino preguntarse en cada punto: ¿quién controla el flujo aquí?

  1. La inversión del control, explicada con tu propio ContenedorSimple

La inversión de control (IoC, Inversion of Control) es el principio general: ceder el control del flujo a otro componente. La inyección de dependencias (DI, Dependency Injection) es la forma concreta de IoC que más vas a usar: en vez de que un objeto cree sus propias dependencias, se las dan hechas.

Esto lo escribiste tú en 10-03. Antes del ContenedorSimple, GestorPrestamos hacía esto:

// ANTES: el objeto crea sus propias dependencias. Control NO invertido.
public class GestorPrestamos {

    private final Repositorio<Prestamo> repositorio;
    private final CalculadoraMultas calculadora;
    private final ServicioAvisos avisos;

    public GestorPrestamos() {
        // El propio gestor decide QUÉ implementación usar y CÓMO construirla.
        this.repositorio = new AlmacenPrestamos(Path.of("datos/prestamos.csv"));
        this.calculadora = new CalculadoraMultas(Clock.systemDefaultZone());
        this.avisos = new ServicioAvisos("smtp.nexussoftware.com", 587);
    }
}

Problemas de ese código, todos reales:

  1. No se puede probar. Para probar el cálculo de una multa necesitas un fichero CSV en disco y un servidor SMTP.
  2. No se puede cambiar de implementación. Si mañana el almacén es una base de datos, hay que editar GestorPrestamos.
  3. No se puede configurar por entorno. El host SMTP de desarrollo y el de producción son distintos, y están escritos a fuego.
  4. El reloj es el del sistema. Ese Clock.systemDefaultZone() hace imposible probar un vencimiento sin esperar 15 días. Justo lo que 10-05 quería evitar.

Después del ContenedorSimple:

// DESPUÉS: el objeto DECLARA lo que necesita. Control invertido.
public class GestorPrestamos {

    private final Repositorio<Prestamo> repositorio;
    private final CalculadoraMultas calculadora;
    private final ServicioAvisos avisos;

    public GestorPrestamos(Repositorio<Prestamo> repositorio,
                           CalculadoraMultas calculadora,
                           ServicioAvisos avisos) {
        this.repositorio = repositorio;
        this.calculadora = calculadora;
        this.avisos = avisos;
    }
}

GestorPrestamos ya no sabe de dónde salen sus colaboradores. Solo sabe qué necesita. Alguien de fuera —el contenedor— resuelve el grafo de dependencias, construye todo en el orden correcto y le pasa las piezas.

Visualmente, el cambio es este:

graph TD
    subgraph "SIN inversión de control"
        G1["GestorPrestamos"] -->|new| R1["AlmacenPrestamos"]
        G1 -->|new| C1["CalculadoraMultas"]
        G1 -->|new| A1["ServicioAvisos"]
    end
    subgraph "CON inversión de control"
        CT["Contenedor IoC"] -->|construye| R2["AlmacenPrestamos"]
        CT -->|construye| C2["CalculadoraMultas"]
        CT -->|construye| A2["ServicioAvisos"]
        CT -->|construye e inyecta| G2["GestorPrestamos"]
        R2 -.->|inyectado| G2
        C2 -.->|inyectado| G2
        A2 -.->|inyectado| G2
    end

Tu ContenedorSimple hacía esto: escaneaba las clases anotadas con @Componente, miraba sus constructores con reflexión, resolvía recursivamente cada parámetro, detectaba ciclos y cacheaba los singletons. Ciento cincuenta líneas.

El ApplicationContext de Spring hace exactamente lo mismo. Y unas cuantas cosas más:

Capacidad Tu ContenedorSimple ApplicationContext de Spring
Escanear componentes Sí, @Componente Sí, @Component y estereotipos
Inyección por constructor Sí (recomendada)
Inyección por setter y campo No
Detección de ciclos Sí, con excepción Sí, con mensaje mucho mejor
Singleton Sí, un Map Sí, más 5 ámbitos adicionales
Ámbito prototipo / petición / sesión No
Ciclo de vida (@PostConstruct / @PreDestroy) No
Configuración por entorno (perfiles) No Sí, @Profile
Propiedades externalizadas No Sí, @Value, @ConfigurationProperties
Resolución de ambigüedad No (fallaba con 2 implementaciones) Sí, @Qualifier, @Primary
Inyectar todas las implementaciones en una List<T> No
Transacciones declarativas No Sí, @Transactional
Programación orientada a aspectos Tres proxies escritos a mano Sí, integrada
Creación perezosa No Sí, @Lazy
Eventos entre componentes No Sí, ApplicationEventPublisher
Arranque de aplicación web No

Ninguna de esas filas adicionales es lógica de negocio. Todas son cosas que necesitarías escribir si tu aplicación creciera. Eso es lo que compras.

  1. Qué aporta un framework

Cuatro cosas concretas, por orden de importancia real.

1. Código no diferenciador ya resuelto. Hay una distinción útil entre código diferenciador (el que resuelve el problema por el que existe tu empresa: en BiblioTech, las reglas de préstamos, multas y reservas) y código de fontanería (lo que necesita cualquier aplicación: arrancar, configurarse, hablar con una base de datos, atender HTTP, registrar trazas). El código de fontanería no aporta valor a nadie y sin embargo se lleva la mayor parte del tiempo cuando lo escribes tú. Un framework te devuelve ese tiempo.

2. Convenciones compartidas. Si contratas a un desarrollador Java con experiencia en Spring, entiende la estructura de tu proyecto en una tarde. Si BiblioTech usara tu ContenedorSimple casero, tardaría una semana en entender un contenedor que solo existe en tu empresa y del que no hay documentación, cursos, ni respuestas en foros. Las convenciones son un activo económico.

3. Ecosistema. Cuando eliges Spring, no eliges solo un contenedor: eliges Spring Data (persistencia), Spring Security (autenticación), Spring Cloud (sistemas distribuidos), Actuator (observabilidad) y cientos de integraciones que ya funcionan juntas. La integración ya está hecha y probada, y eso vale más que cualquier característica individual.

4. Seguridad mantenida. Cuando aparece una vulnerabilidad en el manejo de peticiones HTTP, el equipo de Spring publica un parche y tú actualizas una versión. Si esa lógica es tuya, la vulnerabilidad la descubres cuando te la explotan. Este punto se suele infravalorar y es probablemente el más importante en producción.

  1. Qué cuesta un framework

Un framework no es gratis. Cuatro costes, también reales.

1. Curva de aprendizaje. Spring tiene más de veinte años de superficie acumulada. Ser productivo lleva semanas; ser competente, meses. Y buena parte de ese aprendizaje no es transferible: aprender @Transactional no te enseña nada sobre bases de datos, te enseña sobre Spring.

2. Acoplamiento. Un framework impregna la arquitectura. Migrar una aplicación de Spring a Quarkus no es cambiar una dependencia: es reescribir la capa de configuración, la de arranque, la de pruebas y probablemente parte del dominio. Por eso conviene mantener el dominio limpio de anotaciones del framework siempre que sea posible — una idea que se desarrolla en 12-02.

3. Magia difícil de depurar. Cuando un @Autowired no inyecta, o una transacción no hace rollback, o una entidad se guarda sola sin que llames a save, la causa está en código que tú no escribiste, invocado por reflexión desde un punto que no aparece en tu traza. Esta es exactamente la razón de que el módulo 10 fuera antes que este: si sabes cómo funciona la magia, puedes depurarla.

4. Peso. Un jar ejecutable de Spring Boot con web y JPA ronda los 40-50 MB y arranca en 1-3 segundos. Para un servicio de larga vida es irrelevante. Para una función serverless que arranca en cada petición o un CLI que debe responder en 50 ms, es descalificador — y ahí es donde entran Quarkus, Micronaut o la compilación nativa con GraalVM que se mencionó en 10-07.

Resumido:

Ganas Pagas
Semanas de fontanería resuelta Semanas de curva de aprendizaje
Convenciones que cualquiera entiende Acoplamiento a una arquitectura concreta
Un ecosistema integrado y probado Comportamiento implícito difícil de depurar
Parches de seguridad mantenidos Superficie de ataque de terceros
Menos código propio que mantener Más peso, más arranque, más memoria

La conclusión profesional no es "los frameworks son buenos" ni "son malos": es que la decisión depende del contexto, y que quien no entiende lo que hay debajo no puede tomarla.

  1. Cómo se sostiene la magia: los cuatro mecanismos

Aquí es donde el módulo 10 se cobra por completo. Todo lo que un framework Java hace "por arte de magia" se apoya en cuatro mecanismos, y los cuatro los conoces.

Mecanismo 1: anotaciones (10-02)

Las anotaciones son metadatos: etiquetas que no hacen nada por sí solas. @Service no arranca nada. @Entity no crea ninguna tabla. @Test no ejecuta nada. Son marcas inertes que alguien tiene que leer.

Lo aprendiste construyendo @CampoCsv y @Auditable. Y aprendiste la condición imprescindible:

@Retention(RetentionPolicy.RUNTIME)   // sin esto, la anotación NO existe en ejecución
@Target(ElementType.TYPE)
public @interface Componente { }

Toda anotación de framework que se lea en ejecución lleva @Retention(RUNTIME). Ábrete el código fuente de @Service de Spring o de @Entity de Jakarta Persistence: está ahí.

Mecanismo 2: reflexión (10-03)

Alguien tiene que leer las etiquetas. Ese alguien usa reflexión: Class.forName, getDeclaredFields, isAnnotationPresent, getDeclaredConstructor().newInstance(), invoke, setAccessible(true).

Tu ExportadorAnotado recorría los campos de cualquier objeto buscando @CampoCsv. Hibernate recorre los campos de cualquier entidad buscando @Column. Es el mismo bucle.

Mecanismo 3: proxies dinámicos (10-03)

Cuando el framework necesita añadir comportamiento alrededor de tu método sin tocar tu código —transacciones, seguridad, caché, reintentos, métricas—, no modifica tu clase: la envuelve. Escribiste tres:

// Tu ProxyAuditoria de 10-03
Object proxy = Proxy.newProxyInstance(
    interfaz.getClassLoader(),
    new Class<?>[] { interfaz },
    (p, metodo, args) -> {
        if (metodo.isAnnotationPresent(Auditable.class)) {
            registro.anotarInicio(metodo.getName());
        }
        Object resultado = metodo.invoke(objetivo, args);   // llamada real
        // ... post-procesado
        return resultado;
    });

Cuando en 11-02 veas @Transactional, no verás nada nuevo: verás ese mismo InvocationHandler con iniciar transacción antes y confirmar o revertir después. Y verás también el mismo agujero: si el objetivo se llama a sí mismo internamente, la llamada no pasa por el proxy y el aspecto no se aplica. Ese detalle es la causa de un porcentaje enorme de los "mi @Transactional no funciona" del mundo.

Nota técnica: los proxies JDK que escribiste solo funcionan sobre interfaces. Cuando la clase no implementa ninguna, Spring usa CGLIB, que genera en tiempo de ejecución una subclase de tu clase y sobrescribe sus métodos. De ahí una consecuencia práctica: una clase o un método final no se puede proxiar con CGLIB, y por eso a veces tu @Transactional sobre un método final no hace absolutamente nada.

Mecanismo 4: generación de código

Hay dos momentos posibles.

  • En tiempo de compilación, mediante un procesador de anotaciones (javax.annotation.processing, visto en 10-02). Es lo que hace Lombok: no hay magia en ejecución, el .class ya contiene los getters. También MapStruct y Micronaut.
  • En tiempo de ejecución, generando bytecode al vuelo con ASM, ByteBuddy o CGLIB. Es lo que hacen Spring, Hibernate y Mockito.

Cada opción tiene sus consecuencias:

Mecanismo Cuándo actúa Coste en arranque Depurable Ejemplos
Anotaciones + reflexión Ejecución Medio (escaneo) Regular Spring, Hibernate, JUnit, Jackson
Proxy dinámico JDK Ejecución Bajo Regular (trazas con $Proxy0) @Transactional, Spring Data
Generación de subclase (CGLIB/ByteBuddy) Ejecución Medio Difícil Spring AOP sobre clases, Mockito
Procesador de anotaciones Compilación Ninguno Fácil (el código existe) Lombok, MapStruct, Micronaut

La tendencia actual, precisamente por el coste de arranque y por la compatibilidad con la compilación nativa de GraalVM, es mover trabajo de la ejecución a la compilación. Quarkus y Micronaut nacieron con esa idea, y Spring 6 la ha adoptado parcialmente con AOT.

  1. El mapa: lo que escribiste a mano frente a lo que hace el framework de verdad

Esta tabla es el resumen del módulo 10 leído desde el módulo 11. Es, probablemente, la tabla más importante de esta lección.

Lo que escribiste a mano Lección Lo que hace el framework de verdad Se ve en
ContenedorSimple con @Componente e @Inyectar 10-03 ApplicationContext de Spring con @Component y @Autowired 11-02
ProxyAuditoria, ProxyReintentos, ProxyCronometro 10-03 Spring AOP, @Transactional, @Retryable, @Cacheable 11-02
ExportadorAnotado que lee @CampoCsv por reflexión 10-03 Hibernate leyendo @Column, Jackson leyendo @JsonProperty 11-03, 11-07
ValidadorAnotado con @Validar 10-03 Jakarta Bean Validation (@NotNull, @Size, @Email) 11-02
LectorCsv / EscritorCsv a mano 07-07 OpenCSV, Commons CSV — y, mejor aún, una base de datos 11-03, 11-07
AlmacenPrestamos sobre ficheros 07-06 Hibernate / JPA sobre una base de datos relacional 11-03
Repositorio<T extends Identificable> genérico 10-01 JpaRepository<T, ID> de Spring Data, implementado con proxies 11-03
Parseo de JSON con indexOf 09-06 Jackson ObjectMapper 11-07
Getters, equals, hashCode, toString a mano 03-09 Lombok (o record, que ya usas) 11-07
ConfiguracionLog con java.util.logging 06-07 SLF4J + Logback 11-07
Configuracion / ReglasNegocio con Properties 07-07 application.yml, @ConfigurationProperties, perfiles 11-02
main que imprime y un humano que mira todas JUnit 5 + AssertJ + Mockito 11-04, 11-06
javac con classpath a mano 01-02 Maven 11-05
ApagadoOrdenado con shutdown hooks 08-05 Ciclo de vida del contenedor, @PreDestroy 11-02

Léela en las dos direcciones. De izquierda a derecha, es tu hoja de ruta. De derecha a izquierda, es la respuesta a la pregunta «¿qué está haciendo Spring aquí?».

  1. Mapa del ecosistema Java por categorías

El ecosistema Java es enorme y a un recién llegado le resulta abrumador. En realidad se ordena bien en ocho categorías, y en cada una hay dos o tres opciones dominantes.

Construcción y dependencias

Herramienta Qué es Cuándo Se ve en
Maven Estándar de facto. XML declarativo, ciclo de vida fijo Por defecto en empresa 11-05
Gradle DSL en Groovy o Kotlin, caché e incremental Proyectos grandes, Android 11-05 (comparado)
Ant Histórico, imperativo Solo legado

Inyección de dependencias y aplicaciones

Framework Qué es Fuerte en Se ve en
Spring / Spring Boot El ecosistema dominante Todo; comunidad enorme 11-02
Quarkus Nativo en la nube, arranque en milisegundos Contenedores, serverless mencionado
Micronaut DI resuelta en compilación Arranque y memoria mínimos mencionado
Jakarta EE El estándar (CDI, JAX-RS, JPA) Servidores de aplicaciones 11-03 (JPA)

Persistencia

Librería Enfoque Cuándo Se ve en
JPA / Hibernate ORM: objetos ↔ tablas Dominio rico, CRUD 11-03
Spring Data JPA Repositorios por convención sobre JPA Con Spring 11-03
jOOQ SQL con tipos verificados en compilación SQL complejo y control total mencionado
MyBatis SQL en XML o anotaciones, mapeo explícito Consultas a medida mencionado
JDBC / JdbcTemplate Directo, sin ORM Consultas puntuales, rendimiento 11-03 (línea base)

Pruebas

Librería Para qué Se ve en
JUnit 5 Marco de pruebas 11-04
Mockito 5 Dobles de prueba 11-06
AssertJ Aserciones fluidas y legibles 11-04, 11-06
Testcontainers Base de datos y servicios reales en Docker 11-06, 12-05
WireMock Simular APIs HTTP mencionado
JaCoCo Cobertura 12-05

Serialización

Librería Notas Se ve en
Jackson 2.x El estándar; integrado en Spring 11-07
Gson Google; más simple, menos potente 11-07 (mencionado)
JSON-B Estándar de Jakarta 11-07 (mencionado)

Logging

Pieza Papel Se ve en
SLF4J Fachada: la API contra la que programas 11-07
Logback Implementación por defecto de Spring Boot 11-07
Log4j2 Implementación alternativa, muy rápida 11-07 (y §14)
java.util.logging El del JDK; sin dependencias, limitado 06-07

Utilidades

Librería Qué aporta Se ve en
Lombok Genera código repetitivo en compilación 11-07
Guava Colecciones, caché, utilidades (Google) 11-07 (panorama)
Apache Commons Lang / IO Utilidades de String, ficheros, reflexión 11-07 (panorama)
MapStruct Mapeadores entidad ↔ DTO en compilación 11-07 (panorama)
Caffeine Caché en memoria de alto rendimiento 11-07 (panorama)

Documentación y operación

Librería Qué aporta Se ve en
springdoc-openapi Documentación OpenAPI/Swagger automática 11-07, 12-04
Micrometer Métricas 11-07, 12-07
Resilience4j Reintentos, cortocircuitos, limitadores 11-07, 12-07
Flyway / Liquibase Migraciones de esquema versionadas 11-03, 12-06

Con este mapa delante, el ecosistema deja de ser una lista infinita de nombres y pasa a ser ocho decisiones, cada una con una opción por defecto razonable.

  1. Cómo se elige una dependencia

Añadir una línea al pom.xml cuesta cinco segundos y compromete a tu equipo durante años. Estos son los criterios que se usan de verdad.

Criterio Qué mirar Señal de alarma
Mantenimiento activo Fecha de la última versión, incidencias abiertas y cerradas, ritmo de publicación Sin versiones desde hace >2 años; incidencias sin respuesta
Licencia Apache 2.0, MIT, BSD son seguras. GPL/AGPL contaminan. Revísalo siempre GPL en producto comercial cerrado; licencia ausente
Adopción Número de usos en Maven Central, estrellas, preguntas resueltas en foros Diez usuarios en el mundo; nadie ha tenido tu problema
Tamaño y transitividad ¿Cuántos jars arrastra? mvn dependency:tree Una utilidad de 20 KB que arrastra 30 MB
Alternativa en el JDK ¿Lo hace ya java.util, java.time, java.net.http? Sí, y aun así añades la dependencia
Compatibilidad Versión mínima de Java, jakarta.* frente a javax.* Requiere Java 8 y no ha migrado a jakarta
Seguridad ¿Tiene CVE abiertos? ¿Publica avisos? ¿Con qué rapidez parchea? Vulnerabilidades conocidas sin corregir
Documentación ¿Hay guía de inicio, javadoc, ejemplos? Solo un README de tres líneas
Coste de salida Si mañana desaparece, ¿cuánto cuesta sustituirla? Impregna todo el código

Una lista de comprobación práctica antes de escribir la dependencia:

[ ] ¿Lo necesito de verdad? ¿No lo hace ya el JDK o algo que ya tengo?
[ ] ¿La licencia es compatible con mi producto?
[ ] ¿Ha publicado versión en los últimos 12 meses?
[ ] ¿Tiene CVE abiertos sin corregir?
[ ] ¿Cuántas dependencias transitivas arrastra?
[ ] ¿Es compatible con mi versión de Java y con jakarta.*?
[ ] ¿Hay una alternativa más pequeña o más estándar?
[ ] ¿Sé cómo la quitaría si hiciera falta?

Si dudas en más de dos casillas, no la añadas todavía.

  1. La pregunta previa: ¿lo necesito de verdad?

Antes de todos los criterios anteriores hay una pregunta que se salta demasiada gente.

El JDK moderno es mucho más rico de lo que la gente recuerda. Muchísimas dependencias históricas ya no hacen falta:

Se solía añadir Hoy ya está en el JDK Desde
Joda-Time java.time Java 8
Apache HttpClient (para lo básico) java.net.http.HttpClient Java 11
Commons IO (para lo básico) java.nio.file.Files Java 7
Guava Lists.newArrayList List.of, new ArrayList<>() Java 9
Commons Lang StringUtils.isBlank String.isBlank() Java 11
Una librería para leer un fichero entero Files.readString(path) Java 11
record-like generado por Lombok record Java 16
Base64 de Commons Codec java.util.Base64 Java 8

Y el contrapeso: tampoco escribas tú lo que es notoriamente difícil de hacer bien. Criptografía, parseo de fechas con zonas horarias, parseo de JSON, parseo de CSV con comillas y saltos de línea embebidos (recuerda tu LectorCsv de 07-07 y todos los casos límite que no cubría), y cualquier cosa relacionada con seguridad. Ahí la dependencia madura es siempre mejor que tu implementación.

La regla práctica: escribe tú lo que es específico de tu negocio; delega lo que es genérico y difícil.

  1. Maven Central y las coordenadas

Cuando añades una dependencia, ¿de dónde sale el jar?

Maven Central es el repositorio público de artefactos Java, mantenido por Sonatype. Contiene millones de versiones de cientos de miles de librerías, y es el destino por defecto de Maven y Gradle. Cuando escribes una dependencia y ejecutas mvn package, Maven la descarga de Central y la guarda en tu repositorio local (~/.m2/repository) para no volver a bajarla.

Cada artefacto se identifica con tres coordenadas:

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.17.1</version>
</dependency>
Coordenada Qué es Convención
groupId La organización o proyecto Dominio invertido: org.springframework.boot
artifactId El módulo concreto Nombre corto: spring-boot-starter-web
version La versión publicada Versionado semántico: 3.3.2

Se escriben abreviadas como groupId:artifactId:version, por ejemplo com.fasterxml.jackson.core:jackson-databind:2.17.1. Verás esa notación en las trazas de Maven, en los informes de seguridad y en toda la documentación.

Y la ruta física es predecible: groupId con los puntos convertidos en carpetas, luego artifactId, luego version. El jar anterior vive en:

~/.m2/repository/com/fasterxml/jackson/core/jackson-databind/2.17.1/jackson-databind-2.17.1.jar

Saber esto es útil de verdad el día que Maven te diga que no encuentra un artefacto: puedes ir a esa carpeta y ver si está, si está corrupto, o si solo hay un fichero .lastUpdated (síntoma clásico de descarga fallida que se arregla borrando la carpeta y ejecutando mvn -U).

Todo esto se desarrolla en 11-05. Aquí solo necesitas el vocabulario.

  1. Versionado semántico

La mayoría del ecosistema Java sigue el versionado semántico (SemVer), con el formato MAYOR.MENOR.PARCHE:

Parte Se incrementa cuando Compatibilidad Ejemplo
MAYOR Hay cambios que rompen Rompe 2.7.183.0.0
MENOR Se añade funcionalidad compatible Compatible 3.2.03.3.0
PARCHE Se corrigen errores Compatible 3.3.13.3.2

Los sufijos habituales, de menos a más estable:

Sufijo Significado
-SNAPSHOT En desarrollo, cambia sin avisar. Nunca en producción
-M1, -M2 Hito (milestone), muy preliminar
-RC1 Candidata a versión, casi definitiva
(sin sufijo) Versión liberada (GA), inmutable para siempre

Dos consecuencias prácticas:

  1. Subir de PARCHE o MENOR debería ser seguro, y es lo que haces continuamente para incorporar correcciones de seguridad.
  2. Subir de MAYOR requiere leer las notas de migración. El ejemplo canónico y muy actual: Spring Boot 2.x → 3.x cambió todos los paquetes javax.* por jakarta.* y exige Java 17. Es el motivo de que en este curso todo lo de persistencia sea jakarta.persistence y no javax.persistence. Si copias un ejemplo de internet con javax.persistence, es de Spring Boot 2 y no te va a compilar.

  1. El coste de la seguridad de la cadena de suministro

Aquí viene la parte incómoda.

Cuando añades una dependencia, no añades un jar: añades un árbol. Un spring-boot-starter-web trae del orden de treinta artefactos transitivos. Un proyecto empresarial típico tiene entre 100 y 400 jars de los cuales tú has escrito cero.

Cada uno de esos jars ejecuta código con los mismos permisos que tu aplicación. Puede leer tus ficheros, abrir conexiones de red y acceder a tus variables de entorno. Y ninguno lo has revisado.

Esto se llama riesgo de la cadena de suministro de software (software supply chain), y tiene tres formas:

Riesgo En qué consiste Ejemplo
Vulnerabilidad conocida Un fallo publicado (CVE) en una versión que tú usas Log4Shell, Spring4Shell
Dependencia abandonada Nadie parchea; la vulnerabilidad no se corregirá nunca Librerías sin versión desde 2018
Compromiso del artefacto Alguien publica código malicioso en una librería legítima Casos documentados en npm y PyPI

  1. Log4Shell: qué pasó de verdad

En diciembre de 2021 se publicó CVE-2021-44228, apodada Log4Shell, en Apache Log4j 2, una de las librerías de logging más usadas del mundo Java.

El resumen técnico, sin dramatismo: Log4j 2 admitía en los mensajes de log una sustitución de variables, y una de las formas admitidas permitía hacer una búsqueda JNDI contra un servidor remoto. Consecuencia: si una aplicación registraba en el log un texto controlado por el atacante —una cabecera HTTP, un nombre de usuario, un User-Agent— el atacante podía provocar que el servidor descargara y ejecutara código suyo. Ejecución remota de código con una línea de texto.

Por qué fue tan grave:

  • Trivial de explotar: bastaba con enviar una cadena en un campo cualquiera que acabara en un log.
  • Omnipresente: Log4j 2 estaba en decenas de miles de productos, muchas veces como dependencia transitiva que el equipo ni sabía que tenía.
  • Difícil de inventariar: la pregunta «¿tenemos Log4j?» resultó no ser fácil de responder en la mayoría de las organizaciones. Y esa fue la lección real.

Lo que la industria aprendió, y que a ti te afecta directamente:

  1. Tienes que saber qué tienes. De ahí el auge del SBOM (Software Bill of Materials), el inventario de componentes de una aplicación.
  2. Tienes que poder actualizar rápido. Si actualizar una dependencia requiere tres semanas de pruebas manuales porque no hay pruebas automáticas, tu ventana de exposición son tres semanas. Esto conecta directamente con el módulo 11 al completo: sin Maven no puedes cambiar una versión con una línea, y sin JUnit no puedes verificar que el cambio no rompió nada.
  3. Tienes que vigilar continuamente. Un proyecto seguro hoy tiene vulnerabilidades dentro de seis meses sin cambiar una línea de código, porque las vulnerabilidades se descubren en código que ya existía.

Aviso importante. Lo que se cuenta aquí es lo que un desarrollador debe saber para no tomar decisiones ingenuas. En un proyecto real, la política de dependencias, el análisis de vulnerabilidades y la respuesta ante incidentes son responsabilidad del equipo o el responsable de seguridad de la organización, con herramientas y procesos propios. Tu papel es mantener el árbol de dependencias bajo control, actualizar cuando te lo indiquen y no introducir dependencias sin criterio. Las prácticas de seguridad de la aplicación se tratan en 12-07.

  1. Auditar dependencias en la práctica

Tres herramientas que usarás desde 11-05.

1. Ver el árbol completo. Esta es la primera y la que más se usa:

mvn dependency:tree

Salida (recortada) de un proyecto Spring Boot con 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:compile

Ahí ves de golpe tres cosas que este módulo va a explicar: que Spring Boot ya trae Logback y SLF4J (11-07), que ya trae Jackson (11-07) y que ya trae Tomcat embebido (12-04). Y ves que un jar que tú no declaraste, logback-core, está en tu aplicación.

Para buscar quién arrastra un artefacto concreto:

mvn dependency:tree -Dincludes=com.fasterxml.jackson.core:jackson-databind

2. Buscar vulnerabilidades conocidas. El plugin de OWASP contrasta tu árbol con la base de datos pública de vulnerabilidades:

mvn org.owasp:dependency-check-maven:check

Genera un informe HTML en target/ con los CVE encontrados y su gravedad. En un proyecto real esto se ejecuta en la integración continua, no a mano.

3. Ver qué está desactualizado.

mvn versions:display-dependency-updates

Te lista, dependencia a dependencia, qué versión usas y cuál es la última disponible. Ejecutarlo cada pocas semanas y actualizar los parches es una de las prácticas de higiene más rentables que existen.

  1. Qué framework aprender primero

Si has llegado hasta aquí, la lista de opciones puede paralizarte. El criterio para elegir el primero no es técnico, es de aprendizaje:

  1. Aprende el que más se use. No porque sea el mejor, sino porque cuando te atasques —y te vas a atascar— habrá alguien que ya se atascó igual y lo escribió. La documentación, los cursos y las respuestas son un recurso real.
  2. Aprende uno bien antes que tres a medias. Los conceptos —IoC, DI, ORM, AOP, contexto de persistencia— se transfieren. La sintaxis, no. Quien domina Spring aprende Quarkus en dos semanas; quien ha tocado los tres por encima no domina ninguno.
  3. Aprende con un proyecto real, no con ejemplos aislados. Por eso este módulo va sobre BiblioTech, y por eso hay un módulo 12 entero para integrarlo todo.

  1. Por qué este curso elige Spring Boot

Con honestidad, tres razones y una advertencia.

  • Es lo que hay en el mercado. Una mayoría muy amplia de las ofertas de empleo de back-end Java mencionan Spring o Spring Boot. Es el conocimiento con mayor valor inmediato.
  • Cubre todas las categorías del mapa. Con Spring Boot tocas DI, web, persistencia, pruebas, configuración, seguridad y observabilidad con un modelo coherente. Aprendes ocho cosas en un ecosistema en vez de en ocho.
  • Su magia es exactamente la que ya entiendes. Anotaciones, reflexión, proxies dinámicos. Estás en la mejor posición posible para aprenderlo sin que sea magia.

La advertencia: Spring no es la respuesta a todo. Para un CLI pequeño es un cañón para matar moscas, y hasta 12-03 verás que una aplicación de consola bien estructurada no lo necesita. Para funciones serverless con arranque en frío, Quarkus o Micronaut son mejores. Y para un servicio que solo hace tres consultas SQL, jOOQ o JdbcTemplate pueden ser más adecuados que Hibernate. Elegir bien es parte del oficio; y para elegir hay que conocer.

  1. El plan del módulo 11

Este módulo tiene una estructura deliberada: cada lección salda una deuda del módulo 10 y presenta una herramienta de forma completa y autónoma. La integración en un proyecto real es el módulo 12.

graph TD
    L1["11-01<br/>Introducción<br/>a los 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/>las primeras pruebas"]
    L4 --> L5["11-05<br/>Maven<br/>construcción"]
    L5 --> L6["11-06<br/>Mockito<br/>pruebas avanzadas"]
    L6 --> L7["11-07<br/>Jackson, Lombok<br/>SLF4J y ecosistema"]
    L7 --> M12["Módulo 12<br/>Aplicación real"]
Lección Herramienta Deuda que salda
11-02 Spring Framework y Spring Boot El ContenedorSimple casero y la configuración por entorno
11-03 Hibernate / JPA El CSV como persistencia
11-04 JUnit 5 La ausencia total de pruebas
11-05 Maven El javac a mano y las dependencias
11-06 Mockito Probar lo que depende de red, disco y reloj
11-07 Jackson, Lombok, SLF4J El JSON con indexOf, el código repetitivo, java.util.logging

Al final del módulo, BiblioTech será un proyecto Maven con Spring, persistencia JPA sobre H2, pruebas con JUnit y Mockito, JSON con Jackson y logging con SLF4J. No será todavía una aplicación completa —eso es el módulo 12—, pero todas sus piezas serán las de verdad.

  1. Errores Comunes y Consejos

Error: creer que un framework te exime de entender lo que hay debajo. Es exactamente al revés. El framework automatiza; cuando la automatización falla, solo puede arreglarlo quien entiende el mecanismo. Si @Transactional no revierte, necesitas saber de proxies y de transacciones de base de datos.

Error: añadir dependencias sin evaluarlas. «Necesito ordenar una lista, voy a añadir Guava.» No. Mira primero el JDK, luego lo que ya tienes en el árbol, y solo después Maven Central.

Error: copiar ejemplos de internet sin mirar la versión. El síntoma inequívoco: javax.persistence en vez de jakarta.persistence, o WebSecurityConfigurerAdapter, que se eliminó. Son de Spring Boot 2 y no compilan en 3. Comprueba siempre la fecha y la versión del ejemplo.

Error: fijar versiones de dependencias que gestiona el BOM. Cuando uses spring-boot-starter-parent, no pongas <version> en las dependencias que él gestiona: le estás quitando el trabajo de garantizar que las versiones son compatibles entre sí. Se explica en 11-05.

Error: usar rangos de versiones o LATEST. Rompe la reproducibilidad: la misma etiqueta de git compila distinto según el día. Fija siempre versiones concretas.

Error: no actualizar nunca «porque funciona». Una aplicación que no se toca durante tres años acumula decenas de vulnerabilidades conocidas. Y cuando por fin hay que actualizar, el salto es tan grande que se convierte en un proyecto.

Consejo: distingue el núcleo de la fontanería. Mantén tu dominio (entidades, reglas de negocio) lo más limpio posible de anotaciones del framework. La lógica de las multas de BiblioTech debe poder probarse sin arrancar Spring. Este principio se profundiza en 12-02.

Consejo: aprende a leer un dependency:tree. Es la herramienta que más veces te va a sacar de un apuro: conflictos de versiones, jars duplicados, clases que aparecen dos veces, y la pregunta de seguridad «¿tengo esta librería?».

Consejo: cuando algo del framework te parezca magia, busca el mecanismo. Pregúntate: ¿es una anotación leída por reflexión? ¿Es un proxy? ¿Es código generado en compilación? Casi siempre es una de las tres, y entonces deja de ser magia.

Consejo: mira las dependencias de un starter antes de añadirlo. Un spring-boot-starter-web trae Tomcat, Jackson, validación y logging. Saberlo evita añadir Jackson por tu cuenta con otra versión y provocar un conflicto.

  1. Ejercicios

Ejercicio 1: clasificar y justificar

Para cada elemento de la lista, indica si es librería o framework, y justifica la respuesta con el criterio de "quién llama a quién". En los casos ambiguos, explica por qué lo son.

  1. java.util.Collections
  2. JUnit 5
  3. Jackson ObjectMapper
  4. Hibernate
  5. SLF4J
  6. Spring Boot
  7. El ContenedorSimple que escribiste en 10-03
  8. El ProxyAuditoria que escribiste en 10-03

Ejercicio 2: evaluar una dependencia real

Nexus Software necesita generar códigos de barras para las etiquetas de los libros de BiblioTech. Un compañero propone añadir una librería que encontró. Escribe el análisis que harías antes de aceptarla, usando la lista de comprobación de la lección, y decide. Datos disponibles:

  • groupId:artifactIdcom.ejemplo:barcode-magic
  • Última versión: 0.4.1, publicada hace 3 años
  • Licencia: no consta en el POM
  • Usos en Maven Central: 7 proyectos
  • Dependencias transitivas: 14 jars, incluidos un motor gráfico completo y una librería XML
  • Documentación: un README de 8 líneas
  • Un CVE de gravedad media abierto desde hace 14 meses

Ejercicio 3: el mapa inverso

Para cada uno de estos comportamientos "mágicos" que verás en las próximas lecciones, indica cuál de los cuatro mecanismos del apartado 6 lo hace posible y qué escribiste tú en el módulo 10 que se le parece.

  1. Escribes @Service sobre GestorPrestamos y Spring lo instancia solo.
  2. Escribes @Transactional y la base de datos revierte al lanzar una excepción.
  3. Escribes @Getter de Lombok y aparece un getTitulo() que tú no escribiste.
  4. Declaras interface PrestamoRepository extends JpaRepository<Prestamo, Long> sin implementarla y funciona.
  5. Escribes @Test y el método se ejecuta sin main.
  6. Escribes @JsonProperty("isbn_13") y Jackson usa ese nombre en el JSON.

Soluciones

Solución 1

Elemento Tipo Justificación
java.util.Collections Librería Tú llamas a Collections.sort(...) cuando quieres. Control totalmente tuyo.
JUnit 5 Framework No hay main. JUnit descubre tus @Test por reflexión y los invoca. Principio de Hollywood puro.
Jackson ObjectMapper Librería Tú llamas a writeValueAsString(...). Aunque lea anotaciones tuyas, el flujo lo controlas tú.
Hibernate Los dos Es librería cuando llamas a entityManager.find(...); es framework cuando la comprobación de cambios automática detecta que modificaste una entidad gestionada y emite un UPDATE sin que tú llames a nada. Ese segundo comportamiento es control invertido (se ve en 11-03).
SLF4J Librería (fachada) Tú llamas a log.info(...). Además es una fachada: la implementación se elige en tiempo de ejecución, pero el flujo sigue siendo tuyo.
Spring Boot Framework Arranca él, escanea tus clases, las instancia, las inyecta, y llama a tu CommandLineRunner o a tu controlador cuando llega una petición.
Tu ContenedorSimple Framework (en miniatura) Escanea @Componente, decide el orden de construcción e instancia tus clases. Tú no llamas a new: lo llama él.
Tu ProxyAuditoria Framework (en miniatura) Intercepta la llamada, ejecuta código antes y después, y decide cuándo invocar tu método. El flujo pasa por él.

La conclusión que importa: escribiste dos frameworks en miniatura sin llamarlos así.

Solución 2

Aplicando la lista de comprobación:

Casilla Resultado Comentario
¿Lo necesito de verdad? Generar un código de barras correcto (checksums EAN-13, quiet zones) es difícil de hacer bien a mano. Es un caso legítimo de dependencia.
¿Licencia compatible? NO Sin licencia declarada, por defecto no hay permiso de uso. Esto por sí solo bloquea la decisión.
¿Versión en los últimos 12 meses? NO 3 años sin publicar. Muerta a efectos prácticos.
¿CVE abiertos? NO Uno de gravedad media, sin corregir en 14 meses: confirma el abandono.
¿Transitividad razonable? NO 14 jars, con un motor gráfico y una librería XML, para generar una imagen de código de barras. Desproporcionado y superficie de ataque enorme.
¿Compatible con mi Java? Desconocido Versión 0.x: la API puede cambiar sin previo aviso, aunque el abandono lo hace irrelevante.
¿Alternativa mejor? ZXing (Apache 2.0, de Google, mantenida, muy usada) o Barcode4J son opciones maduras para lo mismo.
¿Coste de salida? Bajo Se usaría desde un único punto, tras una interfaz propia. Es lo único positivo.

Decisión: rechazar. Cuatro casillas críticas fallan, y la licencia sola bastaría. Contrapropuesta: usar ZXing, y hacerlo detrás de una interfaz propia (GeneradorEtiquetas), de modo que la librería concreta esté aislada en una sola clase y el coste de cambiarla en el futuro sea de una implementación. Y añadir el análisis de dependencias a la integración continua para que el próximo CVE se detecte solo.

Nota profesional: el punto de la licencia no se negocia por criterio técnico. En una empresa, un jar sin licencia declarada es un problema legal, y esa conversación la debe tener el responsable correspondiente, no el desarrollador por su cuenta.

Solución 3

Comportamiento Mecanismo Tu equivalente del módulo 10
1. @Service instancia sola Anotación + reflexión. Spring escanea el classpath, encuentra la clase anotada, lee su constructor con getDeclaredConstructors(), resuelve los parámetros y llama a newInstance El ContenedorSimple con @Componente, exactamente el mismo bucle (10-03)
2. @Transactional revierte Proxy dinámico. El objeto que hay en el contenedor no es tu clase: es un envoltorio que abre transacción, invoca tu método y confirma o revierte según haya excepción ProxyAuditoria y ProxyReintentos: el InvocationHandler que ejecuta código antes y después de metodo.invoke(...) (10-03)
3. @Getter de Lombok Generación de código en compilación (procesador de anotaciones). No hay reflexión ni proxy: el .class ya contiene el método. Puedes verlo con javap El procesador de anotaciones estudiado en 10-02
4. JpaRepository sin implementación Proxy dinámico sobre interfaz más análisis del nombre del método. Proxy.newProxyInstance genera una implementación al vuelo que traduce findByIsbn a una consulta Proxy.newProxyInstance sobre interfaces, tal cual lo escribiste (10-03)
5. @Test se ejecuta sin main Anotación + reflexión. JUnit Platform descubre las clases, filtra los métodos con isAnnotationPresent(Test.class) y los invoca con Method.invoke El ExportadorAnotado, que recorría miembros buscando una anotación y actuaba (10-03)
6. @JsonProperty("isbn_13") Anotación + reflexión. Jackson introspecciona la clase, lee la anotación del campo y usa su valor como clave del JSON @CampoCsv("nombre") leída por el ExportadorAnotado — es literalmente el mismo diseño (10-02, 10-03)

Si has podido completar esta tabla, el objetivo de la lección está cumplido: no queda magia, quedan cuatro mecanismos que ya conoces aplicados a escala industrial.

Conclusión

Los frameworks han dejado de ser una caja negra antes incluso de usar el primero.

Sabes distinguir librería de framework por la única pregunta que importa —quién llama a quién— y reconoces el principio de Hollywood cuando lo ves: un método @Test sin main, un @Service que se instancia solo, una entidad que se guarda sin que llames a save. Y entiendes la inversión de control no como una definición de manual sino como el cambio concreto que hiciste en GestorPrestamos: de crear sus propias dependencias con new a declararlas en el constructor y dejar que otro las resuelva — con las cuatro consecuencias que eso tuvo (se puede probar, se puede cambiar de implementación, se puede configurar por entorno y el reloj deja de ser el del sistema).

Sabes qué compras y qué pagas. Compras código no diferenciador ya resuelto, convenciones que cualquier desarrollador entiende, un ecosistema integrado y —lo más subestimado— parches de seguridad mantenidos por otros. Pagas curva de aprendizaje, acoplamiento arquitectónico, comportamiento implícito difícil de depurar y peso. Y sabes que la respuesta correcta a «¿framework sí o no?» siempre empieza por «depende del contexto».

Y sobre todo, sabes cómo se sostiene la magia: anotaciones con @Retention(RUNTIME) que son etiquetas inertes; reflexión que las lee y actúa; proxies dinámicos que envuelven tus objetos para añadir comportamiento sin tocar tu código —con el detalle, que ya te encontrarás, de que la llamada interna no pasa por el proxy y de que CGLIB no puede con lo que es final—; y generación de código, en compilación con procesadores de anotaciones o en ejecución con bytecode. Cuatro mecanismos. Los cuatro los has escrito o estudiado. Y tienes el mapa completo de lo que hiciste a mano → lo que hace el framework de verdad, que es a la vez tu hoja de ruta del módulo y tu manual de diagnóstico cuando algo falle.

Tienes el mapa del ecosistema ordenado en ocho categorías con una opción por defecto en cada una, de modo que la lista infinita de nombres se convierte en ocho decisiones. Y tienes criterio para la decisión que tomarás decenas de veces: añadir o no una dependencia. Con la pregunta previa por delante —¿no lo hace ya el JDK?, porque java.time, HttpClient, Files.readString, String.isBlank y los record han dejado obsoletas muchas dependencias clásicas— y con los criterios reales detrás: mantenimiento, licencia, adopción, transitividad, seguridad y coste de salida.

Conoces Maven Central, las coordenadas groupId:artifactId:version, la ruta predecible dentro de ~/.m2 y el versionado semántico con su consecuencia más práctica hoy: el salto de Spring Boot 2 a 3 cambió javax.* por jakarta.*, y por eso un ejemplo copiado de internet con javax.persistence sencillamente no te va a compilar.

Y entiendes el riesgo de la cadena de suministro: que un proyecto medio tiene cientos de jars que nadie ha revisado ejecutándose con tus permisos, que Log4Shell demostró que la pregunta difícil no era parchear sino saber qué tienes, y que la capacidad de actualizar rápido depende exactamente de las dos herramientas de este módulo —Maven para cambiar una versión con una línea y JUnit para verificar que no rompiste nada—. Con la advertencia clara de que, en un proyecto real, la política de dependencias y la respuesta ante vulnerabilidades corresponden al responsable de seguridad, y de que las prácticas de seguridad de la aplicación llegan en 12-07.


El plan está fijado y cada lección salda una deuda. La primera es la más grande: ese ContenedorSimple de ciento cincuenta líneas que escribiste para no llamar a new, y al que le falta absolutamente todo lo demás — ámbitos, ciclo de vida, perfiles, propiedades externalizadas, resolución de ambigüedad y transacciones.

En la próxima lección, BiblioTech tira ese contenedor a la basura y adopta el que llevan veinte años puliendo diez mil desarrolladores. Verás el ApplicationContext, las tres formas de inyección y por qué solo una es recomendable, los estereotipos, el ciclo de vida de un bean, los ámbitos, los perfiles dev y prod, las propiedades tipadas que sustituyen a tu Configuracion de 07-07, y la autoconfiguración de Spring Boot explicada de verdad — no como magia, sino como un conjunto de condiciones que puedes leer y depurar.

Y cuando llegues a @Transactional, no verás una anotación misteriosa: verás tu propio ProxyAuditoria, con veinte años de pulido encima.

Empezamos por Spring.

Curso de Programación en Java

Módulo 1: Introducción a Java

Módulo 2: Flujo de Control

Módulo 3: Programación Orientada a Objetos

Módulo 4: Programación Orientada a Objetos Avanzada

Módulo 5: Estructuras de Datos y Colecciones

Módulo 6: Manejo de Excepciones

Módulo 7: Entrada/Salida de Archivos

Módulo 8: Multihilo y Concurrencia

Módulo 9: Redes

Módulo 10: Temas Avanzados

Módulo 11: Frameworks y Librerías de Java

Módulo 12: Construcción de Aplicaciones del Mundo Real

© Copyright 2026. Todos los derechos reservados