SpringIOC底层源码解析2

106 阅读6分钟

上文说到Spring bean初始化的原理,今天我们来彻底走到Spring源码来解读一下

Spring有一个BeanDefnition,而我们程序员可以修改它,怎么来扩展,我们开发者需要提供一个类实现一个叫BeanFactoryPostProcessor的接口


这个方法会传一个beanFactory回来,刚刚上篇说过在调用这个方法的回调的时候会把这个map传过来,这个map在这个工厂里面,这个工厂是什么,就是所谓的Spring的工厂,这个beanFactory工厂里面就包含这个map,拿到这个工厂后就会得到这个map信息,走入这个方法beanFactory.getBeanDefinition()


看到上图this.beanDefinitionMap.get(beanName);

就说明这个map存在工厂里面

我们调用这个工厂的API,这个工厂API底层实际上是调用map的API


BeanDefinition是个对象,所以我用BeanDefinition对象来接收它,只不过BeanDefinition是个接口是个父类,它的API不丰富,所以我们用子类来接收它,因为子类比父类更强大,上面有各种的API,因为接下来用到的API在父类中并没有

比如说setBeanClass这个API在父类中没有,在子类中被扩展了,所以尽量用子类

indexService.getBeanClass();毋庸置疑,获取的肯定是indexService,我们现在不拿,indexService.setBeanClass(userServcie);这时候整个map就改变了,改变之后为了让这个类生效,把这个类加一个注解@Component,一旦这个类生效,这个方法就会被调用,一旦这个方法被调用,改变了整个工厂bean的行为,那么改变行为的话,整个bean工厂就不存在indexService


我们来看一下源码


进入这个方法


然后进入这个实例化方法refresh

invokeBeanFactoryPostProcessors(beanFactory);

这个方法是完成所谓的扫描,dubug调式一下可以看到spring容器中什么都不认识只有有一个AppConfig类认识,是因为这个类是手动给Spring的,然后往下走,当完成扫描后会有一个indexService

Spring什么时候开始初始化bean的

finishBeanFactoryInitialization(beanFactory);

可以dubug调式一下当走完这个方法后,如果bean一旦实例化之后,这个构造方法就会打印,当走完这个方法后,构造方法打印了,证明这个方法是实例化bean,所谓的Sping bean的生命周期都在这个方法中,然后进入这个方法里调试一下就行了



beanFactory.preInstantiateSingletons();

这个方法实例化单例,非lazy的对象

进入这个方法就会看到之前说到的list的作用


这个可以看到通过list拿到所有bean的名字

然后循环list从map中拿到所有的beanDefinition

Spring会验证是否是单例非lazy对象

当验证一切通过认为可以new会调用getBean方法进行实例化普通的bean

进入这个方法


然后看到调用doGetBean,进入这个方法


第一行代码是验证是否有非法字符串的,就是这个bean名字是否有非法字符串,一般没有非法字符串,验证完成之后它会把这个名字返回给你,然后它就可以拿bean

Object sharedInstance = getSingleton(beanName);

这个方法背后的意义就是Spring从单例池中获取一遍,看看这个单例池中看这个bean有没有实例化


进入这个getSingleton方法可以看到调用这个getSingleton,进入这个方法


这个singletonObjects就是一个map,进入这个方法


可以看到英文注释大致意识是缓存我们的单例对象

说白了以前我们老师可能一直说的Spring容器,就是Spring单例bean初始化之后放到一个map中,就是这个singletonObjects,其实这样说不对,在Spring中有个专业的术语叫做单例的缓存池或者叫单例池

然后往下走


这个方法是判断这个类是不是在创建过程中

肯定不走这个方法,然后继续往下走

然后继续验证,当验证是否为单例,因为是单例所有走这个方法


这个会调用getSingleton方法会调用createBean创建bean,进入createBean这个方法

当进入这个createBean中还没有创建对象,因为刚才已经开始完成所有的验证,当验证完成之后Spring才开始创建对象,这也是我们所说的Spring生命周期的开始,其实前面说的扫描也是Spring bean的生命周期

然后我们进入这个createBean方法里,开始进行判断日志,然后把BeanDefinition

赋值给它

然后通过BeanDefinition对象获取bean的类型,我们通过BeanDefinition里面存的beanClass获取

Class<?> resolvedClass = resolveBeanClass(mbd, beanName);

我们可以通过BeanDefinition对象获取bean的类型,dubug可以看到resolvedClass

获取的是indexService因为这是一个class,这是一个BeanDefinition,我们通过BeanDefinition里面存的beanClass这个属性得到一个类,因为需要创建对象肯定要类,这是毋庸置疑的,当我们得到这个类后,判断这个beanClass有没有name之类的,这个先不管,都是验证的,继续往下走

怎么说明Spring是支持循环依赖的

Spring在生命周期的某个过程中,Spring在初始化Spring bean的过程中,当它把我们的 bean实例化之后,它会做一次判断当前这个容器允不允许循环依赖,怎么判断是根据一个属性的,allowCircularReferences

这个属性在Spring中默认是ture,这个属性可以通过Spring提供的API供我们修改为false,所以我们不修改Spring是默认开启循环依赖的

怎么关闭Spring的循环依赖

可以在refresh方法之前把allowCircularReferences设为false就可以关闭循环依赖

还有一种

beanFactory.setAllowCircularReferences(false);

设置allowCircularReferences为false


其实这样写没有用

//初始化spring容器
AnnotationConfigApplicationContext ac =       
 new AnnotationConfigApplicationContext(AppConfig.class);

因为这里已经初始化Spring容器了,已经支持了循环依赖了

怎么解决

如果你对Spring非常了解的话,还有一种写法

ac.regisater(AppConfig.class)把这个类传给ac.regisater方法

在手动调用一下ac.refresh();

为什么你这样做呢,可以进入AnnotationConfigApplicationContext

这个方法看一下


这个方法本质是调用默认构造方法,调用register,调用refresh

这个方法可以手动做了,首先调用默认的构造方法,然后调用register方法,然后调用refresh方法

refresh方法真正来初始化Spring环境最主要的代码

这样是不是可以在refresh方法之前把

AbstractAutowireCapableBeanFactory beanFactory = 
(AbstractAutowireCapableBeanFactory) ac.getBeanFactory();
beanFactory.setAllowCircularReferences(false);

这个方法写上


这样就可以关闭Spring的循环依赖


一共三种方法

1.修改源码

2.通过上述方法,在调用register之后在调用refresh设置allowCircularReferences为false

3.通过扩展Spring

为什么别人可以进阿里巴巴,是因为别人对Spring源码比较深入,这样都是有原因的,这些就是解决Spring循环依赖