## Spring-Core核心逻辑源码分析

<a></a>初始化容器

  1. 服务器容器根据配置文件(Tomcat:web.xml)加载ContextInitialListener事件:ContextLoaderListener.contextInitialized();
  2. 尝试获得parentContext;
  3. 根据parentContext [初始化ApplicationContext](#Initial ApplicationContext);
  4. ApplicationRoot为key,将context注册到servletContext中;
  5. 如果当前线程的ClassLoader为ContextClassLoader,则将context设置为currentContext;否则将context设置为perThreadContext;

<a name='Initial ApplicationContext'>初始化ApplicationContext</a>

  1. 通过BeanUtil.instantiateClass()来创建ConfigurableWebApplicationContext;
  2. 设置context的id、parent、servletContext等属性(不晓得Id有什么用,代码还费了好几行获得best possible id😂);
  3. 调用ctx.refresh()方法[构建Spring容器](#start up Spring)构建Spring容器.

通过refresh方式初始化容器而不是init的方式,而且refresh中有通过beanFactory.getBean方式获取的对象,初始化阶段BeanFactory中是没有BeanDefinition的,因此这里我怀疑和tomcat的热部署有关系。

<a name='start up Spring'>构建Spring容器</a>

  1. 根据信号startupShutdownMonitor来作同步,在同步代码块中进行refresh操作;

Spring注释对startupShutdownMonitor有如下解释:
Synchronization monitor for the "refresh" and "destroy".
即destroy方法必须等refresh方法执行完之后才能被调用;

  1. prepareRefresh():准备刷新,这里有个同步设置变量active,没看懂啥意思;
  2. obtainRefreshBeanFactory():[初始化](#init beanFactory)并获取BeanFactory;
  3. prepareBeanFactory():初始化BeanFactory的依赖,包括ignoreDependency和硬代码registerDependency;Ignore掉的依赖通常是由别的方式进行注入,比如BeanFactory对象可以由BeanFactoryAware方式注入;获取并维护系统变量和环境变量
  4. invokeBeanFactoryPostProcessor():根据BeanFactory是否为BeanDefinitionRegistry来调用BeanFactoryPostProcessor,再根据priority、order、nonOrder的顺序调用BeanPostProcessor。在服务器初始化阶段并没有注册BeanFactoryPostProcessor,也许在热部署阶段会执行吧;也就是说,BeanFactoryPostProcessor是在BeanFactory初始化之后Bean对象初始化前执行的
  5. registerBeanPostProcessor():根据BeanFactory.getBeanNamesForType()获取BeanPostProcessor,然后以Priority、Order、NonOrder的顺序注册到BeanPostProcessorList中。BeanPostProcessorList为ArrayList,因此执行顺序为:Priority>Order>NonOrder;
  6. initMessageSource():获取MessageSource对象,默认为DelegatingMessageSource;
  7. 接下来是注册广播事件监听器、调用子类的onRefresh方法、注册应用监听器;
  8. finishBeanFactoryInitialization():初始化所有not-lazy-init Bean。也是SpringCore的核心代码
  9. finishRefresh:触发加载完成事件;

<a name='init beanFactory'>初始化BeanFactory</a>

  1. 清空已加载的Bean;
  2. 创建一个内部BeanFactory;
  3. 自定义BeanFactory配置:setAllowBeanDefinitionOverriding、setAllowEagerClassLoading、setAllowCircularReferences、setAllowRawInjectionDespiteWrapping
  4. loadBeanDefinitions():遍历Resources中的Xml定义的bean加载至BeanDefinition;

<a name='loadBeanDefinition'>Bean加载</a>

  1. 根据Resource生成Document对象;根据Document的Element的nodeName属性来处理Bean:
    • 如果nodeName为Import,则执行importBeanDefinitionResource操作加载import-resource定义的bean:
    • 如果nodeName为Alias,则将BeanName与Alias关联注册;
    • 如果nodeName为Bean,则根据属性解析Element生成BeanHolder,将BeanName与BeanDefinition注册到BeanDefinitions缓存中;
    • 如果nodeName为Beans,则跳至1.处理Beans中的Bean;

<a name='spring-core'>核心代码</a>

  1. 1.finishBeanFactoryInitialization->preInstantiateSingletons():同步获取缓存中的beanDefinitionNames,放到局部变量中;
  2. 遍历beanDefinitionName,调用[getBean()](#get bean)初始化Bean;
<a name = 'get bean'>getBean()</a>
  1. 首先尝试从singletonObjects、earlySingletonObjects的缓存中获取对象;
  2. 如果对象不为空,则调用getObjectForBeanInstance()方法通过FactoryBean或者对象本身的方式获得Bean对象;
  3. 如果对象不为空,则尝试在ParentBeanFactory中获得对象,并返回;
  4. 如果ParentBeanFactory对象中没有获得Bean对象,则读取BeanDefinistions缓存中获得该Bean的Definition对象;
  5. 拿到BeanDefinition对象后,遍历该对象的dependsOn属性,并将该BeanName与DependsOn的对象Name注册dependentBeanMap中,然后调用[getBean()](#get bean)来实例化DependsOn的对象。这里会根据dependentBeanMap来检查是否存在循环依赖,(Spring对Bean属性之间的循坏依赖是有解决方案的,不清楚这里的循坏依赖为什么会直接抛出异常);
  6. 实例化DependsOn对象后,再根据Bean的Scope属性创建对象,Singleton方式和prototype方式的区别在于Singleton对象在创建的时候首先会在缓存中取对象,如果不存在再createBean()创建一个新的对象放到singletonObjects和earlySingletonObjects中,而prototype类型的则不不会在缓存中取对象,直接创建新对象.
  7. getObjectForBeanInstance()
    :拿到Bean对象之后,确定Bean是否为FactoryBean,如果是则需要通过FactoryBean的方式获取bean,否则直接返回bean.
<a name='createBean'>createBean()</a>
  1. 在初始化前,首先调用resolveBeanClass()确保beanClass已经加载到ClassLoader里面;这里会去看BeanDefinition中是否有指定的class,如果没有,则会调用ClassUtils.forName()在ClassLoader容器中加载class;
  2. 确保class已经加载完成之后,执行resolveBeforeInstantiation(),尝试通过InstantiationAwareBeanPostProcessor来创建Bean,如果Bean创建成功,则调用applyBeanPostProcessorsAfterInitialization()应用Bean后处理器,并返回Bean对象;(InstantiationAwareBeanPostProcessor这个里面虽然可以返回null,但是返回null之后,Spring容器会认为没有创建bean,重新创建bean)
  3. 如果InstantiationAwareBeanPostProcessor返回null,调用doCreateBean()真正的创建Bean对象.(os:创建个对象前戏真长😴)
<a name='doCreateBean'>doCreateBean()</a>
  1. 通过createBeanInstance()创建Bean实例。该方法首先在resolvedConstructor的一系列变量中找到缓存的Constructor,否则找到一个FastConstructor(找最合适的过程比较冗长,这里省略这么多字),再根据是否有OverrideMethod,来选择通过Reflect的形式还是CGlib的形式创建对象;
  2. Bean实例化出来之后,调用applyMergedBeanDefinitionPostProcessors()来执行BeanPostProcessor;
  3. 这时候属性还是空的,在注入属性时会发生循环依赖的问题,因此这里会判断是否允许循环依赖,如果允许的话将bean的引用通过ObejctFactory工厂的方式暴露出去;
  4. populateBean(),遍历并且注入依赖。
  5. initializeBean(),调用Bean的初始化方法,init啦,afterPropertiesSet啦。


额外补充:实例Bean是什么时候变成Proxy的呢?

通过doCreateBean()的步骤1.出来的实例这个时候还是对象本身,而Spring暴露出来的Bean又都是以代理形式出现的,那么Spring是什么时候对实例进行代理wrap的呢,通过Debug看到在applyMergedBeanDefinitionPostProcessors()阶段,
AbstractAutoProxyCreator以BeanPostProcessor的方式对实例出来的Bean进行AutoProxy,通过判断该实例的Class对象是否有Interfaces,如果有则通过JDK Dynamic的方式代理,否则使用CGlib.

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

推荐阅读更多精彩内容