转自:http://www.spring4all.com/article/1289
首先,我先列个大纲目录,简明扼要的说明一下这一篇文章要写的内容是啥
- 迷宫入口的宏观结构
- 入口的背后
- 容器中获取一个bean的背后
迷宫入口的宏观结构
嗯,迷宫,迷宫的入口是什么?标题说的很明确,是使用Annotation的方式来注册和注入,那么我分析的入口就是AnnotationConfigApplicationContext
,那么入口有了,但是并不准备直接从这个迷宫走进去。
Spring结构还是清晰有序中透着复杂,直接闯像我这种闯迷宫已经好多次的“老司机”到是能来去自如,但是新司机怕不是要晕头转向,所以明人不说暗话,我要看图,没图说个XX
不要慌,虽然结构复杂,但只关注主要的,其它的也就自行不攻而破所以不慌!
已知ApplicationContext
一个顶层的Context
接口,然后它的顶层是继承的BeanFactory
,BeanFactory
是什么它主要负责什么?我这里就简而言之,它就是一个Bean工厂的接口规范,通过实现这个接口外部可以从工厂中获取一个bean
接下来我们再来说一下HierarchicalBeanFactory
和ListableBeanFactory
HierarchicalBeanFactory
简而言之就是一个用来层次化工厂的,其中就主要定义了2个接口方法getParentBeanFactory
和containsLocalBean
,方法名就很顾名思义,如果还是不懂没关系,这个类对于本文来说不重要
ListableBeanFactory
也简单描述吧,首先这个接口定义的接口方法大体范围意义就是获取BeanDefinition
一些根据Annotation和根据类型获得一些Bean类型属性,那所谓的BeanDefinition
又是个什么鬼东西?那这里先来简单的介绍一下,无论你是从XML配置文件中使用<bean name="...." class="...">
还是使用注解的方式注册一个Bean都会被当做一个BeanDefinition
存储起来
不多哔哔,接下来再来看ApplicationContext
作为一个顶层的Context接口总还是会有牌面的定义一些东西的,里面啊首先就是一堆context的属性获取当前context的id啦,什么ApplicationName啦,启动时间啦,获得父Context啦,但里面定义了getAutowireCapableBeanFactory
(突然严肃),这个工厂也是个接口里面定义着配置Bean、创建Bean、销毁Bean和自动装配,简单点来说它就是一个定义着创建bean和注入bean的一个工厂接口,这样说OK嘛?里面还有一点细节我就不补充了
往下走,我们再来稍微关注一下ConfigurableApplicationContext
接口,这个接口顾名思义就是一个定义了配置ApplicationContext
的接口里面定义了些许接口方法,但暂时并不需要关注那么多,记住refresh
和getBeanFactory
方法即可,现在不同这2个方法是干啥用的都没关系,你先记住,待会你就知道了。
接下来越来越近了,终于看到了一个不是接口的抽象类AbstractApplicationContext
,这个类细节很多,但不用慌,只需要关注我本文所提及即可,仅从本文的角度出发,该抽象类实现了BeanFactory
的getBean
方法,而这些getBean
的实现内容都是通过getBeanFactory().getBean(...)
来实现的,并且实现了一个refresh
方法,请一定要记住这里实现了refresh
,它里面实现了啥,都先不要管,记住就行,本文后面会回到这个方法来的。
接着是GenericApplicationContext
我们仅关注主要的(与本文相关的),该类会在实例化的时候就会创建一个DefaultListableBeanFactory
的beanFactory
属性存起来,之前定义个getBeanFactory
实际上返回的就是此处的beanFactory
,其中还有一个关键的方法就是registerBeanDefinition
实际上也是调用的beanFactory
的registryBeanDefinition
它的实现细节不管,马上就要介绍了,因为接下来就是入口的背后小节了
入口的背后
正式发车,首先来看下如何使用长什么样子?
ApplicationContext applicationContext = new AnnotationConfigApplicationContext("base packages..");
applicationContext.getBean("....")
大致就是这样玩的吧,首先很关键在new AnnotationConfigApplicationContext
时发生了什么
先简要说明各自的作用我们在细入从中,所谓从宏观到微观,可能微观中又透入着宏观,道法自然,顺气自然。
首先this()
,调用了自身的无参构造函数
实例化了2个属性,只需要关注scanner
属性即可它是扫描ClassPath下的BeanDefinition的
接着是scan
方法
这个方法就是去调用了scanner.scan
方法将外部传入的要扫面的basePackage
给传了进去,接着来看下scanner.scan
方法
其中也只要关注doScan
方法即可,它才是幕后主使
首先老套路遍历传入的basePackages,挨个搜索,暂时只需要关注findCandidateComponents
它会根据遍历的每个basePackage去定位出所有需要注册的BeanDefinition来简单看下(这里的原理是直接解析字节码class文件)
没什么好说的直接看scanCandidateComponents
直接聚焦到这个方法中最核心的2段来看下,就是由这里来判断是否有要注册的BeanDefinition
而inlucdeFilters
中,在该类实例化时就会被默认的添加Component
注解,所以默认在这里所有Component
注解都会被注册
this.scanner = new ClassPathBeanDefinitionScanner(this);
isConditionMatch(metadataReader) 方法用来支持spring4新增的条件化配置
接着回来剩下的那一句判断就更加简单了
最后一个判断看方法名都能看出来,就不用再多解释了吧。。。还是简单解释一下吧(皮这一下很开心),首先类必须不是一个接口其次可以被子类重写,通过条件的就会被add进去最后返回,
再度回到doScan
然后在此处获得beanName
然后将拿到的所有BeanDefinition
封装成BeanDefinitionHolder
,其实这里也是封装了一下,将beanName
和alias
还有beanDefinition
封装了进去最后再调用applyScopedProxyMode
这里我们这样使用默认是使用的NO
所以直接返回没什么好说的(PS: 其实我是想说一下的,这里还是有点道道可以说的,日后有机会再说吧对于这里暂时不重要~~)
最后在doScan
中调用registerBeanDefinition
完成注册
我们接着来看这个registerBeanDefinition
干了什么
实际上还是调用了AnnotationConfigApplicationContext
的registerBeanDefinition
方法再把BeanDefinition
传递了过去,我们回到AnnotationConfigApplicationContext
来看
registerBeanDefinition
,你会发现并木有,还记得它是继承什么类来着吗?没错GenericApplicationContext
它里面定义了而它又是调用beanFactory
的registerBeanDefinition
方法所以直接看beanFactory
的registerBeanDefinition
方法就可以了
其实,GenericApplicationContext内部默认的beanFactory就是DefaultListableBeanFactory
别的不管专注于这三句即可,实际上就是定义了一个Map
一个List
将它存储起来,scan
分析完了,有没有发现那里不对现在还只是个BeanDefinition
不是一个实例啊不能注入啊有木有,当然了因为还没有分析完,我们在回到AnnotationConfigApplicationContext
这里来
因为还有一个refresh
方法没有讲啊,它这里也是直接调用AbstractApplicationContext
中的refresh
方法,从本文的前文中就一直提醒读者记住这个方法,说之前先已知一件事情,就是在默认情况下所有的bean
实例化都是单例的和非懒加载,OK现在就是时候来说一下它了,它可是一个至关重要的方法
里面调用了很多个方法来完成这件事,实际上refresh
方法就是初始化容器用的它为IOC容器准备了Bean生命周期的管理条件听不懂没关系,我们只关注它其中很小的部分,还记得我说的已知嘛?时候贴代码了
只关注这一句即可,实例化所有单例的非懒加载的bean,迷宫快走到头了,来我们来看看它究竟是何方神圣
不听不听,我们只关注beanFactory.preInstantiateSingletons()
即可,继续来看这个beanFactory.preInstantiateSingletons
做了什么
实际上这里调用的就是scan
注册的beanDefinitionNames
有木有啊,然后将它挨个循环出来然后调用getBean
他这里调用了它的父类AbstractBeanFactory
的getBean
然后在继续来看doGetBean
里干了啥,代码很长只用关注必要的即可
这个匿名内部类又调用了本类(也就是DefaultListableBeanFactory实例)的createBean(beanName, mbd, args); 方法,这个方法其实定义在AbstractAutowireCapableBeanFactory 抽象类中(因为DefaultListableBeanFactory继承了AbstractAutowireCapableBeanFactory)
还记得我们已知的条件吗?bean
默认的作用域是单例的,所以看这里,这里实际上是先去调用了AbstractAutowireCapableBeanFactory
的createBean
方法,同理我们也只需要关注这其中需要关注的点即可
注意了,这个方法里面的细节还是比较多的,我仅简要说明让大家理解即可
接着先看它的createBeanInstance
方法,直接看到它的最后一行,一般来说也是最常用的,使用无参构造函数来创建一个实例
接着来看instantiateBean
做了啥
此处就是获取一个实例化策略,然后在调用instantiate
来进行实例化,这里我们还是已最常用最简单的SimpleInstantiationStrategy
的instantiate
来说
这里实际上会去调用BeanUtils.instantiateClass
另一种CGLIB的方式在简单的实例化策略中是不支持的
接着来看BeanUtils.instantiateClass
没什么好说的,就是直接通过反射创建实例,接着继续回来
createBean
之后会调用的一个getSingleton
方法,并将创建的bean
实例传递进去,这个方法比较长,直接看关键点
对于新的实例都会调用一个addSingleton
方法来看下
实际上很简单有木有,就是还是定义了一个Map将实例化的对象存储了起来,再继续回到doGetBean
来最后将bean
返回回去,简单的来说doGetBean
如果实例是单例的,如果没有被实例化过就会帮你实例化一遍,否则就直接获取返回
OK容器的里bean的初始化都已经ok了,那我Autowire
的对象是如何注入的呢?实际上在调用doCreateBean
中有一个关键的点就是这一段if (earlySingletonExposure)
会将所依赖的还未初始化的bean
先给拿到并进行优先初始化,然后再通过populateBean
去填充我们所要注入的value
,OK,很简单的几句话就把注入的思路给说明白了,如果要具体到里面的细节又是好多道道,这里我已经把源码中注入的思路写明白了,具体细节自个去看吧
接下来最后一个小节
容器中获取一个bean的背后
这个小节还用写么,不就是调用getBean
?
applicationContext.getBean("xxxx")
还不懂的自个去复看第二个小节~~~
为了更好的理解,我还写了一个炒鸡简单的实现了一个Spring的demo,有兴趣的可以点我了解一下,顺手可以给个star
本文完