在Spring中,BeanDefinition是一个接口,用于描述Spring容器中的Bean的配置元数据。它定义了Bean的名称、类型、作用域以及其他配置信息。
BeanDefinition可以通过XML配置文件、Java注解或者Java代码等方式来定义。它包含以下重要属性:
Bean的名称:每个BeanDefinition都有一个唯一的名称,用于在容器中标识和获取对应的Bean实例。
Bean的类型:指定了该Bean实例所对应的类或接口。
Bean的作用域:描述了该Bean实例在容器中的生命周期和可见范围。常见的作用域包括单例(Singleton)、原型(Prototype)、请求(Request)、会话(Session)等。
Bean之间的依赖关系:描述了该Bean与其他Bean之间的依赖关系,例如通过构造函数、属性注入等方式。
Bean的初始化方法和销毁方法:描述了在容器创建和销毁Bean实例时需要执行的方法。
其他配置信息:如是否延迟初始化、是否懒加载、是否可自动装配等。
通过解析和读取配置文件或注解,Spring可以根据这些BeanDefinition来创建对应的Bean实例,并将其管理起来。这使得Spring可以基于不同需求灵活地创建和管理各种类型的对象,并且可以提供自动装配、AOP等功能。
BeanDefinition 的层次关系
BeanDefinition 是一个接口,提供了设置 Bean 初始化信息的一些方法,如设置作用于、设置依赖关系等
AbstractBeanDefinition
是一个继承了BeanDefinition的抽象类,他对BeanDefinition中需要设置的属性进行了具体的实现。
// Class对象
@Nullable
private volatile Object beanClass;
// 作用域,默认为单例
@Nullable
private String scope = SCOPE_DEFAULT;
// 成员变量
@Nullable
private MutablePropertyValues propertyValues;
// 有参构造方法
@Nullable
private ConstructorArgumentValues constructorArgumentValues;
// 依赖注入的 bean
@Nullable
private String[] dependsOn;
ConstructorArgumentValues
ConstructorArgumentValues类用于保存构造函数参数值的信息。它是一个用于存储和访问构造函数参数值的容器类。
ConstructorArgumentValues类的主要作用是向容器注册构造函数参数值,并在需要时提供对这些参数值的访问。它允许用户通过指定参数索引或参数名称来获取对应的参数值。
具体而言,ConstructorArgumentValues类提供了以下方法:
- addIndexedArgumentValue (int index, Object value):向容器注册按索引指定的构造函数参数值。
- addGenericArgumentValue (Object value):向容器注册不按索引指定的构造函数参数值。
- addGenericArgumentValue (Object value, String type):向容器注册不按索引指定,并且指定了类型的构造函数参数值。
- getIndexedArgumentValue (int index, Class requiredType):根据索引获取指定类型的构造函数参数值。
- getGenericArgumentValue (Class requiredType):根据类型获取不按索引指定的构造函数参数值。
通过使用ConstructorArgumentValues类,我们可以在运行时动态地将参数传递给Spring Bean的构造函数,从而实现更灵活和可扩展的配置方式。
BeanDefinitionBuilder
SpringBoot BeanDefinitionBuilder 的介绍以及常用方法 BeanDefinitionBuilder 是 Spring 框架中的一个工具类,用于构建 BeanDefinition 对象。BeanDefinitionBuilder 提供了一些常用的方法,使得创建和配置 BeanDefinition 常用方法包括:
- setScope(String scope):设置 bean 的作用域,例如 singleton 或 protot2. setLazyInit(boolean lazyInit):设置 bean 是否延迟初始化3. setAutowired(boolean autowire):设置 bean 是否自动注入依赖4. addConstructorArgValue(Object value):添加构造函数参数值。
- addPropertyValue(String name, Object value):添加属性值。
- addPropertyReference(String name, String beanName):添加对其他 bean 的引用作为属性值7. setInitMethodName(String initMethodName):设置初始化方法名。
- setDestroyMethodName(String destroyMethodName):设置销毁方法名。
使用 BeanDefinitionBuilder 可以通过链式调用这些方法来创建和配置 BeanDefinition 对象。例 builder.setScope("singleton")
.setLazyInit(true)
.setAutowired(true)
.addConstructorArgValue("arg1")
.addPropertyValue("property1", "value1")
.addPropertyReference("property2", "otherBean")
.setInitMethodName("initMethod")
.setDestroyMethodName("destroyMethod");
BeanDefinition beanDefinition = builder.getBeanDefinition();
上述代码创建了一个MyClass类的BeanDefinition对象,并通过各种方法进行了配置。最后调用getBeanDefinition()方法获取到最终的BeanDefinition对象。
SpringBoot 运行时通过 BeanDefinition 手动注册 Bean 对象
要想手动注册 bean 对象,我们需要知道 Spring 是通过哪个类哪个对象去将 bean 注册到 Spring 容器之中的。
BeanFactory
是 Spring 提供的一个接口,内部提供了对 Bean 操作的各种基础方法,在 Spring 实现的默认 Bean 工厂为 DefaultListableBeanFactory
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable
可以看到,该默认实现已经实现了 BeanDefinitionRegistry 接口,我们获取到 BeanFactory 对象后强转为 BeanDefinitionRegistry 就可以拥有手动将 bean 注册到容器中的能力
- 示例代码
package xyz.tiegangan.chat.common.fileclient.core;
import jodd.util.ClassUtil;
import lombok.SneakyThrows;
import org.springframework.beans.BeansException;
import org.springframework.context.support.GenericApplicationContext;
import xyz.tiegangan.chat.common.fileclient.properties.FileClientProperties;
/**
* @author huangmuhong
* @version 1.0.0
* @date 2023/7/8
*/
public class FileClientInitializationFactory {
private final GenericApplicationContext applicationContext;
private final FileClientProperties fileClientProperties;
public FileClientInitializationFactory(
GenericApplicationContext applicationContext, FileClientProperties fileClientProperties) {
this.applicationContext = applicationContext;
this.fileClientProperties = fileClientProperties;
}
@SneakyThrows
public void fileClientInit() throws BeansException {
final FileClientProperties.Config config = fileClientProperties.getConfig();
BeanDefinitionRegistry registry =
(BeanDefinitionRegistry) this.applicationContext.getBeanFactory();
final BeanDefinitionBuilder beanDefinitionBuilder =
BeanDefinitionBuilder.genericBeanDefinition();
beanDefinitionBuilder.setScope(BeanDefinition.SCOPE_SINGLETON);
final FileClientEnum type = config.getType();
final Class<? extends FileClient> fileClientClass = type.getFileClientClass();
FileClient fileClient = null;
if (type == FileClientEnum.MINIO) {
beanDefinitionBuilder.addConstructorArgValue(config);
beanDefinitionBuilder.getRawBeanDefinition().setBeanClass(MinioFileClient.class);
} else {
throw new IllegalArgumentException("不支持的文件客户端类型:" + config.getType());
}
registry.registerBeanDefinition(type.getBeanName(),
beanDefinitionBuilder.getBeanDefinition());
return fileClient;
}
}
评论区