[Spring]BeanDefinitionRegistry-BeanDefinition注册

BeanDefinitionRegistry

Spring中BeanDefinition的注册接口,常见的实现有DefaultListableBeanFactoryGenericApplicationContext。看看它的接口清单:

public interface BeanDefinitionRegistry extends AliasRegistry {
    // 往注册表中注册一个BeanDefinition实例
    void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
            throws BeanDefinitionStoreException;
    // 从注册表中移除带有该BeanName的BeanDefinition
    void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
    // 通过beanName从注册表中获取BeanDefinition
    BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
    // 判断当前是否已经注册了该beanName的BeanDefinition
    boolean containsBeanDefinition(String beanName);
    // 获取当前注册的所有BeanDefinition的Name
    String[] getBeanDefinitionNames();
    // 获取当前已注册的BeanDefinition数量
    int getBeanDefinitionCount();
    // beanName(标识)是否被占用
    boolean isBeanNameInUse(String beanName);
}

UML

UML
  • SimpleBeanDefinitionRegistry:BeanDefinitionRegistry较为简单的实现,仅提供注册能力,不提供工厂级别的能力,可作为简单的例子提供给读者做test使用.
  • DefaultListableBeanFactory:Spring最早可以独立运行的工厂类,不仅有容器注册功能,还是一个功能健全的Bean工厂。
  • GenericApplicationContext:Spring通用上下文,需要较多的自定义实现。

DefaultListableBeanFactory的注册功能

前程回顾

上文中提到,BeanDefinitionReader在调用完BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);后,会调用BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());方法对该BeanDefinition进行注册.

DefaultListableBeanFactory会将注册后的BeanDefinition放到一个ConcurrentHashMap的Map中进行存储,Key为beanName,Value为BeanDefinition。

private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

注册的入口

这里传入了两个参数,bdHolder为BeanDefinition的包装器,getReaderContext().getRegistry()为DefaultListableBeanFactory。为什么是DefaultListableBeanFactory?因为Spring在实例化XmlBeanDefinitionReader的时候将容器本身作为参数进行了传递,这在设计模式上也可以称为委托。

BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());

DefaultListableBeanFactory的多职能

DefaultListableBeanFactory本身不仅作为工厂使用,而且还可以用作注册器,资源加载器。

    protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
        // Create a new XmlBeanDefinitionReader for the given BeanFactory.
        // 这里就是委托的体现,将DefaultListableBeanFactory作为构造参数传入了XmlBeanDefinitionReader中
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

        // Configure the bean definition reader with this context's
        // resource loading environment.
        beanDefinitionReader.setEnvironment(this.getEnvironment());
        beanDefinitionReader.setResourceLoader(this);
        beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

        // Allow a subclass to provide custom initialization of the reader,
        // then proceed with actually loading the bean definitions.
        initBeanDefinitionReader(beanDefinitionReader);
        loadBeanDefinitions(beanDefinitionReader);
    }

进入到构造函数中,发现DefaultListableBeanFactory在这里已经充当了registry的职能了.

    public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {
        super(registry);
    }
注册器实例

注册BeanDefinition-注册name和aliases

    public static void registerBeanDefinition(
            BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
            throws BeanDefinitionStoreException {

        // Register bean definition under primary name.
        // 向容器注入BeanDefinition的名称和BeanDefinition本身
        String beanName = definitionHolder.getBeanName();
        registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

        // Register aliases for bean name, if any.
        // 如果该BeanDefinition存在别名,再对别名进行注册
        String[] aliases = definitionHolder.getAliases();
        if (aliases != null) {
            for (String alias : aliases) {
                registry.registerAlias(beanName, alias);
            }
        }
    }

DefaultListableBeanFactory#registerBeanDefinition

注意,这里已经到达了DefaultLististableBeanFactory的registerBeanDefinition中了,上面的registry指向的是DefaultLististableBeanFactory.这里梳理一下主要的逻辑

    1. 判断是否为AbstractBeanDefinition的实例,如果是,校验该BeanDefinition是否有声明需要overrides的方法,如果有,创建代理类进行代理。
    1. beanDefinitionMap中尝试获取该beanName的BeanDefinition.
    1. 如果beanDefinitionMap已经有了,检测是否允许BeanDefinition覆盖。如果允许,则用put进行更新操作.
    1. 如果从beanDefinitionMap获取不到,证明是新的BeanDefinition,查看工厂的bean是否进入已创建的阶段了,如果是,则证明是动态注册bean的方式.使用synchronizedbeanDefinitionMap上锁,让进行put操作,再依次更新beanDefinitionNames和从manualSingletonNames(手动注册单例的名称列表,按注册顺序。)中将该beanName移除
    1. 如果该BeanDefinition已存在,并且在单例缓存中,那么进行resetBeanDefinition,这是一个递归的过程,主要的工作是:清除合并的BeanDefinition缓存,从单例缓存中将该BeanName移除,通知处理器进行reset操作.
    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");
        // 判断该beanDefinition是否为AbstractBeanDefinition的实例
        if (beanDefinition instanceof AbstractBeanDefinition) {
            try {
                // 校验look up 和 replace method 是否存在并且参数合法,如果存在,对该BeanDefinition进一步处理
                ((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覆盖,容器配置
            if (!isAllowBeanDefinitionOverriding()) {
                throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
            }
            else if (existingDefinition.getRole() < beanDefinition.getRole()) {
                // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
                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 + "]");
                }
            }
            // 将beanName作为key,BeanDefinition作为value,放到容器map中
            this.beanDefinitionMap.put(beanName, beanDefinition);
        }
        else {
            // 是否开始创建bean实例,如果已经有该实例,那么本次为动态的形式
            if (hasBeanCreationStarted()) {
                // Cannot modify startup-time collection elements anymore (for stable iteration)
                // 锁住注册表
                synchronized (this.beanDefinitionMap) {
                    // 更新beanDefinition
                    this.beanDefinitionMap.put(beanName, beanDefinition);
                    // 创建一个长度为beanDefinitionNames+1的列表
                    List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
                    // 先将存量的beanDefinitionName存储
                    updatedDefinitions.addAll(this.beanDefinitionNames);
                    // 再存储增量的beanName,顺序存储
                    updatedDefinitions.add(beanName);
                    // 更新整个BeanDefinitionNames
                    this.beanDefinitionNames = updatedDefinitions;
                    // 从单例BeanName列表中移除该beanName,如果this.manualSingletonNames中contains该beanName,执行remove操作
                    removeManualSingletonName(beanName);
                }
            }
            else {
                // Still in startup registration phase
                // 启动阶段
                this.beanDefinitionMap.put(beanName, beanDefinition);
                this.beanDefinitionNames.add(beanName);
                removeManualSingletonName(beanName);
            }
            // 将注册期间冻结的beanName列表清除,一个volatile修饰的String数组,Spring内存优化操作
            this.frozenBeanDefinitionNames = null;
        }
        // 检测是否存在该BeanDefinition且存在于IOC中
        if (existingDefinition != null || containsSingleton(beanName)) {
            // 重置注册的BeanDefinition缓存
            // 包括BeanDefinition的父类以及合并的BeanDefinition缓存,即mergeBeanDefinition
            // Spring会把有parent属性的Bean属性进行合并
            resetBeanDefinition(beanName);
        }
        else if (isConfigurationFrozen()) {
            clearByTypeCache();
        }
    }

DefaultListableBeanFactory#resetBeanDefinition

    protected void resetBeanDefinition(String beanName) {
        // Remove the merged bean definition for the given bean, if already created.
        // 如果该beanName发生过merge操作,remove已经合并出的beanDefinition
        clearMergedBeanDefinition(beanName);

        // Remove corresponding bean from singleton cache, if any. Shouldn't usually
        // be necessary, rather just meant for overriding a context's default beans
        // (e.g. the default StaticMessageSource in a StaticApplicationContext).
        // 从单例缓存中remove该beanName
        destroySingleton(beanName);

        // Notify all post-processors that the specified bean definition has been reset.
        // 通知MergedBeanDefinitionPostProcessor,这个特殊的beanDefinition已经进行了重置
        for (BeanPostProcessor processor : getBeanPostProcessors()) {
            if (processor instanceof MergedBeanDefinitionPostProcessor) {
                ((MergedBeanDefinitionPostProcessor) processor).resetBeanDefinition(beanName);
            }
        }

        // Reset all bean definitions that have the given bean as parent (recursively).
        // 找出容器中与当前beanName不相同的bd,查看paretName是否跟当前beanName相同。
        // 即父bean被清除了,子bean也应该要重置
        for (String bdName : this.beanDefinitionNames) {
            if (!beanName.equals(bdName)) {
                BeanDefinition bd = this.beanDefinitionMap.get(bdName);
                // Ensure bd is non-null due to potential concurrent modification
                // of the beanDefinitionMap.
                if (bd != null && beanName.equals(bd.getParentName())) {
                    // 递归调用,如果该bean还有子bean,继续进行清除
                    resetBeanDefinition(bdName);
                }
            }
        }
    }

流程图

Registry

总结

  • BeanDefinition的注册具体表现为往DefaultListableBeanFactory中的beanDefinitionMap中添加一个BeanDefinition.
  • BeanDefinitionReader将注册功能委托给了DefaultListableBeanFactory进行注册.该注册器在初始化的时候进行了构造注入.
  • 在注册期间,Spring会校验是否允许BeanDefinition覆盖.
  • Spring支持在工厂创建Bean实例后进行动态注册BeanDefinition.
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,108评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,699评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,812评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,236评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,583评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,739评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,957评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,704评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,447评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,643评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,133评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,486评论 3 256
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,151评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,889评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,782评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,681评论 2 272

推荐阅读更多精彩内容