Las clases de configuración en Java reemplazan el enfoque antiguo con configuración XML. Así convierten la configuración de aplicaciones Spring en un proceso más legible y cómodo.
Una clase de configuración es una clase Java que se usa para describir la configuración de tu aplicación. En Spring, estas clases se marcan con la anotación @Configuration. Eso le indica al contenedor IoC que la clase contiene métodos que crean beans. Esos métodos se marcan con la anotación @Bean.
Puedes imaginar la clase de configuración como el menú de tu cafetería favorita. Con @Bean describes qué se va a servir (objetos), y el contenedor IoC hace de camarero, entregando todo lo necesario (inyección de dependencias) a tu mesa.
Ejemplo de una clase de configuración sencilla
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
@Bean
public String welcomeMessage() {
return "¡Bienvenido a Spring Framework!";
}
}
En nuestro ejemplo welcomeMessage es un bean que devuelve una cadena. La clase de configuración AppConfig contiene un método anotado con @Bean, y Spring sabe que ese método crea un bean gestionado por el contenedor IoC.
Pero no te preocupes, no nos vamos a quedar en un bean que solo te dice "hola". Vamos a darle caña y crear configuraciones más complejas.
Creación de beans y trabajo con dependencias
Las clases de configuración son útiles cuando creas objetos que tienen dependencias. Por ejemplo, si tenemos una clase Car que depende de un motor Engine, podemos configurarlo en la clase de configuración.
Ejemplo con dependencias
public class Engine {
private String type;
public Engine(String type) {
this.type = type;
}
public String getType() {
return type;
}
}
public class Car {
private Engine engine;
public Car(Engine engine) {
this.engine = engine;
}
public void drive() {
System.out.println("Car is driving with " + engine.getType() + " engine.");
}
}
Ahora vamos a crear una clase de configuración para estos objetos:
@Configuration
public class VehicleConfig {
@Bean
public Engine engine() {
return new Engine("V8");
}
@Bean
public Car car() {
return new Car(engine());
}
}
En el método car llamamos al método engine para pasarlo al constructor de la clase Car. Esto permite a Spring entender que el bean Car depende del bean Engine. Ahora el contenedor IoC se encargará de configurar correctamente nuestro objeto.
¿Cómo funciona esto?
- Spring crea el bean
Enginecon el tipoV8. - Luego se crea el bean
Car, y en su constructor se inyecta automáticamente el beanEngine.
Este proceso cómodo reduce la cantidad de código que tendríamos que escribir a mano. Convengamos, a nadie le haría mucha gracia crear todas las dependencias manualmente.
Declaración explícita de dependencias entre beans
Si por alguna razón quieres indicarle explícitamente al bean Car que necesita el bean Engine, puedes hacerlo con la anotación @Qualifier (spoiler: te hará falta en las siguientes lecciones).
Dependencias complejas: beans que dependen de varios otros beans
Imagina que creamos un servicio de reserva de vuelos. Necesitamos combinar varias dependencias, como un cliente para la base de datos y un servicio de pagos. Todo eso se puede configurar en la clase de configuración.
public class PaymentService {
public void pay() {
System.out.println("¡Pago procesado con éxito!");
}
}
public class BookingService {
private PaymentService paymentService;
public BookingService(PaymentService paymentService) {
this.paymentService = paymentService;
}
public void bookFlight() {
System.out.println("Reserva completada.");
paymentService.pay();
}
}
Configuremos todo esto con una clase de configuración:
@Configuration
public class BookingConfig {
@Bean
public PaymentService paymentService() {
return new PaymentService();
}
@Bean
public BookingService bookingService() {
return new BookingService(paymentService());
}
}
Ahora obtenemos:
- Encapsulación de la lógica de creación de objetos.
- Minimización del código en la lógica principal de la aplicación.
- Flexibilidad: los cambios en la configuración se adaptan con facilidad.
Evitando problemas por duplicación de beans
Uno de los errores más comunes es crear por accidente beans duplicados. Si intentas declarar dos beans con el mismo nombre, Spring lanzará un error: "Se han encontrado varios candidatos para la inyección de la dependencia". Para evitarlo, asegúrate de que tus métodos @Bean tengan nombres únicos y estén bien definidos.
Práctica: configurar un proyecto real
Hagamos un pequeño ejercicio relacionado con nuestra aplicación educativa. Vamos a crear un servicio para gestionar libros en una biblioteca, usando una clase de configuración.
Lógica de negocio
public class Book {
private String title;
private String author;
public Book(String title, String author) {
this.title = title;
this.author = author;
}
@Override
public String toString() {
return "Book: " + title + " by " + author;
}
}
public class LibraryService {
private Book book;
public LibraryService(Book book) {
this.book = book;
}
public void displayBookInfo() {
System.out.println(book.toString());
}
}
Clase de configuración
@Configuration
public class LibraryConfig {
@Bean
public Book defaultBook() {
return new Book("Clean Code", "Robert C. Martin");
}
@Bean
public LibraryService libraryService() {
return new LibraryService(defaultBook());
}
}
Uso en la aplicación principal
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Main {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(LibraryConfig.class);
LibraryService libraryService = context.getBean(LibraryService.class);
libraryService.displayBookInfo();
}
}
Salida final del programa:
Book: Clean Code by Robert C. Martin
¡Enhorabuena! Hemos visto cómo usar clases de configuración y crear Spring Beans. Ahora puedes construir objetos, enlazar sus dependencias y hacer que el contenedor IoC trabaje para ti.
GO TO FULL VERSION