[Spring系列]-IoC源码分析与手动实现-2

130 阅读12分钟

引言

本篇文章继续写一下我对spring-ioc的理解,记录并方便以后回顾,同样会结合自己的仿spring项目来展示一些代码/伪代码。文章会重点关注以下几个问题。

  1. ioc中的各层职责分离是如何做的?
  2. 循环依赖

文章会侧重于循环依赖,会对循环依赖的概念,解决所使用的三级缓存,以及每一级缓存的作用这几个问题来进行分析。


ioc中各层的职责分离是怎么做的?

ioc简而言之就是框架去管理bean的容器,此处的管理包括:读取程序员配置的bean,创建这些bean,提供给程序员找到这些bean的接口。在此基础之上ioc容器会实现一些方便程序员开发的特性,如注解支持,循环依赖等等。

这些功能其实都可以用一个applicationcontext去完成,但为了框架的解耦合和灵活性,以及为了方便后续的维护和升级,还有提供一定的可读性,spring中用到了很多抽象,继承,设计模式等等。其中最经典的就是spring中工厂模式的使用。

context

ApplicationContext由BeanFactory派生出来,BeanFactory中的功能由ApplicationContext来具体实现,例如refresh,getBean,以及refresh中的核心方法。ApplicationContext翻译就是应用上下文,也可以理解为IoC容器本身,ApplicationContext就是一个完整的IoC。 context的设计还用到了比较经典的模板方法模式,抽象context里定义了核心流程,比如refresh的流程。实现类则去做具体实现,比如前面一直在说的ClassPathXmlApplicationContext,以及后面mvc会提到的web相关的context。

factory

上面所说的管理,其实核心的抽象就是BeanFactory,它的核心功能就是对外提供bean相关的创建,获取,销毁等能力。bean相关的体系架构如下,核心的是BeanFactory,定义BeanDefinition,存储BeanRegistry,Bean相关的一些工具和属性propertyvalue,constructargumentvalue,beanwrapperimpl,propertyeditor等等,以及bean延伸出的beanpostprocessor等。bean-core的继承关系如下

image.png

顶层的BeanFactory接口,它提供的就是基础的bean操作能力,其下有ListableBeanFactory,可以理解为其Factory中的Bean有作为集合来管理的特性,以及ConfigurableBeanFactory,其具备的处理bean间依赖以及bean处理器的特性,还有之前提到过的AutowireCapableBeanFactory,其就具备通过autowired支持的beanpostprocessor去处理当前bean,当然这是在下面的集成里做的。然后简单展示一下顶层的这些interface的代码。

 public interface BeanFactory {
     Object getBean(String name) throws BeansException;
     boolean containsBean(String name);
     boolean isSingleton(String name);
     boolean isPrototype(String name);
     Class<?> getType(String name);
 }
 public interface ListableBeanFactory extends BeanFactory {
     boolean containsBeanDefinition(String beanName);
     int getBeanDefinitionCount();
     String[] getBeanDefinitionNames();
     String[] getBeanNamesForType(Class<?> type);
     <T> Map<String, T> getBeansOfType(Class<T> type) throws BeansException;
 }
 public interface AutowireCapableBeanFactory  extends BeanFactory{
     Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
         Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
 }
 public interface ConfigurableBeanFactory extends BeanFactory,SingletonBeanRegistry {
     void addBeanPostProcessor(BeanPostProcessor beanPostProcessor);
     int getBeanPostProcessorCount();
     void registerDependentBean(String beanName, String dependentBeanName);
     String[] getDependentBeans(String beanName);
     String[] getDependenciesForBean(String beanName);
 }

registry

以及BeanRegistry,BeanRegistry字面意思就是bean注册中心,提供了bean的存储,查询等能力。通过DefaultSingletonBeanRegistry对其能力进行具体实现,同时也提供了registry的容器。以及后续会使用到的FactoryBeanRegistrySupport,也是在此registry的基础上,提供了bean创建bean的能力(aop)。而抽象beanfactory则继承了registry让工厂具有容纳bean的能力。这样bean的存储与管理就完成了闭环。

 public interface SingletonBeanRegistry {
     void registerSingleton(String beanName, Object singletonObject);
     Object getSingleton(String beanName);
     boolean containsSingleton(String beanName);
     String[] getSingletonNames();
 }
 public class DefaultSingletonBeanRegistry  implements SingletonBeanRegistry {
     //这里只展示一下全局的容器
     protected List<String> beanNames=new ArrayList<>();
     //bean name -> obj
     protected Map<String, Object> singletonObjects =new ConcurrentHashMap<>(256);
     //bean name -> bean dependentBeans
     protected Map<String,Set<String>> dependentBeanMap = new ConcurrentHashMap<>(64);
     protected Map<String,Set<String>> dependenciesForBeanMap = new ConcurrentHashMap<>(64);
 }

一切的整合

接着再往下看就是ConfigurableListableBeanFactory和AbstractAutowireCapableBeanFactory,以及再往下的DefaultListableBeanFactory。CongigurableListAbleBeanFactory就是对顶层的bean能力再做一个整合。

 public interface ConfigurableListableBeanFactory extends ListableBeanFactory, AutowireCapableBeanFactory, ConfigurableBeanFactory 

AbstractAutowireCapableBeanFactory则是在基础beanfactory的基础上提供了Autowired的特性,也就是为已经实例化的obj做属性装配的工作,目前的简单实现就是基于BeanPostProcessor去做的,可以通过在bean真正放入容器前为其做装配。最最后面就是集大成体的DefaultListableBeanFactory,此时的它具有多个beanfactory被定义的能力,有beanregistry的能力,是一个完善的bean工厂。以上就是bean的一个完整的工厂体系。而这完整的工厂在ioc本身也就是context中会使用到。

java语言设计中,一个interface代表的是一种能力or一类特性,通过interface将这些特性去抽离、分隔,达到互不干扰的效果,随取随用,这个就是interface的精髓所在。我们想让某个类拥有某种能力,就去实现这个interface,随意组合不互相干扰,这就是设计原则中的interface segregation接口隔离原则。


循环依赖中的多级缓存是否有必要?

这部分与上篇文章有一定重合。写着一部分的目的主要是面试中经常会问到,而且我本身也对spring中的三级缓存也有了解不够透彻的地方。在这我先提出我自己的几个问题:spring中的循环依赖是啥?spring是如何解决循环依赖的?网上常说的三级缓存的含义?是否真的有必要去搞这“三”级缓存?

什么是循环依赖

循环依赖,顾名思义:依赖之间形成闭环。这一部分会解析一下spring如何去应对循环依赖的,当然,这并不意味着循环依赖都是可以解决的,循环依赖本身是代码设计不合理的产物,日常开发中我们需要避免循环依赖的出现,spring6之后也默认关闭了循环依赖的解决。

下图是我认为能生动表达循环依赖的图示,Aservice依赖Bservice,Bservice依赖Cservice,C又转头依赖A,这里就出现了“循环”。这会导致什么问题呢?spring在ioc启动的时候会加载我们的beanDefinition,同时为其注入依赖,当我们在getA的时候,我们的AutowiredAnnotationBeanPostProcessor会起作用(也可以是通过xml配置ref的方式,这里以autowired句举例),去寻找A中依赖的B,而getB时又会去找C,找C时又会返回去找A,但A还在等待B的创建,并不存在在我们registry中的map里,所以会形成一个死等的状态。

图片.png

循环依赖的解决方案

乍一看这个和死锁很类似,好像根本没有办法解决,但实际上这里说的这些service,在jvm中就是一个对象,一块内存,而它其中的autowired依赖,其实也就是一个引用。回顾一下bean创建的过程。

flowchart LR
getBean --> createBean
newLines["create obj
handler cons
populate args"]
createBean --> newLines
newLines -- obj --> autowired_processors
autowired_processors -- find other depends --> getBean

我们早在createBean阶段就已经得到了这个bean的obj,然后才会去执行beanpostprocessor。如果我们在C去getA时,想办法拿到createBean阶段的A,并将其注入,不就解决了死等的问题了吗,然后再回想一下,这里的A确实还没有被创建完,但是它迟早会被我们创建(注入应有的属性),C里存放的只是对他这段内存的引用,后面这个引用就是一个完整体A,因此通过这种思路是可以解决循环依赖问题的。

那么接下来就是找到这个中间的obj,也可以叫做“毛胚Bean”,spring是将其存放在什么位置的。spring是通过一个叫earlySingletonObjects的map去存放毛胚Bean的,具体的做法就是在createBean后,我们会将这个毛胚bean注册到earlymap中,这样后面C再返回来找A时,如果从singletons里找不到A,就会继续从earlySingletons里找,这时候就能找到A,也就完成了属性的注入。如下图所示。

图片.png 当然,这种方式只能解决属性注入的循环依赖,如果是构造器注入是无法解决的。

三级缓存

上一部分介绍了spring解决循环依赖的思路。在spring的源码里,一共有三个map,分别是

 //来自spring3.0.x
 /** Cache of singleton objects: bean name --> bean instance */
 private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>();
 /** Cache of singleton factories: bean name --> ObjectFactory */
 private final Map<String, ObjectFactory> singletonFactories = new HashMap<String, ObjectFactory>();
 /** Cache of early singleton objects: bean name --> bean instance */
 private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>();

但我们上面提到的方案里只用到一个map,因此我一开始觉得两级缓存已经可以解决所有的循环依赖问题。之后在网络上看见了这样一个说法:spring的第三级缓存singletonFactories是为了应对aop出现的。我觉得这里讲的比较清晰的是spring中文网的解析。aop这部分后面也会更新,这里就不从头讲aop的原理了。

当遇到aop时,以下面这张图为例,我们getA时拿到a其实是一个proxy,a里面包含了我们真实的real-a以及代理的内容,在bean创建后的某个时机会做代理生成,这个生成的after-proxy-a才是我们真正需要的a,但是前面已经将proxy放入early了,b获取到的a其实是proxy-a,而不是真正的被代理后的after-proxy-a。

image.png 为了解决这个问题,spring引入了singletonFactories这个cache,先说结论,这个cache的目的和上面说的early-cache不一样,它主要是为了将aop的过程提前。具体的流程如下:在create proxy-a时,首先判断一级缓存中是否存在,如果不存在,则向三级缓存中添加一个name->lambda,并且移除掉二级缓存中的kv,之后给A的属性进行赋值,进入创建B的流程,B创建时同样,往三级中先放入了name->lambda,然后属性赋值,这时,会从三级缓存中找到A的存在,将这里A的代理生成出来,放入二级缓存,同时删除三级缓存的数据,这样B就完成了创建,然后回到A,A中的B也创建好了,接这后续的流程替换A。

三级缓存的源码展示与解析

该部分主要分析org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean这个方法。

首先看一下外层doGetBean的逻辑(对原型模式和else做了省略,对一些打log部分也做了省略)

 protected <T> T doGetBean(
     final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly) throws BeansException {
     final String beanName = transformedBeanName(name);
     // 这个就是最后get的bean
     Object bean;
     // 将singletonFactory中的bean提前加载出来,当在其他bean创建时,依赖的bean就可以从这里触发
     Object sharedInstance = getSingleton(beanName);
 
     // 对aop做特殊处理
     if (sharedInstance != null && args == null)
         bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
     // 第一次创建,会进入这个分支
     else {
         BeanFactory parentBeanFactory = getParentBeanFactory();
         // 如果beanDefinition中不存在这个bean的定义,忽略
         if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {}
         final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
         // 对dependsOn的bean处理,省略
         // 单例情况下的处理方式
         if (mbd.isSingleton()) {
             // 再次getSingleton,这里会传入一个createBean的factory,进入createBean流程
             sharedInstance = getSingleton(beanName, new ObjectFactory() {
                 public Object getObject() throws BeansException {
                     return createBean(beanName, mbd, args);
                 }
             });
             // 对aop做处理
             bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
         }
     }
     return (T) bean;
 }

然后看一些createBean的逻辑

 protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
     if (instanceWrapper == null) {
         // step1
         instanceWrapper = createBeanInstance(beanName, mbd, args);
     }
     addSingletonFactory(beanName, new ObjectFactory() {
         public Object getObject() throws BeansException {
             return getEarlyBeanReference(beanName, mbd, bean);
         }
     });
     Object exposedObject = bean;
     try {
         // step 2
         populateBean(beanName, mbd, instanceWrapper);
         if (exposedObject != null) {
             // step 3 这里会完成aop代理
             exposedObject = initializeBean(beanName, exposedObject, mbd);
         }
     }
     return exposedObject;
 }

这是上面的getSingleton的逻辑,注意这里的getSingleton有个重载方法,getSingleton(name, factory);

 protected Object getSingleton(String beanName, boolean allowEarlyReference) {
     Object singletonObject = this.singletonObjects.get(beanName);
     if (singletonObject == null) {
         synchronized (this.singletonObjects) {
             singletonObject = this.earlySingletonObjects.get(beanName);
             if (singletonObject == null && allowEarlyReference) {
                 // 目前来看,这里是将singletonFactory中的bean提前加载出来,放入二级缓存
                 ObjectFactory singletonFactory = this.singletonFactories.get(beanName);
                 if (singletonFactory != null) {
                     singletonObject = singletonFactory.getObject();
                     this.earlySingletonObjects.put(beanName, singletonObject);
                     this.singletonFactories.remove(beanName);
                 }
             }
         }
     }
     return (singletonObject != NULL_OBJECT ? singletonObject : null);
 }
 public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
     synchronized (this.singletonObjects) {
         Object singletonObject = this.singletonObjects.get(beanName);
         if (singletonObject == null) {
             // 在单例对象创建前先做一个标记
             beforeSingletonCreation(beanName);
             
             boolean newSingleton = false;
             boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
             if (recordSuppressedExceptions) {
                 this.suppressedExceptions = new LinkedHashSet<>();
             }
             try {
                 // 上游传入的lambda在这里会被执行,进入createBean方法
                 singletonObject = singletonFactory.getObject();
                 newSingleton = true;
             }
             if (newSingleton) {
                 // 添加至singletonObjects中
                 addSingleton(beanName, singletonObject);
             }
         }
         return singletonObject;
     }
 }

然后结合上面创建AB的实际过程看一下spring是如何解决循环依赖的。首先getA时会进入getSingleton这个方法,显然为null,然后进入下面的getSingleton(name, factory) 这个方法,这个方法主要做了以下几件事:1.标记这个A的创建。2.执行上面的lambda,也就是进入createBean流程。3.移除标记和添加到一级缓存,代表完成创建。在A的创建过程中,我们进入了createBean流程,首先完成beanInstance的创建,接着将bean包装成一个factory加入三级缓存,然后为A进行属性注入,这时会发现B的存在,然后重复getB的过程,又在B的依赖中发现了A,接着再回到getA的过程,这时回到我们的第一种getSingleton,这里的A是之前addSingletonFactories时放入的lambda表达式创建的,这个factory是通过getEarlyBeanReference提前暴露的对象

 protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
     Object exposedObject = bean;
     // 中间这部分逻辑取决于这个bean的beanpostprocessors
     if (bean != null && !mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
         for (BeanPostProcessor bp : getBeanPostProcessors()) {
             if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                 SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
                 exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
                 if (exposedObject == null) {
                     return exposedObject;
                 }
             }
         }
     }
     return exposedObject;
 }

那么哪些beanPostProcessor是这个Smart的实现?如下

也就是说,如果有代理的bean会在这里执行代理,如果没有则直接返回。然后回到B的创建,此时B通过这个lambda创建了A,移除三级缓存放入二级缓存,完成B的创建,再回到A的创建流程。在A完成了initializeBean之后,会有一个earlySingletonExposure的判断,在这里会将A替换成创建B时创建的那个代理A,此时A也完成了get的过程。

上面的流程图大概是这样

图片.png