Spring IOC原理

1、IOC的理解

IOC也叫控制反转,是将对象的创建和管理交给spring来做。

了解spring IOC,先了解几个相关类:

(1)IOC容器-BeanFactory

BeanFactory是IOC容器的顶层接口,定义一些基础功能,ApplicationContext是IOC容器的高级接口,功能更多,如资源的访问(XML文件、Java配置类)等;他们都是负责生产和管理bean的工厂。

(2)Bean和BeanDefinition

BeanDefinition是spring对bean的定义信息,spring将XML配置文件或注解解析成BeanDefinition,然后根据BeanDefinition可以实例化为Bean。

spring的bean有两种:一种是普通bean,一种是工厂bean(FactoryBean),FactoryBean可以生成并返回某一个类型的bean实例,可以借助于它自定义Bean的创建过程,它在spring框架的一些组件中常用。

(3)后置处理器

spring提供了两种后置处理器:BeanPostProcessor和BeanFactoryPostProcessor。

  • BeanPostProcessor是针对bean级别的处理器,如果一个类实现了BeanPostProcessor,默认对spring容器中所有的bean进行处理。可以通过参数对某个bean进行处理。第一个参数是bean实例,第二个参数是bean的name。

注意:BeanPostProcessor的两个方法分别是在初始化方法(init-method)前后执行,也就是在bean实例化和依赖注入之后执行。

其实常用的@PostConstruct注解也是利用BeanPostProcessor实现的,InitDestroyAnnotationBeanPostProcessor 后置处理器的 postProcessBeforeInitialization() 方法会判断每个Bean是否有方法被@PostConstruct注解标注,标注了就执行。(也就是@PostConstruct也只对使用了它的bean生效)

public interface BeanPostProcessor {
    @Nullable
    default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
​
    @Nullable
    default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
}
  • BeanFactoryPostProcessor是针对BeanFactory级别的处理。

@FunctionalInterface
public interface BeanFactoryPostProcessor {
    void postProcessBeanFactory(ConfigurableListableBeanFactory var1) throws BeansException;
}

此接口只提供了一个方法,方法参数为ConfigurableListableBeanFactory,根据该参数的getBeanDefinition()可以获取BeanDefinition对定义的bean属性进行修改。

注意:调用 BeanFactoryPostProcessor 方法时,这时候bean还没有实例化,此时 bean 刚被解析成 BeanDefinition对象。

(4)初始化Bean—InitializingBean

此接口只有一个方法,在初始化方法init()执行前调用

public interface InitializingBean {
    void afterPropertiesSet() throws Exception;
}

注意:BeanPostProcessor 是对容器所有Bean都会执行,也就是只要有接口实现了BeanPostProcessor,就会作用于所有Bean;InitializingBean 不一样,它只作用于实现了该接口的Bean

2、bean的生命周期

  • 实例化:这个阶段就是Spring加载XML配置文件或扫描注解,将类解析为BeanDefinition,然后通过反射机制创建Bean对象的过程。

  • 依赖注入:实例化完成之后,Spring就会调用Bean对应的setXxx()方法、或者构造方法给相应的属性赋值。

  • 初始化:在Bean实例化完成并且依赖注入之后,就会调用Bean的初始化方法,进行一些额外的处理操作,默认初始化方法一般叫做【init()】。

  • 使用阶段:处于这个阶段的Bean对象,就可以真正的被使用啦。

  • 销毁阶段:当某个Bean对象不再被使用时候,此时会首先调用销毁方法(默认销毁方法我们习惯叫做【destory()】),用于释放一些系统资源,然后将Bean对象进行垃圾回收

               

3、spring源码

3.1、spring IOC容器启动主流程

spring IOC初始化的关键环节在 AbstractApplicationContext#refresh() 方法中。

@Override
  public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
      // 第1步:刷新前预处理.
      prepareRefresh();
​
      // 第2步:获取BeanFactory,默认实现是DefaultListableBeanFactory;加载BeanDefition 并注册到 BeanDefitionRegistry.
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
​
      // 第3步:BeanFactory的预准备工作(BeanFactory进行一些设置,比如context的类加 载器等).
      prepareBeanFactory(beanFactory);
​
      try {
        // 第4步:BeanFactory准备工作完成后进行的后置处理工作.
        postProcessBeanFactory(beanFactory);
​
        // 第5步:实例化并调用实现了BeanFactoryPostProcessor接口的Bean.
        invokeBeanFactoryPostProcessors(beanFactory);
​
        // 第6步:注册BeanPostProcessor(Bean的后置处理器),在创建bean的前后等执 .
        registerBeanPostProcessors(beanFactory);
​
        // 第7步:初始化MessageSource组件(做国际化功能;消息绑定,消息解析).
        initMessageSource();
​
        // 第8步:初始化事件派发器.
        initApplicationEventMulticaster();
​
        // 第9步:子类重写这个方法,在容器刷新的时候可以自定义逻辑.
        onRefresh();
​
        // 第10步:注册应用的监听器。就是注册实现了ApplicationListener接口的监听器Bean.
        registerListeners();
​
        /** 
        第11步:初始化所有剩下的非懒加载的单例bean 初始化创建非懒加载方式的单例Bean实例(未设置属性).
        填充属性 
        初始化方法调用(比如调用afterPropertiesSet方法、init-method方法) 调用BeanPostProcessor(后置处理器)对实例bean进行后置处        
        **/
        finishBeanFactoryInitialization(beanFactory);
​
        // 第12步:完成Context的刷新。主要是调用LifecycleProcessor的onRefresh()方法,并且发布事件(ContextRefreshedEvent).
        finishRefresh();
      }
      。。。。。
    }

3.2、bean的创建:

finishBeanFactoryInitialization(beanFactory)—>

DefaultListableBeanFactory类的preInstantiateSingletons()方法—>

AbstractBeanFactory类的getBean(String beanName),普通bean和工厂bean都是通过getBean创建的—>

AbstractBeanFactory类的doGetBean(String beanName)—>

AbstractAutowireCapableBeanFactory类的createBean(String beanName)—>doCreateBean(String beanName)

在doCreatBean()中:创建Bean实例、属性填充、调用初始化方法、执行后置处理器

 // 创建Bean实例,但是还未设置属性
    if (instanceWrapper == null) {
      instanceWrapper = createBeanInstance(beanName, mbd, args);
    }
    。。。
    //初始化Bean
    Object exposedObject = bean;
    try {
      //属性注入
      populateBean(beanName, mbd, instanceWrapper);
      //调用初始化方法,执行BeanPostProcessor后置处理器
      exposedObject = initializeBean(beanName, exposedObject, mbd);
    }

再看下初始化方法中的具体操作 initializeBean()

Object wrappedBean = bean;
    //执行 BeanPostProcessor 的 postProcessBeforeInitialization
    if (mbd == null || !mbd.isSynthetic()) {
      wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    }
​
    try {
      //执行初始化方法,如果Bean实现了 InitializingBean,会先调用其 afterPropertiesSet()方法
      //再调用初始化方法 init()
      invokeInitMethods(beanName, wrappedBean, mbd);
    }
    //执行 BeanPostProcessor 的 postProcessAfterInitialization 方法
    if (mbd == null || !mbd.isSynthetic()) {
      wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    }

对于Bean懒加载机制,就是在spring IOC容器初始化时不做处理,而是在第一次context.getBean()是进行初始化。

4、循环依赖

循环依赖就是循环引用,如A引用B,B引用C,C又引用A,这样就形成一个环形引用链。

spring的循环依赖方式有两种:

构造函数循环依赖:这种spring自己没法解决,可以通过@Lazy懒加载的方式解决

属性循环依赖:spring采用三级缓存提前暴露对象的方式解决

接下来通过几个问题来说明三级缓存的设计逻辑和原理:

  • 三级缓存是什么?

一级缓存(Singleton Objects):即单例池,存储完全初始化好的bean的缓存。当一个bean被完全处理并准备好后,它会被放入这个缓存中。
​
二级缓存(Early Singleton Objects):这个缓存存储的是早期暴露的对象,即还没有完全初始化的bean。这些对象已经被实例化,但可能还没有完成依赖注入和初始化。
​
三级缓存(Singleton Factories):这个缓存存储的是bean工厂对象ObjectFactory。
  • 三级缓存的作用

其实只有发生循环依赖时才会使用到二级缓存和三级缓存

(1)什么时候会在第三级缓存中添加对象的Bean工厂?

@FunctionalInterface
public interface ObjectFactory<T> {
​
  T getObject() throws BeansException;
}

ObjectFactory是一个函数式接口,实际放入三级缓存的是lambda表达式。

boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
        isSingletonCurrentlyInCreation(beanName));
    if (earlySingletonExposure) {
      //如果是单例模式,允许循环依赖,则添加三级缓存(实际添加的是一个lambda表达式,getObject()时执行)
      addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    }
​
    // 依赖注入和初始化.
    Object exposedObject = bean;
    try {
      populateBean(beanName, mbd, instanceWrapper);
      exposedObject = initializeBean(beanName, exposedObject, mbd);
    }

(2)获取Bean时三级缓存的作用

获取Bean时,首先getSinglton(String beanName)尝试从缓存中获取,没有的话才创建Bean实例。

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 先取一级缓存
    Object singletonObject = this.singletonObjects.get(beanName);
    //这一步:isSingletonCurrentlyInCreation为true说明出现循环依赖
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
      //一级缓存没有取二级缓存
      singletonObject = this.earlySingletonObjects.get(beanName);
      if (singletonObject == null && allowEarlyReference) {
        //加锁双重检测
        synchronized (this.singletonObjects) {
          // Consistent creation of early reference within full singleton lock
          singletonObject = this.singletonObjects.get(beanName);
          if (singletonObject == null) {
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null) {
              //一二级缓存都没有则尝试从三级缓存中取
              ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
              if (singletonFactory != null) {
                //三级缓存获取的Bean工厂对象ObjectFactory创建bean实例
                singletonObject = singletonFactory.getObject();
                this.earlySingletonObjects.put(beanName, singletonObject);
                this.singletonFactories.remove(beanName);
              }
            }
          }
        }
      }
    }
    return singletonObject;
  }
  • 循环依赖的判断

循环依赖的判断发生在populateBean属性注入阶段,假如A依赖B,B依赖A,则链路如下:

A依赖注入B—>B实例化后也进行依赖注入—>这时也会执行getBean获取A的对象

B的依赖注入populateBean()方法执行:尝试通过getBean注入A

private void populateBean(String beanName, GPBeanDefinition beanDefinition, GPBeanWrapper beanWrapper) {
 
        ...
 
            try {
 
                //ioc.get(beanName) 相当于通过接口的全名拿到接口的实现的实例
                field.set(instance,getBean(autowiredBeanName));
            } catch (IllegalAccessException e) {
                e.printStackTrace();
                continue;
            }
        ...
 
    }

再次执行A的getBean(),因为之前创建A时已经执行过一次:

public Object getBean(String beanName){
 
    //1、先拿到BeanDefinition配置信息
    GPBeanDefinition beanDefinition = regitry.beanDefinitionMap.get(beanName);
 
    // 增加一个出口. 判断实体类是否已经被加载过了
    Object singleton = getSingleton(beanName,beanDefinition);
    if (singleton != null) { return singleton; }

在getSingleton()中既可以判断是够发生循环依赖:

isSingletonCurrentlyInCreation为true说明出现循环依赖

因为在B中创建A,但发现A已经在创建了,所以一定发生了循环依赖。

  • 为什么需要三级缓存,两级缓存不能解决循环依赖问题吗?

这主要是跟AOP代理对象和Bean的生命周期设计有关,来看下面的例子:

AService与BService互相依赖,下面是AService的bean生命周期(这里对象分为普通对象和代理对象,实际代理对象Proxy中有个target指针指向普通对象)
​
1、实例化:——>AService普通对象——>放入cacheMap("beanName",AService普通对象)
2、填充BService——>单例池Map(一级缓存)不存在——>创建BService
       BService的bean生命周期
       2.1、实例化:普通对象
       2.2、填充AService——>单例池Map不存在——>cacheMap中获取AService普通对象
       2.3、填充其他属性
       2.4、初始化操作、执行后置处理(AOP:代理对象就是在这一步创建)                                    
    2.5、完整的Bean对象添加到单例池Map 3、填充其他属性 4、初始化操作、执行后置处理(AOP:代理对象就是在这一步创建) 5、完整的Bean对象添加到单例池Map(对于被代理对象,最终放入单例池中的是代理对象)          

从上面的例子可以看出,如果不考虑AOP代理的话,两级缓存就可以解决循环依赖的问题。但是如果AService是AOP代理对象,就会出现BService中注入的是普通对象,而AService放入单例池中的是代理对象,因为AOP代理是在初始化阶段由后置处理器创建的。

所以,如果要解决这个问题,就要再实例化之后,向cacheMap中注入一个可以创建AOP代理对象的东西。

1、实例化:——>AService普通对象——>放入三级缓存singletonFactories.put("beanName",() -> getEarlyBeanReference(beanName, mbd, bean))
2、填充BService——>单例池Map(一级缓存)不存在——>创建BService
       BService的bean生命周期
       2.1、实例化:普通对象
       2.2、填充AService——>单例池Map不存在——>检查出现了循环依赖——>二级缓存earlySingletonObjects——>singletonFactories执行lambda表达式,创建AOP代理对象或普通对象——>放入earlySingletonObjects
       2.3、填充其他属性
       2.4、初始化操作、执行后置处理(AOP:代理对象就是在这一步创建)                                    
    2.5、完整的Bean对象添加到单例池Map 3、填充其他属性 4、初始化操作、执行后置处理(AOP:检查是否已经创建过代理对象,创建过则不再创建) 4.5、earlySingletonObjects.get(beanName)获取对象,这一步主要是为了将代理对象放入单例池中 5、完整的Bean对象添加到单例池Map(对于被代理对象,最终放入单例池中的是代理对象)

只有在发生循环依赖时才会用到二级缓存和三级缓存,其中二级缓存主要是为了防止出现多个AOP代理象。因为如果A和B、A和C都发生循环依赖,没有二级缓存的话,每次都由三级缓存singletonFactories中ObjectFactory创建,则会生成多个代理对象。

这里还有两个问题:

(1)代理对象发生循环依赖时何时进行AOP?

上面getSingleton()方法中,由三级缓冲中的ObjectFactory.getObject()触发lambda表达式执行

addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean))
  @Override
  public Object getEarlyBeanReference(Object bean, String beanName) {
    Object cacheKey = getCacheKey(bean.getClass(), beanName);
    //注意这一步有一个缓存
    this.earlyProxyReferences.put(cacheKey, bean);
    return wrapIfNecessary(bean, beanName, cacheKey);
  }
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    ....
    if (specificInterceptors != DO_NOT_PROXY) {
      this.advisedBeans.put(cacheKey, Boolean.TRUE);
      //如果是代理对象则创建代理对象
      Object proxy = createProxy(
          bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
      this.proxyTypes.put(cacheKey, proxy.getClass());
      return proxy;
    }
​
    this.advisedBeans.put(cacheKey, Boolean.FALSE);
    return bean;
  }

(2)已经进行了AOP后怎么在后置处理器创建代理对象时不再重复创建?

没有循环依赖的AOP是在后置处理器BeanPostProcessor.postProcessAfterInitialization()方法中创建的。

@Override
  public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
    if (bean != null) {
      Object cacheKey = getCacheKey(bean.getClass(), beanName);
      //这一步判断如果代理缓存中有这个Bean,则不再进行创建
      if (this.earlyProxyReferences.remove(cacheKey) != bean) {
        return wrapIfNecessary(bean, beanName, cacheKey);
      }
    }
    return bean;
  }
  • 总结

要解决循环依赖的问题,必须要借助一个额外的缓存在依赖注入前提前暴露一个可供引用的bean(因为发现循环依赖就是在依赖注入这一步),注意这里不能考虑一级缓存(因为一级缓存中存放的是走完整个生命周期完整的Bean,否则多线程使用时会发生问题),假设去掉二级缓存,还会存在代理对象的创建问题:

(1)对于代理对象,在依赖注入发生循环依赖时创建代理对象,那就还是在实例化后向缓存中存入ObjectFactory这种bean工厂以便到时创建代理对象。如A和B,A和C都循环依赖这种情况,会生成多个A的代理对象。

(2)在实例化之后,就判断是否是代理对象,如果是就创建代理对象放入缓存,这样在发生发次循环依赖时就不会生成多个代理对象。但是代理对象发生循环依赖的场景是少数情况,为了少数情况要对每个bean判断是否是代理对象,这无疑增加了不必要的开销。

所以其实两级缓存也可以解决循环依赖的问题,包括代理对象循环依赖,但是spring从bean的生命周期和性能方面考虑没有这样做。

 

posted @ 2024-05-17 17:47  jingyi_up  阅读(21)  评论(0编辑  收藏  举报