1. 程式人生 > 程式設計 >Spring IoC學習之ApplicationContext中refresh過程詳解

Spring IoC學習之ApplicationContext中refresh過程詳解

refresh()

該方法是 Spring Bean 載入的核心,它是 ClassPathXmlApplicationContext 的父類 AbstractApplicationContext 的一個方法 , 顧名思義,用於重新整理整個Spring 上下文資訊,定義了整個 Spring 上下文載入的流程。

public void refresh() throws BeansException,IllegalStateException {
 synchronized(this.startupShutdownMonitor) {
 //準備重新整理上下文 環境 
 this.prepareRefresh();
 //初始化BeanFactory,並進行XML檔案讀取 
 /* 
  * ClassPathXMLApplicationContext包含著BeanFactory所提供的一切特徵,在這一步驟中將會複用 
  * BeanFactory中的配置檔案讀取解析及其他功能,這一步之後,ClassPathXmlApplicationContext 
  * 實際上就已經包含了BeanFactory所提供的功能,也就是可以進行Bean的提取等基礎操作了。 
  */ 
 ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
 //對beanFactory進行各種功能填充
 this.prepareBeanFactory(beanFactory);
​
 try {
  //子類覆蓋方法做額外處理 
  this.postProcessBeanFactory(beanFactory);
  //啟用各種beanFactory處理器 
  this.invokeBeanFactoryPostProcessors(beanFactory);
  //註冊攔截Bean建立的Bean處理器,這裡只是註冊,真正的呼叫實在getBean時候 
  this.registerBeanPostProcessors(beanFactory);
  //為上下文初始化Message源,即不同語言的訊息體,國際化處理 
  this.initMessageSource();
  //初始化應用訊息廣播器,並放入“applicationEventMulticaster”bean中 
  this.initApplicationEventMulticaster();
  //留給子類來初始化其它的Bean 
  this.onRefresh();
  //在所有註冊的bean中查詢Listener bean,註冊到訊息廣播器中 
  this.registerListeners();
  //初始化剩下的單例項(非惰性的)
  this.finishBeanFactoryInitialization(beanFactory);
  //完成重新整理過程,通知生命週期處理器lifecycleProcessor重新整理過程,同時發出ContextRefreshEvent通知別人 
  this.finishRefresh();
 } catch (BeansException var9) {
  if (this.logger.isWarnEnabled()) {
  this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
  }
​
  this.destroyBeans();
  this.cancelRefresh(var9);
  throw var9;
 } finally {
  this.resetCommonCaches();
 }
​
 }
}

每個子方法的功能之後一點一點再分析,首先 refresh()方法有幾點是值得我們學習的:

  1. 方法是加鎖的,這麼做的原因是避免多執行緒同時重新整理 Spring 上下文;
  2. 儘管加鎖可以看到是針對整個方法體的,但是沒有在方法前加 synchronized 關鍵字,而使用了物件鎖 startUpShutdownMonitor,這樣做有兩個好處:
    (1)refresh()方法和 close()方法都使用了 startUpShutdownMonitor 物件鎖加鎖,這就保證了在呼叫 refresh()方法的時候無法呼叫 close()方法,反之依然,這樣就避免了衝突。
    (2)使用物件鎖可以減小同步的範圍,只對不能併發的程式碼塊進行加鎖,提高了整體程式碼執行的速率。
  3. 在 refresh()方法中整合了很多個子方法,使得整個方法流程清晰易懂。這樣一來,方便程式碼的可讀性和可維護性。

3.1 prepareRefresh方法

//設定啟動時間,是否啟用標識位,初始化屬性源(property source)配置
protected void prepareRefresh() {
 this.startupDate = System.currentTimeMillis();
 this.closed.set(false);
 this.active.set(true);
 if (this.logger.isDebugEnabled()) {
 if (this.logger.isTraceEnabled()) {
  this.logger.trace("Refreshing " + this);
 } else {
  this.logger.debug("Refreshing " + this.getDisplayName());
 }
 }
​
 // 在上下文環境中初始化任何佔位符屬性源。(空的方法,留給子類覆蓋)
 this.initPropertySources();
 //驗證所有的必需的屬性是否可解析,若無則不能解析並丟擲異常
 this.getEnvironment().validateRequiredProperties();
 if (this.earlyApplicationListeners == null) {
 this.earlyApplicationListeners = new LinkedHashSet(this.applicationListeners);
 } else {
 this.applicationListeners.clear();
 this.applicationListeners.addAll(this.earlyApplicationListeners);
 }
​
 this.earlyApplicationEvents = new LinkedHashSet();
}

prepareRefresh 的內容如上,該方法主要進行環境的準備,包括 Context 的啟動時間,活動狀態,然後設定 context 中的配置資料來源,使用預設的 StandardEnvironment 物件,該物件添加了 System.env()屬性和 System.properties()屬性 。 initPropertySources 方法用於初始化 context 中 environment 的屬性源。在 AbstractApplicationContext 中為空實現。其他子類的實現如下:

Spring IoC學習之ApplicationContext中refresh過程詳解

在子類 GenericWebApplicationContext 和 AbstractRefreshableWebApplicationContext 的實現大致一致,都是:

protected void initPropertySources() {
 ConfigurableEnvironment env = this.getEnvironment();
 if (env instanceof ConfigurableWebEnvironment) {
 ((ConfigurableWebEnvironment)env).initPropertySources(this.servletContext,this.servletConfig);
 }
​
}

通過在 getEnvironment 方法中,重寫 createEnvironment 方法 。

protected ConfigurableEnvironment createEnvironment() {
 return new StandardServletEnvironment();
}

將 AbstractApplicationContext 類中預設的 StandardEnvironment 替換為StandardServletEnvironment, StandardServletEnvironment 的關係圖為:

Spring IoC學習之ApplicationContext中refresh過程詳解

因而會執行 StandardServletEnvironment 類的 initPropertySources 方法,為 context 新增 ServletContext 和 ServletConfig 對應的配置屬性源。

public void initPropertySources(@Nullable ServletContext servletContext,@Nullable ServletConfig servletConfig) {
 WebApplicationContextUtils.initServletPropertySources(this.getPropertySources(),servletContext,servletConfig);
}

接著是分析 this.getEnvironment().validateRequiredProperties()方法,在上述我們已經提到 getEnvironment()返回的不再是預設的 StandardEnvironment 而是替換為了 StandardServletEnvironment,在此基礎上查詢 validateRequiredProperties()的實現方法,最終定位到了 AbstractEnvironment 類中:

public void validateRequiredProperties() throws MissingRequiredPropertiesException {
 this.propertyResolver.validateRequiredProperties();
}

this.propertyResolver 指的是 PropertySourcesPropertyResolver 物件,最終具體實現定位在 AbstractPropertyResolver 類中:

public void validateRequiredProperties() {
 MissingRequiredPropertiesException ex = new MissingRequiredPropertiesException();
 Iterator var2 = this.requiredProperties.iterator();
​
 while(var2.hasNext()) {
 String key = (String)var2.next();
 if (this.getProperty(key) == null) {
  ex.addMissingRequiredProperty(key);
 }
 }
​
 if (!ex.getMissingRequiredProperties().isEmpty()) {
 throw ex;
 }
}

Spring IoC學習之ApplicationContext中refresh過程詳解

3.2 obtainFreshBeanFactory

該方法的實現如下,通過 refreshBeanFactory 重置 AbstractApplicationContext 持有的 BeanFactory,然後通過 getBeanFactory 獲取該物件並返回。

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
 this.refreshBeanFactory();
 return this.getBeanFactory();
}

AbstractApplicationContext 中 refreshBeanFacoty 方法和 getBeanFactory 方法都是抽象方法, 具體實現在 AbstractRefreshableApplicationContext 中。

protected final void refreshBeanFactory() throws BeansException {
 if (this.hasBeanFactory()) {
  //銷燬已經存在的單例bean
  this.destroyBeans();
  //銷燬已有的BeanFactory
  this.closeBeanFactory();
 }
​
 try {
  //建立一個新的beanFactory,型別為DefaultListableBeanFactory
  DefaultListableBeanFactory beanFactory = this.createBeanFactory();
  //設定序列化id,為例項物件記憶體中的十六進位制標識
  beanFactory.setSerializationId(this.getId());
  //定製beanFactory,設定相關屬性,包括是否允許覆蓋同名稱的不同定義的物件以及迴圈依賴以及設定 
  this.customizeBeanFactory(beanFactory);
  //載入BeanDefiniton
  this.loadBeanDefinitions(beanFactory);
  synchronized(this.beanFactoryMonitor) {
   this.beanFactory = beanFactory;
  }
 } catch (IOException var5) {
  throw new ApplicationContextException("I/O error parsing bean definition source for " + this.getDisplayName(),var5);
 }
}

loadBeanDefinitions 在 AbstractRefreshableApplicationContext 中是個抽象方法,具體實現是在 AbstractXmlApplicationContext 中:

protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException,IOException {
 //為當前工廠建立xml解析器
 XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
 
 //配置當前環境
 beanDefinitionReader.setEnvironment(this.getEnvironment());
 //配置資源解析器
 beanDefinitionReader.setResourceLoader(this);
 //配置schemas或者dtd的資源解析器,EntityResolver維護了url->schemalocation的路徑
 beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
 //子類提供自定義的reader的初始化方法
 this.initBeanDefinitionReader(beanDefinitionReader);
 //載入bean定義
 this.loadBeanDefinitions(beanDefinitionReader);
}

在 loadBeanDefinitions 方法中傳入了 DefaultListableBeanFactory 物件,並且初始化了 XmlBeanDefinitionReader 物件,接著就是初始化 bean 工廠的一些環境、類載入器等。 繼續進入到 loadBeanDefinitions(beanDefinitionReader)方法體中,程式碼如下:

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException,IOException {
 Resource[] configResources = this.getConfigResources();
 if (configResources != null) {
  reader.loadBeanDefinitions(configResources);
 }
​
 String[] configLocations = this.getConfigLocations();
 if (configLocations != null) {
  reader.loadBeanDefinitions(configLocations);
 }
​
}

這裡的 configResources 和 configLocations 對應兩種建構函式,其中 configLocations 是建構函式A使用的。關於 loadBeanDefinitions 方法涉及的內容比較多,我們挑一些重要的來看。以下是 AbstractBeanDefinitionReader 類中的 loadBeanDefinitions 方法。



上述方法主要做兩件事:

  • 呼叫資源載入器獲取資源 resourceLoader.getResource(location) 。
  • 真正執行載入功能的是子類 XmlBeanDefinitionReader的loadBeanDefinitions方法 。

其中 getResources 方法是在 PathMatchingResourcePatternResolver 類實現的。

public Resource[] getResources(String locationPattern) throws IOException {
 Assert.notNull(locationPattern,"Location pattern must not be null");
 if (locationPattern.startsWith("classpath*:")) {
  return this.getPathMatcher().isPattern(locationPattern.substring("classpath*:".length())) ? this.findPathMatchingResources(locationPattern) : this.findAllClassPathResources(locationPattern.substring("classpath*:".length()));
 } else {
  int prefixEnd = locationPattern.startsWith("war:") ? locationPattern.indexOf("*/") + 1 : locationPattern.indexOf(58) + 1;
  return this.getPathMatcher().isPattern(locationPattern.substring(prefixEnd)) ? this.findPathMatchingResources(locationPattern) : new Resource[]{this.getResourceLoader().getResource(locationPattern)};
 }
}

count = this.loadBeanDefinitions(resources);中的 loadBeanDefinitions 方法具體實現在 XmlBeanDefinitionReader 類中。

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
 Assert.notNull(encodedResource,"EncodedResource must not be null");
 if (this.logger.isTraceEnabled()) {
  this.logger.trace("Loading XML bean definitions from " + encodedResource);
 }
​
 Set<EncodedResource> currentResources = (Set)this.resourcesCurrentlyBeingLoaded.get();
 if (currentResources == null) {
  currentResources = new HashSet(4);
  this.resourcesCurrentlyBeingLoaded.set(currentResources);
 }
​
 if (!((Set)currentResources).add(encodedResource)) {
  throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!");
 } else {
  int var5;
  try {
   //將資原始檔轉換為型別為InputStream的I/O流
   InputStream inputStream = encodedResource.getResource().getInputStream();
​
   try {
    //從InputStream中得到xML的解析源
    InputSource inputSource = new InputSource(inputStream);
    //編碼如果不為null,則設定inputSource的編碼
    if (encodedResource.getEncoding() != null) {
     inputSource.setEncoding(encodedResource.getEncoding());
    }
​
    var5 = this.doLoadBeanDefinitions(inputSource,encodedResource.getResource());
   } finally {
    inputStream.close();
   }
  } catch (IOException var15) {
   throw new BeanDefinitionStoreException("IOException parsing XML document from " + encodedResource.getResource(),var15);
  } finally {
   ((Set)currentResources).remove(encodedResource);
   if (((Set)currentResources).isEmpty()) {
    this.resourcesCurrentlyBeingLoaded.remove();
   }
​
  }
​
  return var5;
 }
}

getInputStream 方法用來載入 XML 檔案,具體實現在 ClassPathResource 類中,

public InputStream getInputStream() throws IOException {
 InputStream is;
 if (this.clazz != null) {
  is = this.clazz.getResourceAsStream(this.path);
 } else if (this.classLoader != null) {
  is = this.classLoader.getResourceAsStream(this.path);
 } else {
  is = ClassLoader.getSystemResourceAsStream(this.path);
 }
​
 if (is == null) {
  throw new FileNotFoundException(this.getDescription() + " cannot be opened because it does not exist");
 } else {
  return is;
 }
}

doLoadBeanDefinitions 用來註冊 bean。

protected int doLoadBeanDefinitions(InputSource inputSource,Resource resource) throws BeanDefinitionStoreException {
 try {
  //轉化為Document 物件
  Document doc = this.doLoadDocument(inputSource,resource);
  //啟動對Bean定義解析的詳細過程,會用到Spring Bean的配置規則
  int count = this.registerBeanDefinitions(doc,resource);
  if (this.logger.isDebugEnabled()) {
   this.logger.debug("Loaded " + count + " bean definitions from " + resource);
  }
​
  return count;
 } catch (BeanDefinitionStoreException var5) {
  throw var5;
 .....
}

後續關聯的程式碼在此就不做介紹,後期我們再學習。

因為在 XmlBeanDefinitionReader 中已經將之前初始化的 DefaultListableBeanFactory 註冊進去了,所以 XmlBeanDefinitionReader 所讀取的 BeanDefinitionHolder 都會註冊到 DefinitionListableBeanFactory 中,也就是經過這個步驟,DefaultListableBeanFactory 的變數 beanFactory 已經包含了所有解析好的配置。

至此通過載入 XML 檔案, 將xml檔案解析為對應的 BeanDefinition ,完成了 Bean 定義的載入和註冊。

Spring IoC學習之ApplicationContext中refresh過程詳解

3.3 prepareBeanFactory

protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
 //設定beanFactory的classLoader為當前context的classloader 
 beanFactory.setBeanClassLoader(this.getClassLoader());
 //設定beanFactory的表示式語言處理器,Spring3增加了表示式語言的支援, 
 //預設可以使用#{bean.xxx}的形式來呼叫相關屬性值 
 beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
 //設定PropertyEditorRegistrar,通過PropertyEditor將xml解析出來的bean屬性(字串)和相應的java型別做轉換
 beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this,this.getEnvironment()));
 /**
 新增後置處理器ApplicationContextAwareProcessor,在Bean初始化後自動執行各Aware介面的set方法,包 括ResourceLoaderAware、ApplicationEventPublisherAware、MessageSourceAware、 ApplicationContextAware、EnvironmentAware
 **/
 beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
 beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
 beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
 beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
 beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
 beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
 beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
 //預先設定用於自動依賴注入的介面物件
 //包括BeanFactory、ResourceLoader、ApplicationEventPublisher、ApplicationContext
 beanFactory.registerResolvableDependency(BeanFactory.class,beanFactory);
 beanFactory.registerResolvableDependency(ResourceLoader.class,this);
 beanFactory.registerResolvableDependency(ApplicationEventPublisher.class,this);
 beanFactory.registerResolvableDependency(ApplicationContext.class,this);
  //在 bean 例項化後,新增ApplicationListenerDetector,可以理解成:註冊事件監聽器
 beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
 //如果存在loadTimeWeaver這個Bean,則增加對應的後置處理器
 if (beanFactory.containsBean("loadTimeWeaver")) {
  beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
  beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
 }

 //新增預設的系統環境bean 
 if (!beanFactory.containsLocalBean("environment")) {
  beanFactory.registerSingleton("environment",this.getEnvironment());
 }

 if (!beanFactory.containsLocalBean("systemProperties")) {
  beanFactory.registerSingleton("systemProperties",this.getEnvironment().getSystemProperties());
 }

 if (!beanFactory.containsLocalBean("systemEnvironment")) {
  beanFactory.registerSingleton("systemEnvironment",this.getEnvironment().getSystemEnvironment());
 }

}

其中反覆出現了 addBeanPostProcessor 方法,該方法具體實現在 AbstractBeanFactory 類中。

public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor) {
 Assert.notNull(beanPostProcessor,"BeanPostProcessor must not be null");
 this.beanPostProcessors.remove(beanPostProcessor);
 if (beanPostProcessor instanceof InstantiationAwareBeanPostProcessor) {
  this.hasInstantiationAwareBeanPostProcessors = true;
 }

 if (beanPostProcessor instanceof DestructionAwareBeanPostProcessor) {
  this.hasDestructionAwareBeanPostProcessors = true;
 }

 this.beanPostProcessors.add(beanPostProcessor);
}

詳細分析下程式碼發現上面函式主要是在以下方法進行了擴充套件:

  1. 對SPEL語言的支援
  2. 增加對屬性編輯器的支援
  3. 增加對一些內建類的支援,如EnvironmentAware、MessageSourceAware的注入
  4. 設定了依賴功能可忽略的介面
  5. 註冊一些固定依賴的屬性
  6. 如果存在loadTimeWeaver這個Bean,則增加對應的後置處理器
  7. 將相關環境變數及屬性以單例模式註冊

3.4 postProcessBeanFactory

所有 Bean 的定義已經載入完成,但是沒有例項化,這一步可以修改 bean 定義或者增加自定義的 bean。該方法主要是承接上文中的 prepareBeanFactory 方法,增加一些後置處理器。具體實現在 AbstractRefreshableWebApplicationContext 類中。

protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
 //增加ServletContextAwareProcessor後置處理器
 //用於處理ServletContextAware介面和ServletConfigAware介面中相關物件的自動注入
 beanFactory.addBeanPostProcessor(new ServletContextAwareProcessor(this.servletContext,this.servletConfig));
 beanFactory.ignoreDependencyInterface(ServletContextAware.class);
 beanFactory.ignoreDependencyInterface(ServletConfigAware.class);
 //註冊web環境,包括request、session、golableSession、application
 WebApplicationContextUtils.registerWebApplicationScopes(beanFactory,this.servletContext);
 //註冊servletContext、contextParamters、contextAttributes、servletConfig單例bean
 WebApplicationContextUtils.registerEnvironmentBeans(beanFactory,this.servletContext,this.servletConfig);
}

3.5 invokeBeanFactoryPostProcessors

protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
 PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory,this.getBeanFactoryPostProcessors());
 if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean("loadTimeWeaver")) {
  beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
  beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
 }

}

在執行 invokeBeanFactoryPostProcessors 方法前檢視 beanFactory,對比執行後發現此處有所不同。

Spring IoC學習之ApplicationContext中refresh過程詳解

在 Spring 容器中找出實現了 BeanFactoryPostProcessor 介面的 Bean 並執行。Spring 容器會委託給 PostProcessorRegistrationDelegate 的 invokeBeanFactoryPostProcessors 方法執行。

通過除錯發現,ClassPathXmlApplicationContext 類中的 beanFactoryPostProcessors 屬性為空,所以執行 invokeBeanFactoryPostProcessors 方法時也是如此。

Spring IoC學習之ApplicationContext中refresh過程詳解

那麼我們執行該方法有什麼用呢?那麼還有什麼地方可能存在實現了 BeanFactoryPostProcessor 介面的 Bean,帶著疑問,我們去檢視 PostProcessorRegistrationDelegate 中的 invokeBeanFactoryPostProcessors 方法。

public static void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory,List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
  Set<String> processedBeans = new HashSet();
  ArrayList regularPostProcessors;
  ArrayList registryProcessors;
  int var9;
  ....
}

這一段程式碼特別長,一開始看起來肯定覺得很難,不知道從哪入手,這裡我介紹一下我的學習方法,對測試程式碼進行 debug 除錯,進入該方法後一步一步往下執行,遇到外部引用的方法記下來,待會除錯完畢找到該方法,然後再打斷點進行除錯。反覆來幾遍,大概就能理解這個方法做了什麼事情。

首先該方法的引數 beanFactory 實際型別為 DefaultListableBeanFactory,beanFactoryPostProcessors 引數內容為空。除錯過程中發現比較重要的方法是 getBeanNamesForType 方法,該方法有三個引數值,具體實現在 DefaultListableBeanFactory 類中。

public String[] getBeanNamesForType(@Nullable Class<?> type,boolean includeNonSingletons,boolean allowEagerInit) {
  if (this.isConfigurationFrozen() && type != null && allowEagerInit) {
    Map<Class<?>,String[]> cache = includeNonSingletons ? this.allBeanNamesByType : this.singletonBeanNamesByType;
    String[] resolvedBeanNames = (String[])cache.get(type);
    if (resolvedBeanNames != null) {
      return resolvedBeanNames;
    } else {
      resolvedBeanNames = this.doGetBeanNamesForType(ResolvableType.forRawClass(type),includeNonSingletons,true);
      if (ClassUtils.isCacheSafe(type,this.getBeanClassLoader())) {
        cache.put(type,resolvedBeanNames);
      }

      return resolvedBeanNames;
    }
  } else {
    return this.doGetBeanNamesForType(ResolvableType.forRawClass(type),allowEagerInit);
  }
}

invokeBeanFactoryPostProcessors 方法程式碼在呼叫 getBeanNamesForType 方法時根據第一個引數型別的不同分為兩類: BeanFactoryPostProcessor 和 BeanDefinitionRegistryPostProcessor 。其中 BeanDefinitionRegistryPostProcessor 繼承自 BeanFactoryPostProcessor。執行的時候,先找出所有的 BeanDefinitionRegistryPostProcessor 執行再找出所有 BeanFactoryPostProcessor 執行。因為 BeanDefinitionRegistryPostProcessor 繼承自 BeanFactoryPostProcessor,所以執行後者時會過濾掉前者的內容。

在除錯中發現只有當引數為 BeanFactoryPostProcessor.class 時,才會獲取到有效的內容。 getBeanNamesForType 方法中的關鍵部分是 doGetBeanNamesForType 方法,該方法主要是將 XML 檔案中定義的實現了BeanFactoryPostProcessor的 bean 的 id 取出來,以及將 XML 檔案中定義的 bean 載入到 beanFactory 中。

Spring IoC學習之ApplicationContext中refresh過程詳解

Spring IoC學習之ApplicationContext中refresh過程詳解

等待 getBeanNamesForType 返回這些內容後,接著就會例項化並初始化實現 BeanFactoryPostProcessor 介面的類並執行。這裡比較關鍵的程式碼是 invokeBeanFactoryPostProcessors 和 PropertyResourceConfigurer 類中的 postProcessBeanFactory 方法。

private static void invokeBeanFactoryPostProcessors(Collection<? extends BeanFactoryPostProcessor> postProcessors,ConfigurableListableBeanFactory beanFactory) {
  Iterator var2 = postProcessors.iterator();

  while(var2.hasNext()) {
    BeanFactoryPostProcessor postProcessor = (BeanFactoryPostProcessor)var2.next();
    postProcessor.postProcessBeanFactory(beanFactory);
  }

}

public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
  try {
    Properties mergedProps = this.mergeProperties();
    this.convertProperties(mergedProps);
    this.processProperties(beanFactory,mergedProps);
  } catch (IOException var3) {
    throw new BeanInitializationException("Could not load properties",var3);
  }
}

當 PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors()方法執行完畢後,檢視 beanFactory 的狀態。

Spring IoC學習之ApplicationContext中refresh過程詳解

3.6 registerBeanPostProcessors

從 Spring 容器中找出的 BeanPostProcessor 介面的 Bean,並新增到 BeanFactory 內部維護的 List 屬性中,以便後續 Bean 被例項化的時候呼叫這個 BeanPostProcessor進行回撥處理。該方法委託給了 PostProcessorRegistrationDelegate 類的 registerBeanPostProcessors 方法執行。

同 invokeBeanFactoryPostProcessors 類似, 先從容器中獲取所有型別為 BeanPostProcessor.class 的 Bean 的 name 陣列,然後通過 BeanPostProcessor pp = beanFactory.getBean(ppName,BeanPostProcessor.class); 獲取Bean的例項,最後通過 registerBeanPostProcessors(beanFactory,orderedPostProcessors);將獲取到的 BeanPostProcessor 例項新增到容器的屬性中。在實際過程中會根據 AbstractBeanFactory 類中的 isTypeMatch 方法對 bean 例項進行篩選,具體順序為:

  1. 將實現 PriorityOrdered 介面的 BeanPostProcessor 列表新增到 beanFactory 中
  2. 將實現 Ordered 介面的 BeanPostProcessor 列表新增到 beanFactory 中
  3. 將剩餘的 BeanPostProcessor 列表新增到 beanFactory 中
  4. 將實現 MergedBeanDefinitionPostProcessor 介面的 BeanPostProcessor 列表新增到 beanFactory 中

另外在 PostProcessorRegistrationDelegate 類中有個內部類 BeanPostProcessorChecker,它實現了 BeanPostProcessor 介面,所以最後會有一個 BeanPostProcessorChecker 類新增到 beanFactory 中。

最終該方法用來例項化並初始化實現 BeanPostProcessor 介面的類,但不執行。

3.7 initMessageSource

在 Spring 容器中初始化一些國際化相關的屬性 。

3.8 initApplicationEventMulticaster

初始化 ApplicationEventMulticaste (事件廣播器)是在方法 initApplicationEventMulticaster()中實現的,進入到方法體,如下:

protected void initApplicationEventMulticaster() {
  ConfigurableListableBeanFactory beanFactory = this.getBeanFactory();
  // 1、預設使用內建的事件廣播器,如果有的話.
  // 我們可以在配置檔案中配置Spring事件廣播器或者自定義事件廣播器
  // 例如: <bean id="applicationEventMulticaster" class="org.springframework.context.event.SimpleApplicationEventMulticaster"></bean>
  if (beanFactory.containsLocalBean("applicationEventMulticaster")) {
    this.applicationEventMulticaster = (ApplicationEventMulticaster)beanFactory.getBean("applicationEventMulticaster",ApplicationEventMulticaster.class);
    if (this.logger.isTraceEnabled()) {
      this.logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
    }
  } else {
    // 2、否則,新建一個事件廣播器,SimpleApplicationEventMulticaster是spring的預設事件廣播器
    this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
    beanFactory.registerSingleton("applicationEventMulticaster",this.applicationEventMulticaster);
    if (this.logger.isTraceEnabled()) {
      this.logger.trace("No 'applicationEventMulticaster' bean,using [" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");
    }
  }

}

通過原始碼可以看出該方法實現邏輯與 initMessageSource 基本相同,其步驟如下:查詢是否有 name 為 applicationEventMulticaster 的 bean,如果有則放到容器裡,如果沒有,初始化一個系統預設的 SimpleApplicationEventMulticaster 物件放入容器。

3.9 onRefresh

模組方法,可用於 refresh 動作的擴充套件,預設為空實現。在 SpringBoot 中主要用於啟動內嵌的 Web 伺服器。

3.10 registerListeners

註冊監聽器,找出系統中的 ApplicationListener 物件,註冊到時間廣播器中。如果有需要提前進行廣播的事件,則執行廣播。

protected void registerListeners() {
  // 首先,註冊指定的靜態事件監聽器,在spring boot中有應用
  Iterator var1 = this.getApplicationListeners().iterator();

  while(var1.hasNext()) {
    ApplicationListener<?> listener = (ApplicationListener)var1.next();
    this.getApplicationEventMulticaster().addApplicationListener(listener);
  }

  // 其次,註冊普通的事件監聽器
  String[] listenerBeanNames = this.getBeanNamesForType(ApplicationListener.class,true,false);
  String[] var7 = listenerBeanNames;
  int var3 = listenerBeanNames.length;

  for(int var4 = 0; var4 < var3; ++var4) {
    String listenerBeanName = var7[var4];
    this.getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
  }

  // 如果有早期事件的話,在這裡進行事件廣播
  Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
  this.earlyApplicationEvents = null;
  if (earlyEventsToProcess != null) {
    Iterator var9 = earlyEventsToProcess.iterator();

    while(var9.hasNext()) {
      ApplicationEvent earlyEvent = (ApplicationEvent)var9.next();
      this.getApplicationEventMulticaster().multicastEvent(earlyEvent);
    }
  }

}

3.11 finishBeanFactoryInitialization

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
  // 判斷有無ConversionService(bean屬性型別轉換服務介面),並初始化
  if (beanFactory.containsBean("conversionService") && beanFactory.isTypeMatch("conversionService",ConversionService.class)) {
    beanFactory.setConversionService((ConversionService)beanFactory.getBean("conversionService",ConversionService.class));
  }

  // 如果beanFactory中不包含EmbeddedValueResolver,則向其中新增一個EmbeddedValueResolver
  if (!beanFactory.hasEmbeddedValueResolver()) {// EmbeddedValueResolver-->解析bean中的佔位符和表示式
    beanFactory.addEmbeddedValueResolver((strVal) -> {
      return this.getEnvironment().resolvePlaceholders(strVal);
    });
  }

  // 初始化LoadTimeWeaverAware型別的bean
  // LoadTimeWeaverAware-->載入Spring Bean時織入第三方模組,如AspectJ
  String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class,false,false);
  String[] var3 = weaverAwareNames;
  int var4 = weaverAwareNames.length;

  for(int var5 = 0; var5 < var4; ++var5) {
    String weaverAwareName = var3[var5];
    this.getBean(weaverAwareName);
  }

  // 釋放臨時類載入器
  beanFactory.setTempClassLoader((ClassLoader)null);
  // 凍結快取的BeanDefinition元資料
  beanFactory.freezeConfiguration();
  // 初始化其他的非延遲載入的單例bean
  beanFactory.preInstantiateSingletons();
}

例項化 BeanFactory 中已經被註冊但是未例項化的所有例項(懶載入的不需要例項化),主要操作是 BeanFactory 的 preInstantiateSingletons 方法。該方法分為兩部分:

  1. 遍歷已經解析出來的所有 beanDefinitionNames,如果該 BeanDefinition 不是抽象類、是單例且沒有設定懶載入,則進行例項化和初始化。
  2. 遍歷解析出來的 beanDefinitionNames,如果獲得的單例是 SmartInitializingSingleton 的實現類,則會執行 afterSingletonsInstantiated 方法。注意該方法呼叫只會發生在啟動階段,後續懶載入物件再初始化的時候,不會再進行回撥。

3.12 finishRefresh

完成重新整理過程,通知生命週期處理器 lifecycleProcessor 重新整理過程,同時發出 ContextRefreshEvent 通知。

總結

到此這篇關於Spring IoC學習之ApplicationContext中refresh過程詳解的文章就介紹到這了,更多相關Spring ApplicationContext中refresh過程內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!