千家信息网

Spring Bean中Bean的注册是什么

发表于:2026-11-01 作者:千家信息网编辑
千家信息网最后更新 2026年11月01日,这篇文章主要介绍"Spring Bean中Bean的注册是什么",在日常操作中,相信很多人在Spring Bean中Bean的注册是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希
千家信息网最后更新 2026年11月01日Spring Bean中Bean的注册是什么

这篇文章主要介绍"Spring Bean中Bean的注册是什么",在日常操作中,相信很多人在Spring Bean中Bean的注册是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答"Spring Bean中Bean的注册是什么"的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

前言

这里主要围绕BeanDefinitionReaderUtils#registerBeanDefinition展开分析下Bean注册过程

public static void registerBeanDefinition(                        BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)                        throws BeanDefinitionStoreException {                // Register bean definition under primary name.                String beanName = definitionHolder.getBeanName();                registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());                // Register aliases for bean name, if any.                String[] aliases = definitionHolder.getAliases();                if (aliases != null) {                        for (String alias : aliases) {                                registry.registerAlias(beanName, alias);                        }                }        }

上面无论是注册bd还是建立alias-beanName之间的关系,均用到了BeanDefinitionRegistry,因此我们就以它为突破口来展开

BeanFactory的继承体系

对图中常用接口或类进行说明:

  • ListableBeanFactory 集合类型BeanFactory 提供一种可以查找所有Bean实例的能力

    • getBeanNamesForType(Class) 根据类型去查找Bean名称列表不会强制Bean的初始化,可从源码中看出来

    • getBeansOfType(Class) 根据类型去查找Bean实例列表,会强制Bean的初始化,可从源码中看出来

    • getBeanNamesForAnnotation(Class) 根据注解类型获取Bean名称列表

    • getBeansWithAnnotation(Class) 根据注解类型获取Bean实例列表

    • findAnnotationOnBean(String,Class) 根据指定名称+标注类型获取Bean实例

  • Hierarchical([ˌhaɪəˈrɑːkɪkl])BeanFactory 层次性BeanFactory,有父子容器的概念,可在ConfigurableListableBeanFactory设置其父容器

    • getParentBeanFactory() 获取父容器

    • boolean containsLocalBean(String name) 在当前容器中查找是否存在该名称的Bean实例

  • SingletonBeanRegistry 单实例BeanFactory,与单实例有关

  • ConfigurableBeanFactory 可配置的BeanFactory,这个一般不用于应用程序,是给其他BeanFactory扩展用的。的确,定义了很多配置方法

  • ConfigurableListableBeanFactory 可配置的集合类型的BeanFactory

  • AutowireCapableBeanFactory 提供具有自动装配能力的BeanFactory

透过继承体系可以看出,BeanDefinitionRegistry的实现类是DefaultListableBeanFactory,该类同时实现了诸多接口,可谓是BeanFactory中集大成者,因此我们到DefaultListableBeanFactory中阅读下bd注册及别名注册的源码

Bean的注册

先来分析下DefaultListableBeanFactory的几个重要的成员属性

// 这个实质上就是IoC容器中Bean的载体,没错 它很重要,但它是无序的private final Map beanDefinitionMap = new ConcurrentHashMap<>(256);//它代表了bd名称的集合,它是有序的 遵循bd注册的顺序private volatile List beanDefinitionNames = new ArrayList<>(256);// 这是已创建bd名称的集合,在doGetBean方法根据beanName创建Bean时,beanName会被加到此集合中private final Set alreadyCreated = Collections.newSetFromMap(new ConcurrentHashMap<>(256));

上面两个属性都比较重要,两者结合使用的话可以实现bd的顺序访问(其实就是遍历beanDefinitionNames集合时,使用beanDefinitionMap去获取bd)

public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)                        throws BeanDefinitionStoreException {                //对beanName、bd进行非空验证                Assert.hasText(beanName, "Bean name must not be empty");                Assert.notNull(beanDefinition, "BeanDefinition must not be null");                //如果bd是AbstractBeanDefinition类型,则对bd进行验证(一般情况下 我们场景的bd都是继承自AbstractBeanDefinition的)                if (beanDefinition instanceof AbstractBeanDefinition) {                        try {                           //bd验证                                ((AbstractBeanDefinition) beanDefinition).validate();                        }                        catch (BeanDefinitionValidationException ex) {                                //省略异常代码                        }                }                //从beanDefinitionMap根据beanName取bd                BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);                //如果beanName名称的bd已经存在                if (existingDefinition != null) {                        //如果不允许Bean被重新注册 则抛出异常,这里默认值是true                        if (!isAllowBeanDefinitionOverriding()) {                                throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);                        }                        //如果已被注册bd的角色值小于当前待注册bd的角色值                         else if (existingDefinition.getRole() < beanDefinition.getRole()) {                                // 省略日志输出                        }                        //如果已注册的同名bd 与本次注册的bd不相同                        else if (!beanDefinition.equals(existingDefinition)) {                                //省略日志输出                        }                        else {                                //省略日志输出                        }                        //将beanName-bd键值对放入beanDefinitionMap集合                        this.beanDefinitionMap.put(beanName, beanDefinition);                }                else {                        //流程走到这里 说明在beanDefinitionMap中不存在同名bd                        //条件成立 说明alreadyCreated不为空 即有bd已被创建                        if (hasBeanCreationStarted()) {                                // 如果在此之间 有bean正在被创建 则这里进行加锁处理                                synchronized (this.beanDefinitionMap) {                                    //将beanName-bd键值对放入beanDefinitionMap集合                                        this.beanDefinitionMap.put(beanName, beanDefinition);                                        //将beanName添加到beanDefinitionNames集合中                                        List updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);                                        updatedDefinitions.addAll(this.beanDefinitionNames);                                        updatedDefinitions.add(beanName);                                        this.beanDefinitionNames = updatedDefinitions;                                        //如果beanName是手动注册的单例Bean名称,则更新manualSingletonNames                                        if (this.manualSingletonNames.contains(beanName)) {                                                Set updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);                                                //这里从集合中删除的原因个人理解:                                                //manualSingletonNames记录的是在registerSingleton时被添加的单实例beanName,而这里注入的不是单实例Bean。因为manualSingletonNames包含了此beanName,因此需要剔除                                                updatedSingletons.remove(beanName);                                                this.manualSingletonNames = updatedSingletons;                                        }                                }                        }                        else {                                //如果没有bean在被创建                                //将beanName-bd键值对放入beanDefinitionMap集合                                this.beanDefinitionMap.put(beanName, beanDefinition);                                //将beanName添加到集合中                                this.beanDefinitionNames.add(beanName);                                //这里从manualSingletonNames中剔除,个人理解为拖地操作,毕竟若集合中没有此beanName 也remove不了                                this.manualSingletonNames.remove(beanName);                        }                        //这个集合表示冻结配置时缓存的beanName集合,暂时未理解透此集合的用途                        this.frozenBeanDefinitionNames = null;                }                //如果已存在同名bd或已存在同名的单例对象,则重置所有已被缓存的同名的bd数据,因此这里bd注册成功后,肯定后续还会再生成Bean的                if (existingDefinition != null || containsSingleton(beanName)) {                        resetBeanDefinition(beanName);                }        }

其实分析下来发现Bean注册的过程还是比较容易理解的,下面试着总结一下:

  • 若bd未被注册过,则将bd信息存入BeanDefinitionMap等集合中

  • 若bd已被注册过,允许覆盖注册的情况下,将bd信息存入BeanDefinitionMap等集合中,并清除已被缓存的同名bd信息

下面看一下清除bd信息的代码逻辑

protected void resetBeanDefinition(String beanName) {                // 如果此bd属于被合并的BeanDefinition,则这里将其从MergeBeanDefinition集合中剔除                clearMergedBeanDefinition(beanName);                // 如果已存在同名的单例对象 则销毁,具体细节先不展开                destroySingleton(beanName);                // 这里for循环逻辑与MergeBeanDefinition相关,如果存在MergedBeanDefinitionPostProcessor,则重置此bd                for (BeanPostProcessor processor : getBeanPostProcessors()) {                        if (processor instanceof MergedBeanDefinitionPostProcessor) {                                ((MergedBeanDefinitionPostProcessor) processor).resetBeanDefinition(beanName);                        }                }                // BeanDefinition运行有层级的,如果此bd拥有多个父级bd,那么这里递归地重置其父bd                for (String bdName : this.beanDefinitionNames) {                        if (!beanName.equals(bdName)) {                                BeanDefinition bd = this.beanDefinitionMap.get(bdName);                                if (beanName.equals(bd.getParentName())) {                                        resetBeanDefinition(bdName);                                }                        }                }        }

alias别名的注册

看了Bean的注册,再来看别名的注册 发现流程比较清晰,基本上一目了然。

//注意 这里的name 不要具象为beanName,虽然我们是从建立beanName--alias关系出发追溯到这里的public void registerAlias(String name, String alias) {                //对name、alias进行断言验证                Assert.hasText(name, "'name' must not be empty");                Assert.hasText(alias, "'alias' must not be empty");                synchronized (this.aliasMap) {                        //如果别名与beanName相同,那别名就没有必要存在了,因此选择直接从this.aliasMap中移除此别名                        if (alias.equals(name)) {                                this.aliasMap.remove(alias);                                //省略日志输出                        }                        else {                           //从aliasMap中根据别名获取name                                String registeredName = this.aliasMap.get(alias);                                if (registeredName != null) {                                    //如果已存在的registeredName与此此要注册的name一致,那就没必要注册了                                        if (registeredName.equals(name)) {                                                return;                                        }                                        //流程走到这里,说明同一个别名,对应两个name,如果不允许alias覆盖 则抛出异常                                        if (!allowAliasOverriding()) {                                                //省略异常及日志输出                                }                                //这里对alias进行循环检查,避免出现A的别名是B,B的别名是A的情况                                checkForAliasCircle(name, alias);                                //将alias--name 放入aliasMap                                this.aliasMap.put(alias, name);                                //省略日志输出                        }                }        }

alias与beanName的映射关系,为根据名称查找Bean又提供了一种思路。就是说除了根据beanName外,也可以根据alias去查找Bean。

这部分源码如下

//name可以是beanName,也可以是aliaspublic String canonicalName(String name) {                //局部变量赋值                String canonicalName = name;                // Handle aliasing...                String resolvedName;                do {                    //如果从aliasMap中能根据alias分析出beanName                        resolvedName = this.aliasMap.get(canonicalName);                        if (resolvedName != null) {                                canonicalName = resolvedName;                        }                }                while (resolvedName != null);                // 无论入参name是beanName还是alias,这里返回的都应该是beanName了                return canonicalName;        }

到此,关于"Spring Bean中Bean的注册是什么"的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注网站,小编会继续努力为大家带来更多实用的文章!

0