Bean Overview
Bean Overview
Un contenedor Spring IoC gestiona uno o más beans. Estos beans se crean con los metadatos de configuración que se suministran al contenedor (por ejemplo, en forma de definiciones XML
Dentro del propio contenedor, estas definiciones de bean se representan como objetos BeanDefinition, que contienen (entre otra información) los siguientes metadatos:
- Un nombre de clase calificado por el paquete: normalmente, la clase de implementación real del bean que se está definiendo.
- Elementos de configuración del comportamiento del bean, que establecen cómo debe comportarse el bean en el contenedor (ámbito, devoluciones de llamada del ciclo de vida, etc.).
- Referencias a otros beans que son necesarios para que el bean realice su trabajo. Estas referencias también se denominan colaboradores o dependencias.
- Otros ajustes de configuración a establecer en el objeto recién creado - por ejemplo, el límite de tamaño del pool o el número de conexiones a utilizar en un bean que gestiona un pool de conexiones.
Estos metadatos se traducen en un conjunto de propiedades que conforman la definición de cada bean. La siguiente tabla describe estas propiedades:
| Property | Explained in… |
|---|---|
| Class | Instantiating Beans |
| Name | Naming Beans |
| Scope | Bean Scopes |
| Constructor arguments | Dependency Injection |
| Properties | Dependency Injection |
| Autowiring mode | Autowiring Collaborators |
| Lazy initialization mode | Lazy-initialized Beans |
| Initialization method | Initialization Callbacks |
| Destruction method | Destruction Callbacks |
Además de las definiciones de bean que contienen información sobre cómo crear un bean específico, las implementaciones de ApplicationContext también permiten el registro de objetos existentes que se crean fuera del contenedor (por los usuarios).
Esto se hace accediendo al BeanFactory del ApplicationContext a través del método getBeanFactory(), que devuelve la implementación de DefaultListableBeanFactory. DefaultListableBeanFactory soporta este registro a través de los métodos registerSingleton(..) y registerBeanDefinition(..). Sin embargo, las aplicaciones típicas trabajan únicamente con beans definidos a través de metadatos de definición de beans normales.
ℹ️ Los metadatos de los beans y las instancias singleton suministradas manualmente deben registrarse lo antes posible, para que el contenedor pueda razonar adecuadamente sobre ellos durante el
autowiringy otros pasos de introspección. Mientras que la sobreescritura de metadatos existentes y de instancias singleton existentes está soportada hasta cierto punto, el registro de nuevos beans en tiempo de ejecución (concurrentemente con el acceso en vivo a la factory) no está oficialmente soportado y puede conducir a excepciones de acceso concurrente, estado inconsistente en el contenedor de beans, o ambos.
Overriding Beans
La sobreescritura de beans se produce cuando se registra un bean utilizando un identificador que ya está asignado. Aunque la sobreescritura de beans es posible, hace que la configuración sea más difícil de leer.
❗ En una futura versión la sobreescritura de beans estará deprecada
Para deshabilitar completamente la sobreescritura de beans, puedes establecer el flag allowBeanDefinitionOverriding a false en el ApplicationContext antes de que se actualice. En este caso, se lanzará una excepción si se utiliza la sobreescritura de bean.
Por defecto, el contenedor muestra un log con cada intento de sobreescribir un bean con el nivel INFO para que puedas adaptar tu configuración en consecuencia. Aunque no es recomendable, puedes silenciar esos registros estableciendo el parámetro allowBeanDefinitionOverriding a true.
Java Configuration
Si utilizas la Configuración Java, un método @Bean correspondiente siempre sobrescribe silenciosamente una clase bean escaneada que tenga el mismo nombre de componente, siempre que el tipo de retorno del método @Bean coincida con esa clase bean. Esto simplemente significa que el contenedor llamará al método factory @Bean en lugar de cualquier constructor predeclarado en la clase bean.
Reconocemos que sobreescribir beans en escenarios de prueba es conveniente, y existe soporte explícito para ello a partir de Spring Framework 6.2. Please refer to this section for more details.
Naming Beans
Cada bean tiene uno o más identificadores. Estos identificadores deben ser únicos dentro del contenedor que aloja el bean. Normalmente, un bean sólo tiene un identificador. Sin embargo, si necesita más de uno, los adicionales pueden considerarse alias.
En los metadatos de configuración basados en XML, se utiliza el atributo id, el atributo name o ambos para especificar identificadores de beans. El atributo id permite especificar exactamente un id. Por convención, estos nombres son alfanuméricos (‘myBean’, ‘someService’, etc.), pero también pueden contener caracteres especiales. Si desea introducir otros alias para el bean, también puede especificarlos en el atributo name, separados por una coma (,), un punto y coma (;) o un espacio en blanco. Aunque el atributo id está definido como un tipo xsd:string, la unicidad del id del bean esta impuesta por el contenedor, aunque no por los analizadores XML(XML parsers).
No es necesario proporcionar un name o un id para un bean. Si no proporcionas un nombre o un id explícitamente, el contenedor genera un nombre único para ese bean. Sin embargo, si quieres referirte a ese bean por su nombre, mediante el uso del elemento ref o una búsqueda estilo Service Locator, debes proporcionar un nombre. Los motivos para no proporcionar un nombre están relacionados con el uso de beans internos y autowiring collaborators.
Bean Naming Conventions
La convención consiste en utilizar la convención estándar de Java para los atributos, al nombrar los beans. Es decir, los nombres de los beans comienzan con una letra minúscula y a partir de ahí se escriben en mayúsculas. Algunos ejemplos de estos nombres son accountManager, accountService, userDao, loginController, etc.
Nombrar los beans de forma consistente hace que tu configuración sea más fácil de leer y entender. Además, si utilizas Spring AOP, ayuda mucho a la hora de aplicar advice a un grupo de beans relacionados por el nombre.
Con el component scanning en el classpath, Spring genera nombres de bean para componentes sin nombre, siguiendo las reglas descritas anteriormente: esencialmente, tomando el nombre de clase simple y convirtiendo su carácter inicial a minúsculas. Sin embargo, en el caso especial (poco habitual) de que haya más de un carácter y tanto el primero como el segundo estén en mayúsculas, se conserva el carácter original. Estas son las mismas reglas definidas por java.beans.Introspector.decapitalize (que Spring utiliza aquí).
Aliasing a Bean outside the Bean Definition
En la propia definición de un bean, se puede proporcionar más de un name para el bean, utilizando una combinación de hasta un nombre especificado por el atributo id y cualquier número de otros nombres en el atributo name. Estos nombres pueden ser alias equivalentes para el mismo bean y son útiles para algunas situaciones, como permitir que cada componente de una aplicación se refiera a una dependencia común utilizando un nombre de bean que sea específico para ese componente en sí.
Sin embargo, no siempre es adecuado especificar todos los alias donde se define el bean. A veces es deseable introducir un alias para un bean que está definido en otro lugar. Esto suele ocurrir en sistemas grandes en los que la configuración se divide entre cada subsistema, y cada subsistema tiene su propio conjunto de definiciones de objetos. En los metadatos de configuración basados en XML, puede utilizar el elemento
<alias name="fromName" alias="toName"/>
En este caso, un bean (en el mismo contenedor) denominado fromName también puede, tras el uso de esta definición de alias, ser referido como toName.
Por ejemplo, los metadatos de configuración del subsistema A pueden hacer referencia a un DataSource con el nombre subsystemA-dataSource. Los metadatos de configuración del subsistema B pueden hacer referencia a un DataSource con el nombre subsystemB-dataSource. Cuando se compone la aplicación principal que utiliza ambos subsistemas, la aplicación principal hace referencia al DataSource con el nombre myApp-dataSource. Para que los tres nombres hagan referencia al mismo objeto, puedes añadir las siguientes definiciones de alias a los metadatos de configuración:
<alias name="myApp-dataSource" alias="subsystemA-dataSource"/>
<alias name="myApp-dataSource" alias="subsystemB-dataSource"/>
Ahora cada componente y la aplicación principal pueden referirse al dataSource a través de un nombre que es único y garantizado para no chocar con cualquier otra definición (creando efectivamente un espacio de nombres), sin embargo, se refieren a la misma haba.
Java-configuration
Si utiliza Java Configuration, puede utilizar la anotación @Bean para proporcionar alias. See Using the @Bean Annotation for details.
Instantiating Beans
Una definición de bean es esencialmente una receta para crear uno o más objetos. El contenedor consulta la receta de un bean con nombre cuando se le pide y utiliza los metadatos de configuración encapsulados por esa definición de bean para crear (o adquirir) un objeto real.
Si se utilizan metadatos de configuración basados en XML, se especifica el tipo (o clase) de objeto que se va a instanciar en el atributo class del elemento class (que, internamente, es una propiedad Class de una instancia BeanDefinition) suele ser obligatorio. (Para excepciones, ver Instanciación mediante una instancia de Factory Method y Herencia de Definiciones Bean). Puede utilizar la propiedad Class de dos maneras:
- Típicamente, para especificar la clase bean a construir en el caso de que el propio contenedor cree directamente el bean llamando a su constructor de forma reflexiva, algo equivalente al código Java con el operador new.
- Para especificar la clase real que contiene el método estatico de factory que se invoca para crear el objeto, en el caso menos común de que el contenedor invoque un método de factory estático en una clase para crear el bean. El tipo de objeto devuelto por la invocación del método de fábrica estático puede ser la misma clase u otra clase completamente distinta.
Nested class names
Si desea configurar una definición de bean para una clase anidada, puede utilizar el nombre binario o el nombre fuente de la clase anidada.
Por ejemplo, si tienes una clase llamada SomeThing en el paquete com.example, y esta clase Something tiene una clase estática interna llamada OtherThing, se pueden separar con un signo de dólar ($) o un punto (.). Así, el valor del atributo class en una definición de bean sería com.example.Something$OtherThing o com.example.Something.Otherthing
Instantiation with a Constructor
Cuando se crea un bean mediante el método del constructor, todas las clases normales son utilizables por Spring y compatibles con él. Es decir, no es necesario que la clase que se desarrolle implemente ninguna interfaz específica o que esté codificada de una manera específica. Basta con especificar la clase del bean. Sin embargo, dependiendo del tipo de IoC que utilices para ese bean en concreto, puede que necesites un constructor por defecto (vacío).
El contenedor IoC de Spring puede gestionar prácticamente cualquier clase que desee. No está limitado a gestionar verdaderos JavaBeans. La mayoría de los usuarios de Spring prefieren JavaBeans reales con sólo un constructor por defecto (sin argumentos) y setters y getters apropiados modelados según las propiedades del contenedor. También puede tener clases más exóticas que no sean JavaBeans en su contenedor. Si, por ejemplo, necesitas utilizar un pool de conexiones heredado que no se adhiere en absoluto a la especificación JavaBean, Spring también puede gestionarlo.
Con los metadatos de configuración basados en XML puede especificar su clase bean de la siguiente manera:
<bean id="exampleBean" class="examples.ExampleBean"/>
<bean name="anotherExample" class="examples.ExampleBeanTwo"/>
Para obtener más información sobre el mecanismo para suministrar argumentos al constructor (si es necesario) y establecer las propiedades del objeto después de que se construya el objeto see Injecting Dependencies.
En el caso de argumentos para el constructor, el contenedor puede seleccionar un constructor correspondiente entre varios constructores sobrecargados. Dicho esto, para evitar ambigüedades, se recomienda mantener las firmas de los constructores lo más sencillas posible.
Instantiation with a Static Factory Method
Cuando definas un bean que creas con un método estático como factory, utiliza el atributo class para especificar la clase que contiene el método estático como factory y un atributo llamado factory-method para especificar el nombre del propio método factory. Deberías ser capaz de llamar a este método (con argumentos opcionales, como se describe más adelante) y devolver un objeto, que posteriormente es tratado como si hubiera sido creado a través de un constructor. Un uso para esta definición es llamar a métodos estáticos como factory en código legacy
La siguiente definición de bean especifica que el bean se creará llamando a un método de fábrica. La definición no especifica el tipo (clase) del objeto devuelto, sino la clase que contiene el método de fábrica. En este ejemplo, el método createInstance() debe ser un método estático. El siguiente ejemplo muestra cómo especificar un método factory:
<bean id="clientService"
class="examples.ClientService"
factory-method="createInstance"/>
El siguiente ejemplo muestra una clase que funcionaría con la definición de bean anterior:
public class ClientService {
private static ClientService clientService = new ClientService();
private ClientService() {}
public static ClientService createInstance() {
return clientService;
}
}
Para más detalles sobre el mecanismo para suministrar argumentos (opcionales) al método de fábrica y establecer propiedades a la instancia del objeto después de que el objeto sea devuelto por la factory, see Dependencies and Configuration in Detail.
ℹ️ En el caso de los argumentos de métodos factory, el contenedor puede seleccionar un método correspondiente entre varios métodos sobrecargados del mismo nombre. Dicho esto, para evitar ambigüedades, se recomienda mantener las firmas de los métodos de factory lo más sencillas posible.
TIP 💡
Un caso problemático típico con la sobrecarga de métodos de factory es Mockito con sus muchas sobrecargas del método mock. Elija la variante más específica de mock posible:
<bean id="clientService" class="org.mockito.Mockito" factory-method="mock"> <constructor-arg type="java.lang.Class" value="examples.ClientService"/> <constructor-arg type="java.lang.String" value="clientService"/> </bean>
Instantiation by Using an Instance Factory Method
De forma similar a la instanciación a través de un método estático factory, la instanciación con un método factory de una instancia invoca un método no estático de un bean existente del contenedor para crear un nuevo bean .(Usar un bean que tiene un metodos estaticos que sirven como factory para otro bean).
Para utilizar este mecanismo, deje vacío el atributo class y, en el atributo factory-bean, especifique el nombre de un bean en el contenedor actual (o padre o ancestro) que contenga el método de instancia que debe invocarse para crear el objeto. Establece el nombre del propio método de fábrica con el atributo factory-method. El siguiente ejemplo muestra cómo configurar un bean de este tipo:
<!-- the factory bean, which contains a method called createClientServiceInstance() -->
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
<!-- inject any dependencies required by this locator bean -->
</bean>
<!-- the bean to be created via the factory bean -->
<bean id="clientService"
factory-bean="serviceLocator"
factory-method="createClientServiceInstance"/>
El siguiente ejemplo muestra la correspondiente clase:
public class DefaultServiceLocator {
private static ClientService clientService = new ClientServiceImpl();
public ClientService createClientServiceInstance() {
return clientService;
}
}
Una clase factory puede tener mas de un método factory, como muestra el siguiente ejemplo:
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
<!-- inject any dependencies required by this locator bean -->
</bean>
<bean id="clientService"
factory-bean="serviceLocator"
factory-method="createClientServiceInstance"/>
<bean id="accountService"
factory-bean="serviceLocator"
factory-method="createAccountServiceInstance"/>
El siguiente ejemplo muestra la correspondiente clase:
public class DefaultServiceLocator {
private static ClientService clientService = new ClientServiceImpl();
private static AccountService accountService = new AccountServiceImpl();
public ClientService createClientServiceInstance() {
return clientService;
}
public AccountService createAccountServiceInstance() {
return accountService;
}
}
Este acercamiento muestra que el bean factory (DefaultServiceLocator) puede ser manejado y configurado mediante la inyección de dependencias
ℹ️ En la documentación de spring, “factory bean” se refiere a un bean que es configurado en el contenedor de Spring y que crea objetos a través de una instancia (bean registrado ya) o un método estatico como factory. Por el contrario
FactoryBeanse refiere a una implementación de la claseFactoryBean
Determining a Bean’s Runtime Type
El tipo en tiempo de ejecución de un bean no es fácil de determinar. El atributo class en la definición de un bean es solo una referencia inicial a la clase, potencialmente combinado con un método factory declarado o ser una clase FactoryBean la cual puede conducir a un tipo diferente de bean en tiempo de ejecución, o no ser establecido ni siquiera como en el caso de una metodo factory de una instancia (bean)(el cual es resuelto usando el nombre descrito anteriormentefactory-name).
Además, el proxy AOP puede envolver una instancia de bean con un proxy basado en interfaces con limitación del tipo real del bean de destino (sólo sus interfaces implementadas).
La forma recomendada para averiguar el tipo real en tiempo de ejecución de un bean en particular es una llamada a BeanFactory.getType para el nombre del bean especificado. Esto tiene en cuenta todos los casos anteriores y devuelve el tipo de objeto que una llamada BeanFactory.getBean va a devolver para el mismo nombre del bean.