{
 "subtemas": [
  "Núcleo IoC",
  "Configuración",
  "Boot y starters",
  "Web MVC y REST",
  "Spring Data JPA",
  "Transacciones",
  "AOP",
  "Seguridad",
  "Testing",
  "Producción y observabilidad",
  "Async, caché y scheduling",
  "Spring Cloud y resiliencia",
  "Troubleshooting clásico"
 ],
 "sub_old": {
  "S01": "Boot y starters",
  "S02": "Boot y starters",
  "S03": "Núcleo IoC",
  "S04": "Núcleo IoC",
  "S05": "Núcleo IoC",
  "S06": "Núcleo IoC",
  "S07": "Núcleo IoC",
  "S08": "Núcleo IoC",
  "S09": "Web MVC y REST",
  "S10": "Web MVC y REST",
  "S11": "Web MVC y REST",
  "S12": "Spring Data JPA",
  "S13": "Spring Data JPA",
  "S14": "Transacciones",
  "S15": "Transacciones",
  "S16": "Spring Data JPA",
  "S17": "Web MVC y REST",
  "S18": "Configuración",
  "S19": "Producción y observabilidad",
  "S20": "Spring Cloud y resiliencia"
 },
 "tarjetas": [
  {
   "sub": "Núcleo IoC",
   "q": "BeanFactory vs ApplicationContext",
   "a": "BeanFactory: contenedor básico, crea beans perezosamente (al pedirlos).\nApplicationContext: la superinterfaz que usan las apps — añade eventos, internacionalización, recursos, y beans singleton se crean AL ARRANCAR (eager).\nEn Spring Boot obtienes un ApplicationContext automáticamente.",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Núcleo IoC",
   "q": "@Primary y @Qualifier: resoluciones de ambigüedad",
   "a": "Cuando 2+ beans implementan la misma interfaz, la inyección por tipo es ambigua → NoUniqueBeanDefinitionException.\n@Primary: el 'por defecto'.\n@Qualifier(\"nombre\"): elige explícito en el punto de inyección.\nAlternativa: inyectar Map<String, MiServicio> (todos por nombre) o List<MiServicio> (todos en orden @Order).",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Núcleo IoC",
   "q": "@Lazy: qué hace y sus dos usos clave",
   "a": "1) Diferir la creación del bean hasta su primer uso (bean caro o poco usado).\n2) ROMPER dependencias circulares: inyectando un proxy perezoso, ambos beans se crean sin bloquearse.\nTrampa: los errores de configuración aparecen tarde (en runtime, no al arrancar).",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Núcleo IoC",
   "q": "¿Cómo resuelve Spring las dependencias circulares?",
   "a": "3 niveles de caché (singletonFactories, earlySingletonReferences, singletons): expone una referencia TEMPRANA del bean mientras se crea el otro.\nSolo funciona con inyección por SETTER/campo y scopes singleton; con inyección por CONSTRUCTOR falla al arrancar (y está bien: es un smell).\nFix moderno: @Lazy en una dependencia o rediseñar (extraer el código común).",
   "nivel": "avanzado",
   "clave": false
  },
  {
   "sub": "Núcleo IoC",
   "q": "BeanPostProcessor vs BeanFactoryPostProcessor",
   "a": "BFPP: opera sobre DEFINICIONES de beans ANTES de instanciarlos (modificar propiedades, registrar más beans).\nBPP: envuelve cada bean DESPUÉS de instanciar (postProcessBeforeInitialization/After) — aquí nacen los proxies de @Transactional, @Async, @Validated, @Lazy.\nEs la extensibilidad del contenedor.",
   "nivel": "avanzado",
   "clave": false
  },
  {
   "sub": "Núcleo IoC",
   "q": "FactoryBean: qué es y cuándo aparece",
   "a": "Un bean que FABRICA otro tipo de objeto: getObject() devuelve la instancia real, getObjectType su clase, isSingleton su alcance.\nUso: creación compleja de objetos de terceros (MyBatis SqlSessionFactoryBean, feign clients).\nReferencia con & prefijo obtiene la fábrica misma: context.getBean(\"&miFactoryBean\").",
   "nivel": "avanzado",
   "clave": false
  },
  {
   "sub": "Núcleo IoC",
   "q": "@Lookup y ObjectProvider: inyección de prototype en singleton",
   "a": "Problema: un singleton con dependencia prototype la recibe UNA vez.\n@Lookup: método abstracto que Spring sobrescribe para dar una instancia nueva por llamada.\nObjectProvider<T>: provider.getObject() por petición, getIfAvailable/getIfUnique — más moderno y testeable.",
   "nivel": "avanzado",
   "clave": false
  },
  {
   "sub": "Núcleo IoC",
   "q": "Interfaces Aware: qué inyectan",
   "a": "Callbacks para 'conciencia' del entorno: BeanNameAware (nombre del bean), ApplicationContextAware (el contexto), EnvironmentAware, ApplicationEventPublisherAware, BeanFactoryAware.\nSe llaman tras la inyección de dependencias, antes de @PostConstruct.\nHoy prefieres inyección directa o implementar solo lo necesario.",
   "nivel": "avanzado",
   "clave": false
  },
  {
   "sub": "Núcleo IoC",
   "q": "@DependsOn, @Order y @PostConstruct/@PreDestroy",
   "a": "@DependsOn(\"beanA\"): fuerza orden de creación sin dependencia de tipo (side effects, caches de arranque).\n@Order/@Ordered: orden relativo en colecciones inyectadas, interceptores y filtros.\n@PostConstruct: init tras inyección; @PreDestroy: cleanup al cerrar contexto (solo singletons; prototype no gestiona destrucción).",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Núcleo IoC",
   "q": "¿Cómo registras beans SIN anotaciones ni escaneo?",
   "a": "1) @Configuration + métodos @Bean.\n2) ImportSelector / ImportBeanDefinitionRegistrar (mecanismo de @Import — así funcionan @Enable*).\n3) BeanDefinitionRegistryPostProcessor programático.\n4) spring.factories / AutoConfiguration.imports para autoconfigs.\nEs lo que hacen las librerías para registrar sus beans.",
   "nivel": "avanzado",
   "clave": false
  },
  {
   "sub": "Núcleo IoC",
   "q": "¿Qué son las interfaces @Enable* ?",
   "a": "Interruptores que importan configuración: @EnableAsync, @EnableScheduling, @EnableCaching, @EnableTransactionManagement, @EnableFeignClients...\nInternamente usan @Import para registrar beans/proxies necesarios.\nEn Spring Boot muchas vienen activadas por autoconfiguración.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Núcleo IoC",
   "q": "Component scan: paquetes base y filtros",
   "a": "@SpringBootApplication escanea desde SU paquete hacia abajo — clase main mal ubicada = beans no encontrados (error #1 de principiantes).\nPersonalizar: @ComponentScan(basePackages, includeFilters, excludeFilters).\nBeans fuera del paquete base: registrarlos con @Import o @Bean.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Núcleo IoC",
   "q": "Bean con nombre: convenciones y referencia por nombre",
   "a": "Nombre default: primera letra minúscula del nombre de clase (userService) o del método @Bean.\nCustom: @Component(\"miNombre\") / @Bean(\"miNombre\").\nInyectar por nombre: @Qualifier o campo cuyo nombre coincida — cuando tipo y nombre coinciden no hace falta qualifier.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Configuración",
   "q": "@ConfigurationProperties vs @Value: cuál y por qué",
   "a": "@ConfigurationProperties(prefix=\"app\"): POJO con muchos valores agrupados, type-safe, IDE-friendly, validable con @Validated+JSR-303, relaxed binding (app.max-retry → maxRetry).\n@Value(\"${clave}\"): valores sueltos y rápidos, con SpEL, pero disperso y sin metadatos.\nRegla: grupos de config → @ConfigurationProperties; un dato puntual → @Value.",
   "nivel": "basico",
   "clave": true
  },
  {
   "sub": "Configuración",
   "q": "Relaxed binding: ¿app.max-connections mapea a qué campo?",
   "a": "A maxConnections. Formatos equivalentes: camelCase (maxConnections), kebab-case (max-connections, recomendado en YAML), snake_case (MAX_CONNECTIONS en env vars), UPPER_CASE env var APP_MAXCONNECTIONS.\nListas: app.servers[0].host=...\nEs el puente entre propiedades y variables de entorno de K8s/Cloud Run.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Configuración",
   "q": "Orden de precedencia de la configuración en Spring Boot",
   "a": "De mayor a menor: argumentos CLI (--server.port=9090) > atributos SPRING_APPLICATION_JSON > variables de entorno (SERVER_PORT) > application-{profile} > application.yml/properties (externo > interno al jar) > @PropertySource > defaults.\nDev/test sobrescriben sin recompilar.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Configuración",
   "q": "¿Cómo validar que la configuración externa sea correcta al arrancar?",
   "a": "@ConfigurationProperties + @Validated + anotaciones JSR-303 en los campos (@NotNull, @Min, @URL).\nConfiguración inválida → falla el ARRANQUE con mensaje claro, no un NPE a las 3 a.m.\nAlternativa: checks manuales en @PostConstruct o fail-fast con ApplicationContextRunner en tests.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Configuración",
   "q": "Placeholders, defaults y referencias entre propiedades",
   "a": "@Value(\"${app.timeout:30}\") — default tras los ':'.\nReferencias: app.url=http://${app.host}:${app.port}/api.\nVariables especiales: ${random.value}, ${random.int[1024,65536]} (puertos de test).\nSpEL en @Value: #{systemProperties['x']}.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Configuración",
   "q": "@Profile: activar beans por entorno",
   "a": "@Profile(\"prod\") / @Profile({\"dev\",\"test\"}) / @Profile(\"!prod\") en @Component o @Bean.\nActivar: spring.profiles.active=prod (propiedad, env var SPRING_PROFILES_ACTIVE, o CLI).\nCombinar con application-{profile}.yml para configuración por entorno.\nUn perfil default existe si no se activa ninguno.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Configuración",
   "q": "spring.config.import y multi-documento YAML",
   "a": "spring.config.import=optional:file:.env[.properties], classpath:secrets.yml — importa configs adicionales (secrets, docker).\nYAML multi-documento: '---' separa bloques, cada uno con spring.config.activate.on-profile — un solo archivo para todos los perfiles.\nOrden: el último gana.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Configuración",
   "q": "@PropertySource y conversiones de tipos custom",
   "a": "@PropertySource(\"classpath:app.properties\") añade fuentes al Environment (no soporta YAML directamente).\nConversiones: Spring usa ConversionService — String→enum, duraciones (\"30s\" → Duration), tamaños (\"10MB\" → DataSize) automáticamente en @ConfigurationProperties; registrar CustomConverter para tipos propios.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Boot y starters",
   "q": "¿Qué es un starter y cuáles usas siempre?",
   "a": "Dependencia 'todo en uno' que agrupa librerías compatibles: spring-boot-starter-web (MVC+Tomcat+Jackson+validación), starter-data-jpa, starter-security, starter-actuator, starter-test, starter-validation.\nEliminan versiones incompatibles: el BOM de Boot gobierna versiones transitivas.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Boot y starters",
   "q": "Servidor embebido: configuración y swap",
   "a": "Tomcat por defecto; starter-web-reactor trae Netty (WebFlux), y puedes cambiar a Jetty/Undertow excluyendo starter-tomcat.\nConfig: server.port, server.servlet.context-path, server.tomcat.threads.max, graceful shutdown (server.shutdown=graceful).\nJar ejecutable con java -jar — sin instalar servlet container.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Boot y starters",
   "q": "¿Qué hace @SpringBootApplication exactamente?",
   "a": "Agrupa 3: @SpringBootConfiguration (es una @Configuration), @EnableAutoConfiguration y @ComponentScan (paquete actual hacia abajo).\nAtributos útiles: exclude=DataSourceAutoConfiguration.class (quita autoconfigs), scanBasePackages (mover el escaneo).\nDebe vivir en el paquete raíz.",
   "nivel": "basico",
   "clave": true
  },
  {
   "sub": "Boot y starters",
   "q": "Anatomía de una autoconfiguración (condiciones)",
   "a": "@AutoConfiguration + @ConditionalOnClass(DataSource.class) — si la clase está en el classpath, activa.\n@ConditionalOnMissingBean: si TÚ ya definiste uno, respeta el tuyo.\n@ConditionalOnProperty(prefix=\"spring.datasource\", name=\"url\").\nListadas en META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Boot y starters",
   "q": "Cómo depurar la autoconfiguración (por qué se activó X)",
   "a": "--debug o debug=true → CONDITION EVALUATION REPORT al arrancar: cada autoconfig con positivo/negativo y POR QUÉ (conditional on class, missing bean, property).\nTambién: actuator/conditions endpoint.\nEs el primer paso ante 'no se configura mi DataSource/conector'.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Boot y starters",
   "q": "Clase main: SpringApplication y run() qué devuelve",
   "a": "ApplicationContext context = SpringApplication.run(App.class, args).\nObtener beans: context.getBean(...), publicar eventos, perfiles activos.\nCustomizar: new SpringApplicationBuilder(App.class).bannerMode(OFF).profiles(\"x\").run(args).\nCommandLineRunner/ApplicationRunner: código de arranque post-contexto (reciben args crudos/parsed).",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Boot y starters",
   "q": "Baner, nombres de app y metadata útil",
   "a": "spring.application.name: identificador en logs/tracing/eureka.\nbanner.txt custom o off.\nbuild-info: spring-boot-maven-plugin con build-info → /actuator/info muestra versión y timestamps.\nLos metadatos de propiedades (additional-spring-configuration-metadata.json) dan autocomplete y documentación en el IDE.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Boot y starters",
   "q": "Excluir autoconfiguración: las 3 formas",
   "a": "1) @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}).\n2) spring.autoconfigure.exclude=... (propiedad, soporta lista).\n3) Definir TÚ el bean: @ConditionalOnMissingBean cede (no es exclusión, pero anula el efecto).\nCaso típico: BD en memoria de Boot cuando la quieres manual.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Boot y starters",
   "q": "Propiedades comunes de arranque que conviene saber de memoria",
   "a": "server.port=0 (puerto aleatorio para tests), spring.profiles.active, spring.datasource.url/username/password, spring.jpa.hibernate.ddl-auto (none/validate/update/create), spring.jpa.show-sql, management.endpoints..., logging.level.paquete=DEBUG, spring.servlet.multipart.max-file-size.\nEn YAML con kebab-case.",
   "nivel": "basico",
   "clave": false
  }
 ]
}