什么是Spring BeanDefinition
在使用Spring中,我们通常在XML文件或者使用Java Config来定义Bean。这些定义Bean相关的信息被解析后是如何存储的呢?答案就是Spring BeanDefinition。
BeanDefinition 就是Spring中用来存储解析后的Bean定义相关信息的,BeanDefinition与Bean之间的关系就像是类与类的实例一样。
BeanDefinition中包含哪些信息
引用文档源码上的注释信息,BeanDefinition描述了一个Bean实例,它具有属性值、构造函数参值以及具体实现等其他信息。例如我们在Spring的配置文件中我们可以定义一个Bean如下:
<bean id="user" name="user" class="com.buydeem.share.beandefinition.User">
<constructor-arg name="name" value="tom"/>
<constructor-arg name="age" value="18"/>
<constructor-arg name="address" value="xx路38号"/>
</bean>
上面就是通过XML定义的一个简单的Bean,我们定义了构造函数的参数值还有具体实现类等信息。这部分信息在Spring中最后都会以BeanDefinition的形式存储起来和使用。
BeanDefinition详解
通过上面我们大致了解了BeanDefinition的作用和基础信息,现在我们具体来分析一下BeanDefinition。
接口定义
BeanDefinition在Spring中是一个接口,接口定义如下:
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;
int ROLE_APPLICATION = 0;
int ROLE_SUPPORT = 1;
int ROLE_INFRASTRUCTURE = 2;
void setParentName(@Nullable String parentName);
@Nullable
String getParentName();
void setBeanClassName(@Nullable String beanClassName);
@Nullable
String getBeanClassName();
void setScope(@Nullable String scope);
@Nullable
String getScope();
void setLazyInit(boolean lazyInit);
boolean isLazyInit();
void setDependsOn(@Nullable String... dependsOn);
@Nullable
String[] getDependsOn();
void setAutowireCandidate(boolean autowireCandidate);
boolean isAutowireCandidate();
void setPrimary(boolean primary);
boolean isPrimary();
void setFactoryBeanName(@Nullable String factoryBeanName);
@Nullable
String getFactoryBeanName();
void setFactoryMethodName(@Nullable String factoryMethodName);
@Nullable
String getFactoryMethodName();
ConstructorArgumentValues getConstructorArgumentValues();
default boolean hasConstructorArgumentValues() {
return !getConstructorArgumentValues().isEmpty();
}
MutablePropertyValues getPropertyValues();
default boolean hasPropertyValues() {
return !getPropertyValues().isEmpty();
}
void setInitMethodName(@Nullable String initMethodName);
@Nullable
String getInitMethodName();
void setDestroyMethodName(@Nullable String destroyMethodName);
@Nullable
String getDestroyMethodName();
void setRole(int role);
int getRole();
void setDescription(@Nullable String description);
@Nullable
String getDescription();
ResolvableType getResolvableType();
boolean isSingleton();
boolean isPrototype();
boolean isAbstract();
@Nullable
String getResourceDescription();
@Nullable
BeanDefinition getOriginatingBeanDefinition();
}
可以看出整个定义还是比较简单的,它继承了BeanMetadataElement
和AttributeAccessor
接口。其中BeanMetadataElement
只有一个方法getSource
用来返回Bean的来源。而AttributeAccessor
接口主要用来规范访问对象元素的方法。而BeanDefinition接口本身提供了一系列的方法,结合我们平时用的XML文件还是比较好理解的。
-
SCOPE_SINGLETON
和SCOPE_PROTOTYPE
这两个属性用来表示Bean是不是单例的,对应的就是后面的setScope
和getScope
方法。 -
ROLE_XXX
用来描述BeanDefinition的角色。ROLE_APPLICATION
表示是用户定义的Bean,ROLE_SUPPORT
表示是该BeanDefinition是某些复杂配置的支持部分,ROLE_INFRASTRUCTURE
用来表示这是Spring内部的BeanDefinition。这些配置通常很少使用,我们可以通过getRole或者setRole来修改。 -
setParentName/getParentName
用来配置parent名称,对应的就是xml中parent的值。该属性可能我们平常使用较少,主要跟BeanDefinition继承相关。 -
setBeanClassName/getBeanClassName
用来配置Bean的全路径,对应的就是xml中的class的值。 -
setLazyInit/isLazyInit
用来配置是否懒加载,对应的就是xml中的lazy-init配置。 -
setDependsOn/getDependsOn
设置获取Bean的依赖。对应的是xml中的depends-on。 -
setAutowireCandidate/isAutowireCandidate
设置获取是否自动装配。对应的是xml中的autowire-candidate。 -
setPrimary/isPrimary
设置获取是否是首选Bean。对应的是xml中的primary。 -
setFactoryBeanName/getFactoryBeanName
设置获取FactoryBean。对应xml中factory-bean配置。 -
setFactoryMethodName/getFactoryMethodName
设置获取FactoryMethodName。对应xml中factory-method配置。 -
getConstructorArgumentValues
返回该Bean构造方法的参数值。 -
hasConstructorArgumentValues
判断Bean构造方法的参数值。 -
getPropertyValues
获取Bean的普通属性集合。 -
hasPropertyValues
判断该Bean的属性值是否为空。 -
setInitMethodName/setDestroyMethodName
设置初始化/销毁方法名称。 -
setDescription/getDescription
设置获取Bean的描述。 -
isSingleton
Bean是否为单例。 -
isPrototype
Bean是否为原型。 -
isAbstract
Bean是否抽象。 -
getResourceDescription
返回定义Bean的资源描述。 -
getOriginatingBeanDefinition
如果当前BeanDefinition是一个代理对象,那么该方法可以用来返回原始的BeanDefinition。
可以发现上面的大多数方法跟我们XML中配置文件相关,如何了解XML配置文件对这些方法肯定不陌生。
BeanDefinition实现类

从图中可以看出BeanDefinition有很多实现,下面我们来看BeanDefinition的子类信息。
AbstractBeanDefinition
该类是一个抽象类,在BeanDefinition中定义了很多的get和set属性相关方法,而这些属性就是在AbstractBeanDefinition中定义的。同时BeanDefinition中的大多数方法都是在该类中实现的,而BeanDefinition的实现类基本上都是通过继承AbstractBeanDefinition来实现的。
ChildBeanDefinition
从Spring2.5开始,ChildBeanDefinition已经不再使用,取而代之的是GenericBeanDefinition。
GenericBeanDefinition
该类替代了ChildBeanDefinition,在BeanDefinition中也存在父子关系。我们可以通过设置父BeanDefinition,从父类继承部分属性,在Spring官方文档中叫做「Bean Definition Inheritance」。
<bean id="parentBd" abstract="true">
<property name="name" value="jack"/>
<property name="age" value="19"/>
</bean>
<bean id="jack-user" class="com.buydeem.share.beandefinition.User" parent="parentBd">
<property name="address" value="xx路38号"/>
</bean>
<bean id="jack-student" class="com.buydeem.share.beandefinition.Student" parent="parentBd">
<property name="classNo" value="1"/>
</bean>
从上面的XML中可以看出,一共有三个BeanDefinition被定义在XML中,其中另外两个的parent都是「parentBd」。我们通过代码打印出定义的BeanDefinition内容:
public class BeanDefinitionDemo1 {
public static void main(String[] args) {
//创建IOC容器
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
//加载XML
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinitions("spring-bean-definition-1.xml");
//遍历BeanDefinition
String[] definitionNames = factory.getBeanDefinitionNames();
for (String definitionName : definitionNames) {
BeanDefinition definition = factory.getBeanDefinition(definitionName);
System.out.println(definition);
}
}
}
上面代码运行后会打印出容器中所有的BeanDefinition,我们截取上面的三个BeanDefinition内容如下:
Generic bean: class [null]; scope=; abstract=true; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [spring-bean-definition-1.xml]
Generic bean with parent 'parentBd': class [com.buydeem.share.beandefinition.User]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [spring-bean-definition-1.xml]
Generic bean with parent 'parentBd': class [com.buydeem.share.beandefinition.Student]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [spring-bean-definition-1.xml]
从结果可以看出,上面定义的「jack-user」和「jack-student」都继承了「parentBd」。它们从「parentBd」继承了「name」和「age」信息,而其中的「address」和「classNo」则是自己定义的,这种方式通常能用来减少我们的XML配置。
RootBeanDefinition
RootBeanDefinition可以用来作为其他BeanDefinition的父BeanDefinition,但是它有一个与其他BeanDefinition最大的不同在于,它不能设置父BeanDefinition。
@Override
public String getParentName() {
return null;
}
@Override
public void setParentName(@Nullable String parentName) {
if (parentName != null) {
throw new IllegalArgumentException("Root bean cannot be changed into a child bean with parent reference");
}
}
从parent相关方法的实现可以看出,RootBeanDefinition是不能设置parent属性的。在BeanDefinition合并之后,存储在mergedBeanDefinitions中的Bean就是RootBeanDefinition。
ScannedGenericBeanDefinition和AnnotatedGenericBeanDefinition
这两个类都继承了AnnotatedBeanDefinition
接口,通常来说通过包扫描出来的BeanDefinition就是ScannedGenericBeanDefinition类型,而在SpringBoot中我们使用@Configuration标记一个类为配置类,它生成的BeanDefinition类型就是AnnotatedGenericBeanDefinition。
public class BeanDefinitionDemo2 {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
String[] definitionNames = context.getBeanDefinitionNames();
for (String definitionName : definitionNames) {
System.out.printf("BeanDefinition名称:[%s],类型:[%s]n",definitionName,context.getBeanDefinition(definitionName).getClass());
}
}
}
@Component
class UserDao{
}
@Service
class UserService{
}
@Configuration
@ComponentScan
class AppConfig{
}
BeanDefinition名称:[appConfig],类型:[class org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition]
BeanDefinition名称:[userDao],类型:[class org.springframework.context.annotation.ScannedGenericBeanDefinition]
BeanDefinition名称:[userService],类型:[class org.springframework.context.annotation.ScannedGenericBeanDefinition]
从打印结果可以看出,appConfig的类型就是AnnotatedGenericBeanDefinition
,而userDao和userService的类型则为ScannedGenericBeanDefinition
。
BeanDefinitionRegistry
从前面的内容我们了解到BeanDefinition的作用以及类型,现在的问题是BeanDefinition是如何注册到IOC容器中的呢?
public interface BeanDefinitionRegistry extends AliasRegistry {
//注册BeanDefinition
void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException;
//删除BeanDefinition
void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
//获取BeanDefinition
BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
//是否包含BeanDefinition
boolean containsBeanDefinition(String beanName);
//获取所有的BeanDefinition
String[] getBeanDefinitionNames();
//获取BeanDefinition数量
int getBeanDefinitionCount();
//bean名称是否被使用
boolean isBeanNameInUse(String beanName);
}
查看Spring源码可以找到BeanDefinitionRegistry
类,这个类就是Spring中定义的BeanDefinition注册接口。接口主要定义了一些对BeanDefinition管理相关方法。而这个接口的实现如下图所示:

从图中可以看出,BeanDefinition我们主要可以看到三个实现,其中SimpleBeanDefinitionRegistry
这个主要用来做示例使用我们可以忽律暂不关注。而GenericApplicationContext
也实现了该接口,不过你查看实现可以发现,它的内部保存了DefaultListableBeanFactory
,而对于BeanDefinitionRegistry
接口的实现是通过内部DefaultListableBeanFactory
来实现的,所以我们重点关注DefaultListableBeanFactory
的实现即可。(PS:DefaultListableBeanFactory
类很重要,IOC的很多实现就是在这个里面实现的)
查看DefaultListableBeanFactory
实现,可以发现其实BeanDefinition是通过一个ConcurrentHashMap来存储BeanDefinition的。
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
@Override
public int getBeanDefinitionCount() {
return this.beanDefinitionMap.size();
}
@Override
public String[] getBeanDefinitionNames() {
String[] frozenNames = this.frozenBeanDefinitionNames;
if (frozenNames != null) {
return frozenNames.clone();
}
else {
return StringUtils.toStringArray(this.beanDefinitionNames);
}
}
而getBeanDefinitionCount
和getBeanDefinitionNames
实现就比较简单了,一看就懂。我们重点关注注册和删除两个方法。
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
//校验参数
Assert.hasText(beanName, "Bean name must not be empty");
Assert.notNull(beanDefinition, "BeanDefinition must not be null");
//对AbstractBeanDefinition做校验
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed", ex);
}
}
//判断BeanDefinition是否已存在
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
if (existingDefinition != null) {
//如果不允许BeanDefinition覆盖,默认情况下是true
if (!isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
}
//打印日志相关
else if (existingDefinition.getRole() < beanDefinition.getRole()) {
if (logger.isInfoEnabled()) {
logger.info("Overriding user-defined bean definition for bean '" + beanName +
"' with a framework-generated bean definition: replacing [" +
existingDefinition + "] with [" + beanDefinition + "]");
}
}
else if (!beanDefinition.equals(existingDefinition)) {
if (logger.isDebugEnabled()) {
logger.debug("Overriding bean definition for bean '" + beanName +
"' with a different definition: replacing [" + existingDefinition +
"] with [" + beanDefinition + "]");
}
}
else {
if (logger.isTraceEnabled()) {
logger.trace("Overriding bean definition for bean '" + beanName +
"' with an equivalent definition: replacing [" + existingDefinition +
"] with [" + beanDefinition + "]");
}
}
//将BeanDefinition放入到beanDefinitionMap中
this.beanDefinitionMap.put(beanName, beanDefinition);
}
//BeanDefinition已经在beanDefinitionMap存在了
else {
//判断此时是否已经有Bean被创建了
if (hasBeanCreationStarted()) {
// 如果已经有Bean被创建了,为了稳定迭代不能再修改启动时集合元素
synchronized (this.beanDefinitionMap) {
//同步代码块中执行添加操作
this.beanDefinitionMap.put(beanName, beanDefinition);
//因为Map无法保证beanName有序,使用List主要是用来保证顺序的
List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
//如果有单体Bean跟这个BeanDefinition名称相同则在manualSingletonNames中remove掉这个单体对象
//BeanDefinition和单体对象都是依赖来源,都可以通过beanName找到Bean。Spring是可以手动注册单体Bean到容器中的
removeManualSingletonName(beanName);
}
}
else {
// Still in startup registration phase
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
removeManualSingletonName(beanName);
}
this.frozenBeanDefinitionNames = null;
}
if (existingDefinition != null || containsSingleton(beanName)) {
//重新设置BeanDefinition
//如果该BeanDefinition注册过,则在mergedBeanDefinitions中会有缓存
//如果该beanName曾经是单体对象,上面只是remove manualSingletonNames,还需要清空singletonObjects等等,singletonObjects是Spring单例Bean的三层缓存中第一层,三层缓存可以解决循环依赖问题
resetBeanDefinition(beanName);
}
else if (isConfigurationFrozen()) {
//删除缓存
clearByTypeCache();
}
}
上面就是注册BeanDefinition方法的全部实现,简单的理解就是将BeanDefinition放到beanDefinitionMap中。下面我们就手动注册BeanDefinition到容器中试一下。
public class BeanDefinitionDemo4 {
public static void main(String[] args) {
//创建BeanDefinition
AbstractBeanDefinition bd = BeanDefinitionBuilder.genericBeanDefinition(Person.class)
.addPropertyValue("name", "tom")
.addPropertyValue("age", 18)
.getBeanDefinition();
//创建BeanFactory
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
//注册
factory.registerBeanDefinition("person",bd);
//获取BeanDefinition
BeanDefinition personDb = factory.getBeanDefinition("person");
System.out.println("personDb == bd : " + (personDb == bd));
}
}
class Person{
private String name;
private Integer age;
public Person() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
上面的示例代码中我们手动创建BeanDefinition然后将其注册到IOC容器中,然后我们再从IOC容器中获取BeanDefinition对比之前放入的看是不是相同的。很明显这个两个BeanDefinition是一样的。
总结
看完本文我们主要要明白下面两点:
-
BeanDefinition简单的说就是用来描述一个Bean的,它类似于施工中的图纸,而IOC就是根据BeanDefinition中的定义来对Bean就行创建、销毁等。 -
如何将BeanDefinition注册到IOC容器中,了解BeanDefinitionRegistry接口。
后面我会不定时更新Spring进阶相关文章,欢迎关注公众号及时查看更新内容。
原文始发于微信公众号(一只菜鸟程序员):Spring进阶-BeanDefinition
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/73022.html