Gestión de Prioridades en la Configuración
Spring Boot permite definir propiedades mediante tres formatos de archivo distintos: application.properties, application.yml y application.yaml. Es crucial mantener la ortografía correcta de los nombres de archivo para evitar errores de carga.
Cuando existe una colisión de propiedades definidas en múltiples archivos, el framework establece un orden de precedencia estricto: application.properties tiene la mayor prioridad, seguido de application.yml y finalmente application.yaml. Se recomienda estandarizar el uso de YAML en proyectos modernos.
Más allá de los archivos, el framework admite la inyección de propiedades mediante atributos del sistema Java y argumentos pasados directamente por línea de comandos.
// Atributo del sistema Java
-Dspring.profiles.active=production
// Argumento de línea de comandos
--server.port=8080
En entornos de desarrollo como IntelliJ IDEA, estas opciones se configuran marcando las casillas correspondientes para opciones de máquina virtual y parámetros del programa. La jerarquía de ejecución final es: Argumentos de línea de comandos > Atributos del sistema Java > Archivos de configuración locales.
Ciclo de Vida y Ámbito de los Beans
El contenedor de Spring gestiona los beans bajo diferentes ámbitos (scopes). Aunque existen cinco tipos, solo tres son relevantes en contextos web. Por defecto, los beans se registran como singleton.
Para alterar este comportamiento, se utiliza la anotación @Scope:
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
Es fundamental distinguir entre beans sin estado y beans con estado. Los primeros no mantienen datos internos y se benefician del patrón singleton para optimizar recursos. Los segundos, que almacenan estado, requieren un ámbito prototype para evitar conflictos.
En la práctica, la mayoría de los componentes son sin estado. Por defecto, la inicialización ocurre al arranqeu de la aplicación, pero el uso de @Lazy permite diferir la creación hasta el primer uso.
Seguridad en hilos: Un bean singleton sin estado es inherentemente seguro para hilos múltiples. Sin embargo, si un singleton mantiene estado mutable, el acceso concurrente puede provocar inconsistencias. Utilizar un ámbito prototype garantiza seguridad thread-safe al instanciar un nuevo objeto por solicitud.
Integración de Beans de Terceros
Cuando se requiere gestionar clases externas sobre las cuales no se tiene control del código fuente (librerías de terceros), no es posible aplicar anotaciones como @Component directamente. La solución consiste en definir métodos de fábrica dentro de la configuración de Spring.
Aunque es técnicamente viable declarar beans en la clase principal de arranque, la mejor práctica establece la creación de clases de configuración dedicadas.
@Configuration
public class ExternalLibConfig {
@Bean
public ObjectMapper jsonMapper() {
return new ObjectMapper();
}
}
Mecanismos Internos de Spring Boot
Dependencias de Arranque (Starters)
Los "Starter Dependencies" son módulos POM que agrupan dependencias transitivas necesarias para funcionalidades específicas. Esto abstrae la gestión compleja de versiones y librerías requeridas, facilitando la inicialización del proyecto.
Configuración Automática
El principio de auto-configuración permite que Spring registre beans y configuraciones predeterminadas sin intervención manual explícita. Anotaciones como @Autowired funcionan gracias a este mecanismo.
Un problema común surge con clases de terceros que no son detectadas por el escaneo de componentes estándar.
Escaneo de Componentes
La anotación @SpringBootApplication incluye implícitamente @ComponentScan. Por defecto, el escaneo se limita al paquete donde reside la clase principal y sus subpaquetes. Para cubrir estructuras más amplias, se puede configurar explícitamente el base package.
Importación de Clases
La anotación @Import permite registrar clases específicas directamente en el contexto de la aplicación.
Importación Directa
@Configuration
@Import({AuthService.class, RedisConfig.class})
public class CoreConfig {
// Configuración adicional
}
Selectores de Importación
Para lógica condicional, se implementa la interfaz ImportSelector. Esto permite decidir dinámicamente qué clases cargar basándose en metadatos de la anotación.
public class ModuleSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
// Lógica condicional para retornar nombres de clases
return new String[]{"com.example.ModuleX", "com.example.ModuleY"};
}
}
@Configuration
@Import(ModuleSelector.class)
public class DynamicConfig {
// ...
}
Este patrón es la base para crear anotaciones personalizadas tipo @Enable.
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(DynamicConfig.class)
public @interface EnableCustomModule {
String version() default "1.0";
}
@SpringBootApplication
@EnableCustomModule
public class SpringBootApp {
public static void main(String[] args) {
SpringApplication.run(SpringBootApp.class, args);
}
}