文章目录
- 前言
- BeanDefinition类的属性释义
- BeanDefinition的分类
- BeanDefinition的使用
写在前面
注:本文章使用的 SpringBoot 版本为 2.2.4.RELEASE,其 Spring 版本为 5.2.3.RELEASE
前言
BeanDefinition是Spring中非常重要的概念,可以说 它是IoC容器中我们定义的各种Bean的"真身"。它主要用来描述Bean,内部拥有很多属性及方法。我们通过注解操作Bean或在Bean上设置的一些属性均能在BeanDefinition类中找到其身影。Spring也是借助BeanDefinition来生成Bean的。
接下来我们会分三个部分介绍BeanDefinition,分别是BeanDefinition类中常见属性释义,BeanDefinition的分类以及BeanDefinition的使用
BeanDefinition类的属性释义
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
//单例作用域 属性
String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
//原型作用域 属性
String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;
// 这三个属性 用于标识Bean的类别或来源
//代表是用户自己定义的Bean
int ROLE_APPLICATION = 0;
//代表Bean来自于配置文件
int ROLE_SUPPORT = 1;
//代表Bean来自于Spring内部
int ROLE_INFRASTRUCTURE = 2;
// 设置parentName,bd允许有层级 因此可以指定父bd的名称
void setParentName(@Nullable String parentName);
//获取parentName
@Nullable
String getParentName();
// 这个比较重要 设置BeanClassName,代表bd是属于哪个类型的
void setBeanClassName(@Nullable String beanClassName);
//获取BeanClassName
@Nullable
String getBeanClassName();
// 设置Bean的作用域 是单例、原型还是其他
void setScope(@Nullable String scope);
@Nullable
String getScope();
//设置是否懒加载,与@Lazy相似
void setLazyInit(boolean lazyInit);
boolean isLazyInit();
//设置依赖的Bean,即加载当前bd之前,需要先加载依赖的bd 才会有当前bd。这与@DependsOn注解效果类似
void setDependsOn(@Nullable String... dependsOn);
@Nullable
String[] getDependsOn();
//设置bean是否可以依赖注入,可参考@AutoWired
void setAutowireCandidate(boolean autowireCandidate);
boolean isAutowireCandidate();
//设置当前bean是否为主要候选Bean,一般同一类型的Bean存在多个实例时,这个字段很有效
void setPrimary(boolean primary);
boolean isPrimary();
// 设置工厂BeanName,这可以跟FactoryMethod一起来用 帮忙创建Bean
void setFactoryBeanName(@Nullable String factoryBeanName);
@Nullable
String getFactoryBeanName();
//设置FactoryMethod
void setFactoryMethodName(@Nullable String factoryMethodName);
@Nullable
String getFactoryMethodName();
//获取构造函数参数
ConstructorArgumentValues getConstructorArgumentValues();
/**
* Return if there are constructor argument values defined for this bean.
* @since 5.0.2
*/
default boolean hasConstructorArgumentValues() {
return !getConstructorArgumentValues().isEmpty();
}
//获取属性参数
MutablePropertyValues getPropertyValues();
default boolean hasPropertyValues() {
return !getPropertyValues().isEmpty();
}
//设置Bean的初始化方法,这与@InitMethod类似
void setInitMethodName(@Nullable String initMethodName);
@Nullable
String getInitMethodName();
//设置Bean销毁前方法,这与@DestroyMethod类似
void setDestroyMethodName(@Nullable String destroyMethodName);
@Nullable
String getDestroyMethodName();
//设置Bean的类别 具体取值 在前面属性部分介绍了
void setRole(int role);
int getRole();
//设置bd的描述信息
void setDescription(@Nullable String description);
@Nullable
String getDescription();
// Read-only attributes
//这个暂时不太清楚其用途,有知道的小伙伴也可以分享一下
ResolvableType getResolvableType();
//是否为单例bean
boolean isSingleton();
// 是否为原型bean
boolean isPrototype();
//当前Bean是否为抽象类
boolean isAbstract();
@Nullable
String getResourceDescription();
@Nullable
BeanDefinition getOriginatingBeanDefinition();
}
通过上面BeanDefinition的类描述信息看到,其继承了AttributeAccessor
和BeanMetadataElement
,我们也来看一下这两个接口吧。
public interface AttributeAccessor {
//设置附加属性
void setAttribute(String name, @Nullable Object value);
//获取附加属性
@Nullable
Object getAttribute(String name);
//移除附加属性
@Nullable
Object removeAttribute(String name);
boolean hasAttribute(String name);
String[] attributeNames();
}
对于AttributeAccessor
接口,我理解为它是为BeanDefinition设置附加属性的,有没有都行。也不会影响到Bean的属性赋值等操作
public interface BeanMetadataElement {
//获取BeanDefinition的来源,可以理解为创建BeanDefinition的那个类,setSource会在AbstractBeanDefinition中进行
@Nullable
default Object getSource() {
return null;
}
}
BeanDefinition的分类
AbstractBeanDefinition
是 BeanDefinition 的子抽象类,它实现了接口中定义的一系列操作方法,并定义了一系列的常量属性,这些常量会影响到 Spring 实例化 Bean过程 。
-
RootBeanDefinition
代表一个被合并的BeanDefinition。可以由多个具有继承关系的GenericBeanDefinition
创建而来。RootBeanDefinition也可被当做普通的BeanDefinition注册到容器中。 -
ChildBeanDefinition
不可以单独存在,必须依赖一个父 BeanDefinition(通过观察ChildBeanDefinition
构造方法可知)。
构造 ChildBeanDefinition 时,通过构造方法传入父 BeanDetintion 的名称或通过 setParentName 设置父名称。它可以从父类继承方法参数、属性值,并可以重写父类的方法,同时也可以增加新的属性或者方法。
-
GenericBeanDefinition
是 当前BeanDefinition的首选实现类。它允许指定一个类以及可选的构造函数参数值和属性值。此外,可以通过“parentName”属性动态定义父BeanDefinition。
AnnotatedBeanDefinition
主要是用来操作注解元信息的,与注解Bean有关
-
ConfigurationClassBeanDefinition
描述 标注 @Configuration 注解的类中,通过 @Bean 注解实例化的 Bean(后面分析@Bean的解析时会说到) -
AnnotatedGenericBeanDefinition
描述标注了@Configuration注解的Bean -
ScannedGenericBeanDefinition
描述标注了@Component注解的Bean,也包括由@Component派生出来的@Service、@Controller 等
实际上 RootBeanDefinition
、ChildBeanDefinition
组成的这种层次结构的BeanDefinition,用GenericBeanDefinition
也可以实现。当下更愿意将RootBeanDefinition
当做一个被合并的BeanDefinition
,而一般情况下 主要使用GenericBeanDefinition
下面就演示下几种BeanDefinition的使用
BeanDefinition的使用
先来看一下实体类
@Data
public class SuperUser implements Serializable {
private String address;
public SuperUser(String address) {
this.address = address;
}
public SuperUser() {
}
}
@Data
@ToString(callSuper = true)
public class User extends SuperUser {
private String name;
private Integer age;
public User() {
}
public User(String name, Integer age) {
this.name = name;
this.age = age;
}
}
基于RootBeanDefinition/ChildBeanDefinition 注册有层次的Bean
public class ChildBeanDefinitionDemo {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
//父BeanDefinition
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition();
rootBeanDefinition.setBeanClass(SuperUser.class);
MutablePropertyValues propertyValues = new MutablePropertyValues();
propertyValues.addPropertyValue("address", "地址");
rootBeanDefinition.setPropertyValues(propertyValues);
//设置附加属性
rootBeanDefinition.setAttribute("a","b");
//设置bd来源
rootBeanDefinition.setSource(ChildBeanDefinitionDemo.class);
//子BeanDefinition
//superUser是parentName
ChildBeanDefinition childBeanDefinition = new ChildBeanDefinition("superUser");
childBeanDefinition.setBeanClass(User.class);
//设置构造函数参数
ConstructorArgumentValues argumentValues = new ConstructorArgumentValues();
argumentValues.addIndexedArgumentValue(0, "我就是我");
argumentValues.addIndexedArgumentValue(1, 18);
childBeanDefinition.setConstructorArgumentValues(argumentValues);
childBeanDefinition.setPrimary(true);
//注册BeanDefinition
context.registerBeanDefinition("superUser", rootBeanDefinition);
context.registerBeanDefinition("user", childBeanDefinition);
context.refresh();
User user = context.getBean("user",User.class);
System.out.println(user);
SuperUser superUser = context.getBean("superUser",SuperUser.class);
System.out.println(superUser);
context.close();
}
}
基于GenericBeanDefinition 注册有层次的Bean
public class GenericBeanDefinitionDemo {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
//父BeanDefinition
GenericBeanDefinition rootBeanDefinition = new GenericBeanDefinition();
rootBeanDefinition.setBeanClass(SuperUser.class);
//设置参数
MutablePropertyValues propertyValues = new MutablePropertyValues();
propertyValues.addPropertyValue("address", "地址");
rootBeanDefinition.setPropertyValues(propertyValues);
//子BeanDefinition
GenericBeanDefinition childBeanDefinition = new GenericBeanDefinition();
childBeanDefinition.setBeanClass(User.class);
//设置构造参数
ConstructorArgumentValues argumentValues = new ConstructorArgumentValues();
argumentValues.addIndexedArgumentValue(0, "我就是我");
argumentValues.addIndexedArgumentValue(1, 18);
childBeanDefinition.setConstructorArgumentValues(argumentValues);
childBeanDefinition.setParentName("superUser");
//类型相同时 以子类为主
childBeanDefinition.setPrimary(true);
context.registerBeanDefinition("superUser", rootBeanDefinition);
context.registerBeanDefinition("user", childBeanDefinition);
context.refresh();
User user = context.getBean("user", User.class);
System.out.println(user);
SuperUser superUser = context.getBean("superUser", SuperUser.class);
System.out.println(superUser);
context.close();
}
}
可以看出两者的实现基本上是相同的。
上面我们介绍了通过API的方式注册有层次的BeanDefinition,下面也给出通过XML配置的方式
<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="user" class="com.wojiushiwo.dto.User" parent="superUser" primary="true">
<property name="name" value="wojiushiwo"/>
<property name="age" value="18"/>
</bean>
<bean id="superUser" class="com.wojiushiwo.dto.SuperUser">
<property name="address" value="地址"/>
</bean>
</beans>
如上 通过bean标签中parent属性为user指定了父BeanDefinition,也可实现与API相同的效果。
加载xml资源至Spring容器的代码这里就不再赘述了,可参见其他文章
关于AnnotatedBeanDefinition
接口相关的BeanDefinition实例使用这里先不介绍了,等在分析常用注解的解析过程时再聊。