Using The Configuration Annotation
Uso de la anotación @Configuration
@Configuration es una anotación de nivel de clase que indica que un objeto es una fuente de definiciones de beans. Las clases @Configuration declaran beans mediante métodos anotados con @Bean.
Las llamadas a métodos @Bean dentro de clases @Configuration también pueden utilizarse para definir dependencias entre beans.
Inyección de dependencias entre beans
Cuando los beans dependen unos de otros, expresar esa dependencia es tan sencillo como hacer que un método de bean llame a otro:
@Configuration
public class AppConfig {
@Bean
public BeanOne beanOne() {
return new BeanOne(beanTwo());
}
@Bean
public BeanTwo beanTwo() {
return new BeanTwo();
}
}
En el ejemplo anterior, beanOne recibe una referencia a beanTwo mediante inyección por constructor.
Este método de declarar dependencias entre beans solo funciona cuando el método @Bean se declara dentro de una clase @Configuration. No puedes declarar dependencias entre beans mediante clases @Component simples.
Inyección de métodos de búsqueda
Como se indicó anteriormente, la inyección de métodos de búsqueda es una característica avanzada que debe utilizarse en pocas ocasiones. Resulta útil cuando un bean con ámbito singleton depende de un bean con ámbito prototype.
El uso de Java para este tipo de configuración proporciona una forma natural de implementar este patrón. El siguiente ejemplo muestra cómo usar la inyección de métodos de búsqueda:
public abstract class CommandManager {
public Object process(Object commandState) {
// Obtiene una nueva instancia de la interfaz Command adecuada.
Command command = createCommand();
// Establece el estado en la instancia de Command, idealmente nueva.
command.setState(commandState);
return command.execute();
}
// De acuerdo..., ¿pero dónde está la implementación de este método?
protected abstract Command createCommand();
}
Mediante configuración Java, puedes crear una subclase de CommandManager en la que el método abstracto createCommand() se sobrescriba de forma que busque un nuevo objeto Command con ámbito prototype:
@Bean
@Scope("prototype")
public AsyncCommand asyncCommand() {
AsyncCommand command = new AsyncCommand();
// Inyecta aquí las dependencias necesarias.
return command;
}
@Bean
public CommandManager commandManager() {
// Devuelve una implementación anónima de CommandManager con createCommand()
// sobrescrito para devolver un nuevo objeto Command con ámbito prototype.
return new CommandManager() {
@Override
protected Command createCommand() {
return asyncCommand();
}
};
}
Más información sobre el funcionamiento interno de la configuración Java
Considera el siguiente ejemplo, que muestra un método anotado con @Bean llamado dos veces:
@Configuration
public class AppConfig {
@Bean
public ClientService clientService1() {
ClientServiceImpl clientService = new ClientServiceImpl();
clientService.setClientDao(clientDao());
return clientService;
}
@Bean
public ClientService clientService2() {
ClientServiceImpl clientService = new ClientServiceImpl();
clientService.setClientDao(clientDao());
return clientService;
}
@Bean
public ClientDao clientDao() {
return new ClientDaoImpl();
}
}
clientDao() se invoca una vez desde clientService1() y otra desde clientService2(). Como este método crea y devuelve una instancia nueva de ClientDaoImpl, normalmente se esperaría que existieran dos instancias, una para cada servicio.
Esto sería problemático, porque los beans de Spring tienen ámbito singleton de forma predeterminada.
Aquí interviene el mecanismo interno de Spring: al iniciar la aplicación, todas las clases @Configuration se convierten en subclases mediante CGLIB.
En esa subclase, el método sobrescrito consulta primero el contenedor para comprobar si existe un bean almacenado en caché —o con ámbito— antes de llamar al método original y crear una nueva instancia.
El comportamiento puede ser diferente según el ámbito del bean. En este caso, se trata de beans singleton.
No es necesario añadir CGLIB al classpath, ya que sus clases se han reempaquetado bajo el paquete org.springframework.cglib y se incluyen directamente en el JAR spring-core.
Existen algunas restricciones debido a que CGLIB añade funcionalidades dinámicamente durante el inicio. En particular, las clases de configuración no deben ser final.
Sin embargo, las clases @Configuration pueden declarar cualquier constructor, incluido uno anotado con @Autowired o un único constructor que no sea el predeterminado para la inyección predeterminada.
Si prefieres evitar las limitaciones impuestas por CGLIB, puedes declarar los métodos @Bean en clases que no estén anotadas con @Configuration, por ejemplo, en clases @Component simples, o anotar la clase de configuración con @Configuration(proxyBeanMethods = false).
En esos casos, las llamadas entre métodos @Bean no se interceptan. Por tanto, debes depender exclusivamente de la inyección de dependencias a nivel de constructor o método.