Spring注解的实现原理

Spring注解的实现原理其实分很多种情况,我们这里不概括所有的通用的流程,而是结合具体的注解来具体分析。


一,@Configuration注解的实现原理


上图:

图片发自简书App

下面文字解读一下大体流程:

第一步:Spring容器初始化时,会注册一个后置处理器,解析@Configuration注解的后置处理器是ConfigurationClassPostProcessor。

第二步:Spring容器初始化时,当执行refresh()方法时,会调用第一步注册的后置处理器ConfigurationClassPostProcessor。

第三步:ConfigurationClassPostProcessor后置处理器借助ConfigurationClassParser完成配置类解析。

第四步:ConfigurationClassParser配置类解析过程中会完成嵌套注解,迭代解析扫描包下面的class类的@Component注解的解析。

第五步,注册bean到spring容器。


二,AnnotationConfigApplicationContext注解处理器实现原理


AnnotationConfigApplicationContext注解处理器是Spring boot提供的一个比较常用的注解处理器。我们先看看它的工作原理。


第一步:初始化读取器AnnotationBeanDefinitionReader


第二步:初始化扫描器AnnotationBeanDefinitionScanner


第三步:调用父类GenericApplicationContext的无参构造方法初始化一个BeanFactory。这里初始化使用的是DefaultListableBeanFactory。


第四步:注册Bean配置类


第五步:刷新上下文


图片发自简书App


这里注册Bean配置类是关键,也是最复杂的一块,我们来单独过一遍。


三,AnnotationConfigApplicationContext注册Bean详细流程


AnnotationConfigApplicationContext注册Bean的具体实现是下面这行代码:

this. reader. register(annotatedGlasses);

这里的reader就是我们上面提到的读取器AnnotatedBeanDefinitionReader,register(annotatedGlasses)方法的具体实现就在这个读取器中。

接下来我们分步骤来看一下register方法的具体实现细节。


第一步:解析Bean注解信息。AnnotationConfigApplicationContext使用AnnotationGenericBeanDefinition来解析Bean的注解信息。解析完成以后得到一个AnnotationGenericBeanDefinition对象abd。


第二步:解析Bean作用域scope及scopeProxyMode。

使用默认的Bean作用域处理器ScopeMetaDataResolver解析得到Bean的scope信息。默认情况下,作用域是singleton,scopeProxyMode是NO。也就是说这个Bean是单例的,并且不使用代理模式。如果我们通过Scope注解指定了作用域的话,则使用我们指定的作用域。

不同的作用域,在解析注解,注册Bean的时候并没有区别。但是在生成Bean的时候就有区别了,以singleton为例,会先判断是否已经生成过实例,如果已经生成过,就不再生成,而是直接返回。


第三步:解析beanName。

这里有个beanName的处理细节在这里说一下。如果我们通过注解的方式指定了beanName,则直接使用我们指定的beanName。如果我们没有指定beanName,Spring会获取class的名字,然后生成一个beanName。


第四步,解析其他Bean的注解信息。

借助AnnotationConfigUtils工具类解析注解的其他可选属性。

主要包括lazyInit属性、primary属性、dependsOn属性、role属性、description属性。


第五步:BeanDefinitionHolder

BeanDefinitionHolder可以看做是对Bean的注解信息的再次封装。为什么要用BeanDefinitionHolder再次封装注解信息呢?我们来看一下BeanDefinitionHolder都做了什么。

首先,增加了一个属性aliases,这是一个字符串数组。

然后,会判断是否需要生成scope作用域的代理,由于默认是不生成作用域代理,直接把BeanDefinitionHolder对象返回了。也就是说仅仅是加了一个属性aliases。

经过BeanDefinitionHolder的处理以后,bean的定义信息结构如下:

beanName

BeanDefinition对象

aliases字符串数组

最后,带着这个BeanDefinitionHolder对象,开始执行Bean的注册。


第六步:注册Bean主流程。

这个流程是关键点,也是最复杂的。

首先,注册的关键类就是这个BeanDefinitionRegistry。这是一个接口,实现了AliasRegistry接口。BeanDefinitionRegistry接口定义了7个方法,都是对BeanDefinition的操作,包括注册Bean,删除Bean等一系列操作。这里我们重点关注注册Bean的方法registerBeanDefinition()。

registerBeanDefinition()方法进来,首先会进行一个校验,这个校验是一个合法性校验。为什么要做这个校验,目前我没研究清楚,有清楚的不妨留言交流,不胜感激。

校验完成以后,接着会查询beanDefinitionMap,如果不存在,接下来才会注册bean。如果存在,会有一系列的逻辑判断,因为我们注入的bean通常不会执行这段逻辑,这里略过。

接下来就是注册Bean了,注册Bean有三个操作。

第一个操作:把当前需要注册的Bean插入beanDefinitionMap,这个map就是Spring存放Bean的容器。这个map的实现是ConcurrentHashMap,初始容量是256。

键值对的key是beanName,value是beanDefinition。

第二个操作:把beanName插入beanDefinitionNames,beanDefinitionNames是一个ArrayList容器,初始容量也是256。

第三个操作:把当前这个Bean从manualSingletonNames容器中移除。manualSingletonNames是一个LinkedHashSet容器,初始容量是16。


OK,到这里,Bean的注册就算结束了。


注册完Bean以后,还有一些其他细节,比如,把frozenBeanDefinitionNames这个数组置为空。至于为什么,目前我还没有研究清楚。但是这些不影响我们对主流程的把控。这些细节,稍后会再次研究,然后另起一篇文章来说明,这里不再赘述。



四,疑惑


DefaultListableBeanFactory定义了下面2个容器,作用是什么呢?

1,allBeanNameByType

2,singletonBeanNameByType

这2个容器的实现是ConcurrentHashMap,初始容量为64。

这个问题我们后续研究,大家有兴趣,也可以自己研究一下。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,122评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,070评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,491评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,636评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,676评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,541评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,292评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,211评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,655评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,846评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,965评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,684评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,295评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,894评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,012评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,126评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,914评论 2 355

推荐阅读更多精彩内容

  • 本来是准备看一看Spring源码的。然后在知乎上看到来一个帖子,说有一群**自己连Spring官方文档都没有完全读...
    此鱼不得水阅读 6,934评论 4 21
  • Spring容器高层视图 Spring 启动时读取应用程序提供的Bean配置信息,并在Spring容器中生成一份相...
    Theriseof阅读 2,812评论 1 24
  • 老峰野竹笋, 荒原苦菜花。 枯枝干十指, 颤颤轻采拔。 大山老阿婆, 独身深林中。 四儿高堂坐, 何人问关怀? 集...
    jdmq阅读 310评论 0 0
  • 书读的越多而不加思索,你就会觉得你知道得很多;而当你读书而思考得越多的时候,你就会越清楚地看到,你知道的还是很少。
    行走的教育阅读 141评论 0 0
  • 恍惚间2017年已在不知不觉中到了该说再见的时候了,也不知道这一年是中了魔鬼的诅咒,还是上帝懒于打理,天空是灰...
    千帆不渡仙阅读 462评论 9 3