天天看點

spring ioc源碼解析-啟動流程與循環依賴使用示例啟動流程建立bean三級緩存與循環依賴

使用示例

讀源碼先從最簡單的使用示例入手,然後一層一層往下探索,下面先寫一個典型的spring小例子,來看一下它的入口與使用方法:

//建立User類
public class User {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
           
//定義XML檔案:user.xml
//省略spring配置檔案頭
......
<bean name="user" class="User">
    <property name="name" value="test"/>
</bean>
           
//使用示例:
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("user.xml");
User user = (User)applicationContext.getBean("user");
System.out.println(user.getName());
           

整個例子非常簡單,就是建立一個User Bean,然後獲得它的name,下面我們從入口

ClassPathXmlApplicationContext開始來了解Spring的啟動流程。

啟動流程

ClassPathXmlApplicationContext構造方法如下:

public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
			throws BeansException {

		super(parent);
        //設定xml配置檔案
		setConfigLocations(configLocations);
		if (refresh) {
      //啟動
			refresh();
		}
	}
           

接下來看啟動核心方法refresh():

public void refresh() throws BeansException, IllegalStateException {
  synchronized (this.startupShutdownMonitor) {
    //主要是校驗配置與參數
    // Prepare this context for refreshing.
    prepareRefresh();

    //建立一個BeanFactory,用于建立bean
    // Tell the subclass to refresh the internal bean factory.
    ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

    //初始化beanFactory, 主要是準備建立bean時需要的環境與關系,并且會建立幾個初始bean
    // Prepare the bean factory for use in this context.
    prepareBeanFactory(beanFactory);

    try {
      //允許子類設定postProcessor
      //ClassPathXmlApplicationContext是spring基礎的ApplicationContext,是以不需要設定特有功能的postprocessor
      //像webApplicationContext則需要把servlet相關的processor設定進來,用來支撐spring mvc相關功能
      // Allows post-processing of the bean factory in context subclasses.
      postProcessBeanFactory(beanFactory);

      //執行個體化processor  并注冊到beanfactory中
      //這裡的processor主要是通過AbstractApplicationContext類addBeanFactoryPostProcessor方法設定進來的。
      // Invoke factory processors registered as beans in the context.
      invokeBeanFactoryPostProcessors(beanFactory);

      //執行個體化processor  并注冊到beanfactory中
      //這裡processor主要是spring本身自帶的
      // Register bean processors that intercept bean creation.
      registerBeanPostProcessors(beanFactory);

      //初始化message source并注冊成bean,用于消息解析相關
      // Initialize message source for this context.
      initMessageSource();

      //初始化事件廣播器
      // Initialize event multicaster for this context.
      initApplicationEventMulticaster();

      //子類重新整理使用
      // Initialize other special beans in specific context subclasses.
      onRefresh();

      //注冊監聽器,eventMulticaster廣播之前産生的事件
      // Check for listener beans and register them.
      registerListeners();

      //完成beanfactory初始化,確定所有單例bean完成加載,
      //主要是通過getBean方法一個一個檢查,為空時就建立bean
      // Instantiate all remaining (non-lazy-init) singletons.
      finishBeanFactoryInitialization(beanFactory);

      //釋出容器重新整理完成事件,主要内容是定義生命周期的Bean
      // Last step: publish corresponding event.
      finishRefresh();
    }

    catch (BeansException ex) {
      if (logger.isWarnEnabled()) {
        logger.warn("Exception encountered during context initialization - " +
                    "cancelling refresh attempt: " + ex);
      }

      // Destroy already created singletons to avoid dangling resources.
      destroyBeans();

      // Reset 'active' flag.
      cancelRefresh(ex);

      // Propagate exception to caller.
      throw ex;
    }

    finally {
      // Reset common introspection caches in Spring's core, since we
      // might not ever need metadata for singleton beans anymore...
      resetCommonCaches();
    }
  }
}
           

通過中文注釋我們大概了解了spring的啟動流程,因為流程涉及到的方法比較多,是以不一一分析各個方法的源碼了,簡單總結一下流程是這樣的:

1.檢查配置與參數

2.建立一個類型為

ConfigurableListableBeanFactory的beanFactory

3.初始化該beanFactory,主要是配置環境、建立一些預設bean等

4.注冊postprocessor,先擴充類processor後spring基礎類processor

5.注冊廣播與監聽bean

6.完成初始化

7.注冊生命周期管理的bean

spring ioc源碼解析-啟動流程與循環依賴使用示例啟動流程建立bean三級緩存與循環依賴

其中processor作用是建立bean時允許加入自定義的修改。上面流程中最核心的是第6步,在這一步調用getBean方法,進而真正執行建立bean的工作,接下來我們看一下建立bean的過程。

建立bean

getBean方法最終後調用AbstractBeanFactory的getBean方法,如下:

public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
  return doGetBean(name, requiredType, null, false);
}
           

然後執行doGetBean:

protected <T> T doGetBean(
  final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
throws BeansException {
  ..................
  // Create bean instance.
  if (mbd.isSingleton()) {
    //從DefaultSingletonBeanRegistry的一級緩存中拿bean,如果不存在執行建立函數
    //将建立成功後bean放入到一級緩存中。
    sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
                                  @Override
                                  public Object getObject() throws BeansException {
      try {
        //bean不存在則建立
        return createBean(beanName, mbd, args);
      }
      catch (BeansException ex) {
        // Explicitly remove instance from singleton cache: It might have been put there
        // eagerly by the creation process, to allow for circular reference resolution.
        // Also remove any beans that received a temporary reference to the bean.
        destroySingleton(beanName);
        throw ex;
      }
    }
  });
  bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
           

需要注意的是

DefaultSingletonBeanRegistry有三級緩存,getBean方法隻是從第一級緩存中查找bean的成品。

接下來我們看createBean:

@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException {
  if (logger.isDebugEnabled()) {
    logger.debug("Creating instance of bean '" + beanName + "'");
  }
  RootBeanDefinition mbdToUse = mbd;

  ...............

  //執行建立bean
  Object beanInstance = doCreateBean(beanName, mbdToUse, args);
  if (logger.isDebugEnabled()) {
    logger.debug("Finished creating instance of bean '" + beanName + "'");
  }
  return beanInstance;
}
           

這樣我們就進入了建立bean的核心方法doCreateBean:

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
throws BeanCreationException {

  //執行個體化bean
  // Instantiate the bean.
  BeanWrapper instanceWrapper = null;
  if (mbd.isSingleton()) {
    instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
  }
  if (instanceWrapper == null) {
    //執行個體化
    instanceWrapper = createBeanInstance(beanName, mbd, args);
  }

  ........................

  //加入到三級緩存中
  // Eagerly cache singletons to be able to resolve circular references
  // even when triggered by lifecycle interfaces like BeanFactoryAware.
  boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
                                    isSingletonCurrentlyInCreation(beanName));
  //為解決依賴問題,需要提前曝光還未建立完成的bean
  if (earlySingletonExposure) {
    if (logger.isDebugEnabled()) {
      //這裡的日志說得非常清楚,放在第三級緩存中是為了解決循環依賴問題 
      logger.debug("Eagerly caching bean '" + beanName +
                   "' to allow for resolving potential circular references");
    }
    //加入到三級緩存中
    addSingletonFactory(beanName, new ObjectFactory<Object>() {
                        @Override
                        public Object getObject() throws BeansException {
      return getEarlyBeanReference(beanName, mbd, bean);
    }
  });
}

//初始化:主要是設定注入的變量等
// Initialize the bean instance.
Object exposedObject = bean;
try {
  //填充屬性
  populateBean(beanName, mbd, instanceWrapper);
  if (exposedObject != null) {
    //調用aware類方法,設定beanPostProcessor
    exposedObject = initializeBean(beanName, exposedObject, mbd);
  }
}

...............

return exposedObject;
}
           

這個方法有點長,我把關鍵的幾行挑出來并用中文注釋了,其實思路也挺清晰的,先建立執行個體再初始化最後加入到緩存中。

在doGetBean方法中,會執行getSingleton,這個方法會調用createBean,并将建立的bean加入到一級緩存中,代碼如下:

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
  Assert.notNull(beanName, "'beanName' must not be null");
  synchronized (this.singletonObjects) {
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null) {
      if (this.singletonsCurrentlyInDestruction) {
        throw new BeanCreationNotAllowedException(beanName,
                                                  "Singleton bean creation not allowed while singletons of this factory are in destruction " +
                                                  "(Do not request a bean from a BeanFactory in a destroy method implementation!)");
      }
      if (logger.isDebugEnabled()) {
        logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
      }
      beforeSingletonCreation(beanName);
      boolean newSingleton = false;
      boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
      if (recordSuppressedExceptions) {
        this.suppressedExceptions = new LinkedHashSet<Exception>();
      }
      try {
        //執行建立bean操作:createBean()方法
        singletonObject = singletonFactory.getObject();
        newSingleton = true;
      }
      catch (IllegalStateException ex) {
        // Has the singleton object implicitly appeared in the meantime ->
        // if yes, proceed with it since the exception indicates that state.
        singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null) {
          throw ex;
        }
      }
      catch (BeanCreationException ex) {
        if (recordSuppressedExceptions) {
          for (Exception suppressedException : this.suppressedExceptions) {
            ex.addRelatedCause(suppressedException);
          }
        }
        throw ex;
      }
      finally {
        if (recordSuppressedExceptions) {
          this.suppressedExceptions = null;
        }
        afterSingletonCreation(beanName);
      }
      if (newSingleton) {
        //加入到一級緩存中
        addSingleton(beanName, singletonObject);
      }
    }
    return (singletonObject != NULL_OBJECT ? singletonObject : null);
  }
}
           

加入緩存的代碼如下:

protected void addSingleton(String beanName, Object singletonObject) {
  synchronized (this.singletonObjects) {
    //放入到一級緩存
    this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT));
    //從三級緩存中删除
    this.singletonFactories.remove(beanName);
    //從二級緩存中删除
    this.earlySingletonObjects.remove(beanName);
    //标記該beanname已經被建立過
    this.registeredSingletons.add(beanName);
  }
}
           

上面的代碼我們了解到bean的建立過程,總結如下:

getBean-> doGetBean->createBean->doCreateBean->放入一級緩存中->傳回
           

當然這裡主要是為了了解過程,是以很多細節被忽略了,包括兩個重要功能:aop支援與循環依賴的解決,下一節将着重分析這兩個功能的代碼。

三級緩存與循環依賴

上文有提到

DefaultSingletonBeanRegistry中有三級緩存,用來儲存不同建立階段的bean, 第一級是bean執行個體化後存放的地方,此時還沒有注入依賴關系,第二級則是将第一級緩存的bean進行增強後存放的地方,增強的主要内容是使用動态代理的方式使它支援AOP,最後把第二級的bean注入依賴關系後放入到第三級。

三級緩存如下:

1.第一級為bean成品:singletonObjects

/** Cache of singleton objects: bean name --> bean instance */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);
           

2.第二級:earlySingletonObjects

/** Cache of early singleton objects: bean name --> bean instance */
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);
           

3.第三級:singletonFactories

/** Cache of singleton factories: bean name --> ObjectFactory */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);
           

那spring是怎麼利用三級緩存來解決依賴問題的呢? 在doCreateBean方法中,spring使用createBeanInstance方法建立了一個bean執行個體,這個執行個體還未進行初始化,它會先放到第三級緩存中,然後檢查依賴關系,下面繼續回到doCreateBean方法中:

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
throws BeanCreationException {
  .................................................
  boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
                                    isSingletonCurrentlyInCreation(beanName));
  if (earlySingletonExposure) {
    if (logger.isDebugEnabled()) {
      //這裡的日志說得非常清楚,放在第三級緩存中是為了解決循環依賴問題 
      logger.debug("Eagerly caching bean '" + beanName +
                   "' to allow for resolving potential circular references");
    }
    //加入到三級緩存中
    addSingletonFactory(beanName, new ObjectFactory<Object>() {
                        @Override
                        public Object getObject() throws BeansException {
      return getEarlyBeanReference(beanName, mbd, bean);
    }
  });
}

.........................................

//開始檢查循環依賴
if (earlySingletonExposure) {
  //從三級緩存中拿出剛剛執行個體化的bean
  Object earlySingletonReference = getSingleton(beanName, false);
  if (earlySingletonReference != null) {
    if (exposedObject == bean) {
      exposedObject = earlySingletonReference;
    }
    else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
      //拿出它所有依賴的bean
      String[] dependentBeans = getDependentBeans(beanName);
      Set<String> actualDependentBeans = new LinkedHashSet<String>(dependentBeans.length);
      for (String dependentBean : dependentBeans) {
        if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
          actualDependentBeans.add(dependentBean);
        }
      }
      //如果發現有依賴的bean還沒有被建立就會報異常
      if (!actualDependentBeans.isEmpty()) {
        //其實看這裡的注釋就知道跟解決循環依賴有關
        throw new BeanCurrentlyInCreationException(beanName,
                                                   "Bean with name '" + beanName + "' has been injected into other beans [" +
                                                   StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
                                                   "] in its raw version as part of a circular reference, but has eventually been " +
                                                   "wrapped. This means that said other beans do not use the final version of the " +
                                                   "bean. This is often the result of over-eager type matching - consider using " +
                                                   "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
      }
    }
  }
}
..............................................
}
           

看完上面代碼你是不是感覺很奇怪,上面代碼隻有檢查依賴關系,那哪裡設定依賴關系呢?下面我們從deCreateBean裡進入populateBean方法中:

protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) {
  PropertyValues pvs = mbd.getPropertyValues();
  ................
  //對屬性指派,如果屬性是注入的對象,則會觸發設定依賴關系
  applyPropertyValues(beanName, mbd, bw, pvs);
}
           
protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
  if (pvs == null || pvs.isEmpty()) {
    return;
  }
  ..................
  //解析屬性值,如果屬性是依賴注入
  Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
  ................
           

開始解析引用

public Object resolveValueIfNecessary(Object argName, Object value) {
  // We must check each value to see whether it requires a runtime reference
  // to another bean to be resolved.
  //通過xml注入的
  if (value instanceof RuntimeBeanReference) {
    RuntimeBeanReference ref = (RuntimeBeanReference) value;
    //
    return resolveReference(argName, ref);
  }
  //通過autowired注入的
  else if (value instanceof RuntimeBeanNameReference) {
    String refName = ((RuntimeBeanNameReference) value).getBeanName();
    refName = String.valueOf(doEvaluate(refName));
    if (!this.beanFactory.containsBean(refName)) {
      throw new BeanDefinitionStoreException(
        "Invalid bean name '" + refName + "' in bean reference for " + argName);
    }
    return refName;
  }
  ................................
           
private Object resolveReference(Object argName, RuntimeBeanReference ref) {
  try {
    String refName = ref.getBeanName();
    refName = String.valueOf(doEvaluate(refName));
    if (ref.isToParent()) {
      if (this.beanFactory.getParentBeanFactory() == null) {
        throw new BeanCreationException(
          this.beanDefinition.getResourceDescription(), this.beanName,
          "Can't resolve reference to bean '" + refName +
          "' in parent factory: no parent factory available");
      }
      return this.beanFactory.getParentBeanFactory().getBean(refName);
    }
    else {
      //調用getBean來建立所依賴的bean
      Object bean = this.beanFactory.getBean(refName);
      this.beanFactory.registerDependentBean(refName, this.beanName);
      return bean;
    }
  }
  .....................
           

從上面的代碼我們可以看出,createBean前會先檢查它所依賴的bean,如果該bean還沒有被建立就調用getBean來建立該bean,這個時候就會有一個問題了,如果兩個bean互相依賴,那豈不是會死循環了?

我們來繼續看看解決循環依賴死循環的關鍵:getSingleton方法

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
  //先在一級緩存中找
  Object singletonObject = this.singletonObjects.get(beanName);
  //如果發現bean還在建立中
  if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
    synchronized (this.singletonObjects) {
      //從二級緩存中找
      singletonObject = this.earlySingletonObjects.get(beanName);
      if (singletonObject == null && allowEarlyReference) {
        //從三級緩存中找
        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);
}
           

通過代碼我們可以看到即使bean還在三級緩存也會拿出來,這樣它就不會重複執行doCreateBean了。

簡化以上邏輯如下:

  1. 假設A與B互相依賴
  2. 建立A時先執行個體化放入三級緩存
  3. 指派時發現依賴B,調用getBean方法來建立B
  4. B執行個體化後放入三級緩存
  5. 指派時發現依賴A,調用getBean方法來建立A
  6. 這個時候A已經存在于第三緩存了,傳回A給B
  7. 然後完成B的建立
  8. 接着完成A的建立。

關注個人微信公衆号:肌肉碼農,回複“好友”讨論問題。

繼續閱讀