前言
上篇文章介绍了Bean元信息的配置与解析过程,限于篇幅Bean注册过程就没展开。
这里主要围绕 BeanDefinitionReaderUtils#registerBeanDefinition 展开分析下Bean注册过程
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
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 的几个重要的成员属性
|
1 2 3 4 5 6 |
// 这个实质上就是IoC容器中Bean的载体,没错 它很重要,但它是无序的 private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>( 256 ); //它代表了bd名称的集合,它是有序的 遵循bd注册的顺序 private volatile List<String> beanDefinitionNames = new ArrayList<>( 256 ); // 这是已创建bd名称的集合,在doGetBean方法根据beanName创建Bean时,beanName会被加到此集合中 private final Set<String> alreadyCreated = Collections.newSetFromMap( new ConcurrentHashMap<>( 256 )); |
上面两个属性都比较重要,两者结合使用的话可以实现bd的顺序访问(其实就是遍历beanDefinitionNames集合时,使用beanDefinitionMap去获取bd)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
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<String> 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<String> 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信息的代码逻辑
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
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的注册,再来看别名的注册 发现流程比较清晰,基本上一目了然。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
//注意 这里的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。
这部分源码如下
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
//name可以是beanName,也可以是alias public 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; } |
总结
好了,这篇主要分析了BeanDefinition的注册,顺带着也说了别名的注册情况。既然BeanDefinition已经注册完成,那紧接着就是BeanDefinition的实例化过程了,这个放到下次分析吧。
本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注的更多内容!
原文链接:https://blog.csdn.net/zyxwvuuvwxyz/article/details/123217602
查看更多关于Spring Bean生命周期之Bean的注册详解的详细内容...