首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Spring源码系列:BeanDefinition载入(下)

Spring源码系列:BeanDefinition载入(下)

作者头像
磊叔的技术博客
发布2025-06-07 15:06:24
发布2025-06-07 15:06:24
1170
举报

在Spring源码系列:BeanDefinition载入(上)中已经大概捋了一下解析过程,本篇将记录一下bean的注册过程。

bean的注册就是DefaultListableBeanFactory中registerBeanDefinition方法来完成的。那我就来看registerBeanDefinition这个方法的具体逻辑。

1、beanDefinition类型判断和验证

这里的验证主要是验证不能将静态工厂方法与方法重写相结合(静态工厂方法必须创建实例);

代码语言:javascript
复制
if (beanDefinition instanceof AbstractBeanDefinition) {
    try {
        ((AbstractBeanDefinition) beanDefinition).validate();
    }
    catch (BeanDefinitionValidationException ex) {
        throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(),
        beanName,"Validation of bean definition failed", ex);
    }
}

2、尝试从beanDefinitionMap中获取老的bean

这里就是先根据beanName从beanDefinitionMap去取BeanDefinition,并将结果给oldBeanDefinition。

代码语言:javascript
复制
BeanDefinition oldBeanDefinition;
oldBeanDefinition = this.beanDefinitionMap.get(beanName);

3、beanDefinitionMap中已经存在名为beanName的Beandefinition

如果当前beanDefinitionMap中已经存在名为beanName的Beandefinition了(即检查是否有相同名称的beanDefinition已经在Ioc容器中注册过了)。,如果有,则进行以下具体策略:

  • 如果不允许bean被覆盖,则直接抛出不能重新注册,bean已经存在这样的异常信息
  • 使用框架生成的Bean来代替用户自定义的bean
  • 覆盖原有的Beandefinition
代码语言:javascript
复制
if (oldBeanDefinition != null) {
    if (!isAllowBeanDefinitionOverriding()) {
        //省略异常代码
    }
    else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
        //省略异常代码
    }
    else if (!beanDefinition.equals(oldBeanDefinition)) {
        //提示覆盖log信息
    }
    else {
        //提示覆盖log信息
    }
    //覆盖原有的Beandefinition
    this.beanDefinitionMap.put(beanName, beanDefinition);
}

4、beanDefinitionMap不存在名为beanName的Beandefinition

代码语言:javascript
复制
//检查bean的创建阶段是否已经开始,也就是说是否已经创建了
if (hasBeanCreationStarted()) {
    //Cannot modify startup-time collection elements anymore (for stable iteration)
    // 无法修改启动时间收集元素(用于稳定迭代)(译注)
    //注册过程需要保证数据的一致性,所有需要加锁同步
    synchronized (this.beanDefinitionMap) {
        //注册到beanDefinitionMap中
        this.beanDefinitionMap.put(beanName, beanDefinition);
        //下面就是将当前beanName存放到beanDefinitionNames中
        List<String> updatedDefinitions = new ArrayList<String>(
        this.beanDefinitionNames.size() + 1);
        updatedDefinitions.addAll(this.beanDefinitionNames);
        updatedDefinitions.add(beanName);
        this.beanDefinitionNames = updatedDefinitions;
        //如果单例模式的bean名单中有该bean的name,那么移除掉它。
        //也就是说着,将一个原本是单例模式的bean重新注册成一个普通的bean
        if (this.manualSingletonNames.contains(beanName)) {
            Set<String> updatedSingletons = new
            LinkedHashSet<String>(this.manualSingletonNames);
            updatedSingletons.remove(beanName);
            this.manualSingletonNames = updatedSingletons;
        }
    }
}
// 仍处于启动阶段,bean还没有开始注册
else {
    // Still in startup registration phase
    this.beanDefinitionMap.put(beanName, beanDefinition);
    this.beanDefinitionNames.add(beanName);
    this.manualSingletonNames.remove(beanName);
}
this.frozenBeanDefinitionNames = null;

5、执行缓存清除

  • 1:oldBeanDefinition如果存在,且执行到了这里也没有抛出异常,说明该BeanDefinition已经被覆盖,缓存需要更新。
  • 2:如果是单例模式的bean对象则Set中包含该beanName,执行到这里说明该BeanDefinition已经从一个单例模式的bean变为了一个普通的bean,所以缓存也需要更新。
代码语言:javascript
复制
if (oldBeanDefinition != null || containsSingleton(beanName)) {
    resetBeanDefinition(beanName);
}

OK,我们来看下resetBeanDefinition这个方法:

这个方法的作用就是重置给定bean的所有bean定义缓存,包括从它派生的bean的缓存。

代码语言:javascript
复制
protected void resetBeanDefinition(String beanName) {
    // 如果已经创建,则删除给定bean的合并bean定义。
    clearMergedBeanDefinition(beanName);

    // 如果有的话,从singleton 高速缓存中删除相应的bean。
    //但是这也不是必须的,而只是为了覆盖上下文的默认bean
    //(就是从manualSingletonNames中移除)
    destroySingleton(beanName);
    //递归的方式来 重置具有给定bean作为父项的所有bean定义。
    for (String bdName : this.beanDefinitionNames) {
        if (!beanName.equals(bdName)) {
            BeanDefinition bd = this.beanDefinitionMap.get(bdName);
            if (beanName.equals(bd.getParentName())) {
                resetBeanDefinition(bdName);
            }
        }
    }
}

Bean的注册就到这里了,下一篇学习的是DefaultListableBeanFactory这个集大成者容器。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2018-02-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 磊叔的技术博客 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档