Spring进阶-BeanDefinition

什么是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 AttributeAccessorBeanMetadataElement {
  
 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();

}

可以看出整个定义还是比较简单的,它继承了BeanMetadataElementAttributeAccessor接口。其中BeanMetadataElement只有一个方法getSource用来返回Bean的来源。而AttributeAccessor接口主要用来规范访问对象元素的方法。而BeanDefinition接口本身提供了一系列的方法,结合我们平时用的XML文件还是比较好理解的。

  • SCOPE_SINGLETONSCOPE_PROTOTYPE这两个属性用来表示Bean是不是单例的,对应的就是后面的setScopegetScope方法。
  • 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的描述。
  • isSingletonBean是否为单例。
  • isPrototypeBean是否为原型。
  • isAbstractBean是否抽象。
  • getResourceDescription返回定义Bean的资源描述。
  • getOriginatingBeanDefinition如果当前BeanDefinition是一个代理对象,那么该方法可以用来返回原始的BeanDefinition。

可以发现上面的大多数方法跟我们XML中配置文件相关,如何了解XML配置文件对这些方法肯定不陌生。

BeanDefinition实现类

Spring进阶-BeanDefinition
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管理相关方法。而这个接口的实现如下图所示:

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);
  }
 }

getBeanDefinitionCountgetBeanDefinitionNames实现就比较简单了,一看就懂。我们重点关注注册和删除两个方法。

 @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

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!