歡迎光臨
每天分享高質量文章

【死磕 Spring】—– ApplicationContext 相關介面架構分析

在前面 30 多篇部落格中都是基於 BeanFactory 這個容器來進行分析的,BeanFactory 容器有點兒簡單,它並不適用於我們生產環境,在生產環境我們通常會選擇 ApplicationContext ,相對於大多數人而言,它才是正規軍,相比於 BeanFactory 這個雜牌軍而言,它由如下幾個區別:
  1. 繼承 MessageSource,提供國際化的標準訪問策略。
  2. 繼承 ApplicationEventPublisher ,提供強大的事件機制。
  3. 擴充套件 ResourceLoader,可以用來載入多個 Resource,可以靈活訪問不同的資源。
  4. 對 Web 應用的支援。

ApplicationContext

下圖是 ApplicationContext 結構類圖:

  • BeanFactory:Spring 管理 Bean 的頂層介面,我們可以認為他是一個簡易版的 Spring 容器。ApplicationContext 繼承 BeanFactory 的兩個子類:HierarchicalBeanFactory 和 ListableBeanFactory。HierarchicalBeanFactory 是一個具有層級關係的 BeanFactory,擁有屬性 parentBeanFactory。ListableBeanFactory 實現了列舉方法可以列舉出當前 BeanFactory 中所有的 bean 物件而不必根據 name 一個一個的獲取。
  • ApplicationEventPublisher:用於封裝事件釋出功能的介面,向事件監聽器(Listener)傳送事件訊息。
  • ResourceLoader:Spring 載入資源的頂層介面,用於從一個源載入資源檔案。ApplicationContext 繼承 ResourceLoader 的子類 ResourcePatternResolver,該介面是將 location 解析為 Resource 物件的策略介面。
  • MessageSource:解析 message 的策略介面,用不支撐國際化等功能。
  • EnvironmentCapable:用於獲取 Environment 的介面。

ApplicationContext 的子介面

ApplicationContext 有兩個直接子類:WebApplicationContext 和 ConfigurableApplicationContext。

WebApplicationContext

  1. public interface WebApplicationContext extends ApplicationContext {
  2.    ServletContext getServletContext();
  3. }

該介面只有一個 getServletContext() ,用於給 servlet 提供背景關係資訊。

ConfigurableApplicationContext

  1. public interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle, Closeable {
  2. // 為 ApplicationContext 設定唯一 ID
  3. void setId(String id);
  4.  
  5. // 為 ApplicationContext 設定 parent
  6. // 父類不應該被修改:如果建立的物件不可用時,則應該在建構式外部設定它
  7. void setParent(@Nullable ApplicationContext parent);
  8.  
  9. // 設定 Environment
  10. void setEnvironment(ConfigurableEnvironment environment);
  11.  
  12. // 獲取 Environment
  13. @Override
  14. ConfigurableEnvironment getEnvironment();
  15.  
  16. // 新增 BeanFactoryPostProcessor
  17. void addBeanFactoryPostProcessor(BeanFactoryPostProcessor postProcessor);
  18.  
  19. // 新增 ApplicationListener
  20. void addApplicationListener(ApplicationListener> listener);
  21.  
  22. // 新增 ProtocolResolver
  23. void addProtocolResolver(ProtocolResolver resolver);
  24.  
  25. // 載入或者掃清配置
  26. // 這是一個非常重要的方法
  27. void refresh() throws BeansException, IllegalStateException;
  28.  
  29. // 註冊 shutdown hook
  30. void registerShutdownHook();
  31.  
  32. // 關閉 ApplicationContext
  33. @Override
  34. void close();
  35.  
  36. // ApplicationContext 是否處於啟用狀態
  37. boolean isActive();
  38.  
  39. // 獲取當前背景關係的 BeanFactory
  40. ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;
  41. }

從上面程式碼可以看到 ConfigurableApplicationContext 介面提供的方法都是對 ApplicationContext 進行配置的,例如 setEnvironment()、 addBeanFactoryPostProcessor,同時它還繼承瞭如下兩個介面:

  • Lifecycle:對 context 生命週期的管理,它提供 start() 和 stop() 方法啟動和暫停元件。
  • Closeable:標準 JDK 所提供的一個介面,用於最後關閉元件釋放資源等。

WebApplicationContext 介面和 ConfigurableApplicationContext 介面有一個共同的子類介面 ConfigurableWebApplicationContext,該介面將這兩個介面進行合併,提供了一個可配置、可管理、可關閉的WebApplicationContext,同時該介面還增加了 setServletContext(), setServletConfig()等方法,用於裝配WebApplicationContext。

  1. public interface ConfigurableWebApplicationContext extends WebApplicationContext, ConfigurableApplicationContext {
  2. void setServletContext(@Nullable ServletContext servletContext);
  3.  
  4. void setServletConfig(@Nullable ServletConfig servletConfig);
  5.  
  6. ServletConfig getServletConfig();
  7.  
  8. void setNamespace(@Nullable String namespace);
  9.  
  10. String getNamespace();
  11.  
  12. void setConfigLocation(String configLocation);
  13.  
  14. void setConfigLocations(String... configLocations);
  15.  
  16. String[] getConfigLocations();
  17. }

上面三個介面就可以構成一個比較完整的 Spring 容器,整個 Spring 容器體系涉及的介面較多,所以下麵小編就一個具體的實現類來看看 ApplicationContext 的實現(其實在前面一系列的文章中,小編對涉及的大部分介面都已經分析了其原理),當然不可能每個方法都涉及到,但小編會把其中最為重要的實現方法貼出來分析。ApplicationContext 的實現類較多,就以 ClassPathXmlApplicationContext 來分析 ApplicationContext。

ClassPathXmlApplicationContext

ClassPathXmlApplicationContext 是我們在學習 Spring 過程中用的非常多的一個類,很多人第一個接觸的 Spring 容器就是它,包括小編自己,下麵程式碼我想很多人依然還記得吧。

  1. ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
  2. StudentService studentService = (StudentService)ac.getBean("studentService");

下圖是 ClassPathXmlApplicationContext 的結構類圖:

主要的的類層級關係如下:

  1. org.springframework.context.support.AbstractApplicationContext
  2.      org.springframework.context.support.AbstractRefreshableApplicationContext
  3.            org.springframework.context.support.AbstractRefreshableConfigApplicationContext
  4.                  org.springframework.context.support.AbstractXmlApplicationContext
  5.                        org.springframework.context.support.ClassPathXmlApplicationContext

這種設計是模板方法樣式典型的應用,AbstractApplicationContext 實現了 ConfigurableApplicationContext 這個全家桶介面,其子類 AbstractRefreshableConfigApplicationContext 又實現了 BeanNameAware 和 InitializingBean 介面。所以 ClassPathXmlApplicationContext 設計的頂級介面有:

  1. BeanFactorySpring 容器 Bean 的管理
  2. MessageSource:管理 message ,實現國際化等功能
  3. ApplicationEventPublisher:事件釋出
  4. ResourcePatternResolver:資源載入
  5. EnvironmentCapable:系統 Environmentprofile + Properties 相關
  6. Lifecycle:管理生命週期
  7. Closeable:關閉,釋放資源
  8. InitializingBean:自定義初始化
  9. BeanNameAware:設定 beanName Aware 介面

下麵就這些介面來一一分析。

MessageSource

MessageSource 定義了獲取 message 的策略方法 getMessage(),在 ApplicationContext 體系中,該方法 AbstractApplicationContext 實現,在 AbstractApplicationContext 中,它持有一個 MessageSource 實體,將 getMessage() 的實現給該實體來實現,如下:

  1. private MessageSource messageSource;
  2.  
  3. // 實現 getMessage()
  4. public String getMessage(String code, @Nullable Object[] args, @Nullable String defaultMessage, Locale locale) {
  5.    // 委託給 messageSource 實現
  6.  return getMessageSource().getMessage(code, args, defaultMessage, locale);
  7. }
  8.  
  9. private MessageSource getMessageSource() throws IllegalStateException {
  10.  if (this.messageSource == null) {
  11.   throw new IllegalStateException("MessageSource not initialized - " +
  12.     "call 'refresh' before accessing messages via the context: " + this);
  13.  }
  14.  return this.messageSource;
  15. }

真正實現 “ 是在 AbstractMessageSource 中,如下:

  1. public final String getMessage(String code, @Nullable Object[] args, @Nullable String defaultMessage, Locale locale) {
  2.  String msg = getMessageInternal(code, args, locale);
  3.  if (msg != null) {
  4.   return msg;
  5.  }
  6.  if (defaultMessage == null) {
  7.   return getDefaultMessage(code);
  8.  }
  9.  return renderDefaultMessage(defaultMessage, args, locale);
  10. }

具體的實現這裡就不分析了,有興趣的小夥伴可以自己去深入研究。

ApplicationEventPublisher

用於封裝事件釋出功能的介面,向事件監聽器(Listener)傳送事件訊息。

該介面提供了一個 publishEvent() 用於通知在此應用程式中註冊的所有的監聽器。該方法在 AbstractApplicationContext 中實現。

  1. @Override
  2. public void publishEvent(Object event) {
  3.  publishEvent(event, null);
  4. }
  5.  
  6. protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
  7.  Assert.notNull(event, "Event must not be null");
  8.  if (logger.isTraceEnabled()) {
  9.   logger.trace("Publishing event in " + getDisplayName() + ": " + event);
  10.  }
  11.  
  12.  ApplicationEvent applicationEvent;
  13.  if (event instanceof ApplicationEvent) {
  14.   applicationEvent = (ApplicationEvent) event;
  15.  }
  16.  else {
  17.   applicationEvent = new PayloadApplicationEvent<>(this, event);
  18.   if (eventType == null) {
  19.    eventType = ((PayloadApplicationEvent) applicationEvent).getResolvableType();
  20.   }
  21.  }
  22.  
  23.  if (this.earlyApplicationEvents != null) {
  24.   this.earlyApplicationEvents.add(applicationEvent);
  25.  }
  26.  else {
  27.   getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
  28.  }
  29.  
  30.  if (this.parent != null) {
  31.   if (this.parent instanceof AbstractApplicationContext) {
  32.    ((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
  33.   }
  34.   else {
  35.    this.parent.publishEvent(event);
  36.   }
  37.  }
  38. }

如果指定的事件不是ApplicationEvent,則它將包裝在PayloadApplicationEvent中。如果存在父級 ApplicationContext ,則同樣要將 event 釋出給父級 ApplicationContext。

ResourcePatternResolver

ResourcePatternResolver 介面繼承 ResourceLoader 介面,為將 location 解析為 Resource 物件的策略介面。他提供的 getResources() 在 AbstractApplicationContext 中實現,在 AbstractApplicationContext 中他持有一個 ResourcePatternResolver 的實體物件。

如下:

  1. public Resource[] getResources(String locationPattern) throws IOException {
  2.  return this.resourcePatternResolver.getResources(locationPattern);
  3. }

如果小夥伴對 Spring 的 ResourceLoader 比較熟悉的話,你會發現最終是在 PathMatchingResourcePatternResolver 中實現,該類是 ResourcePatternResolver 介面的實現者。

EnvironmentCapable

提供當前系統環境 Environment 元件。提供了一個 getEnvironment() 用於傳回 Environment 實體物件,該方法在 AbstractApplicationContext 實現。

  1. public ConfigurableEnvironment getEnvironment() {
  2.  if (this.environment == null) {
  3.   this.environment = createEnvironment();
  4.  }
  5.  return this.environment;
  6. }

如果持有的 environment 實體物件為空,則呼叫 createEnvironment() 建立一個。

  1. protected ConfigurableEnvironment createEnvironment() {
  2.  return new StandardEnvironment();
  3. }

StandardEnvironment 是一個適用於非 WEB 應用的 Environment。

Lifecycle

一個用於管理宣告週期的介面。

在 AbstractApplicationContext 中存在一個 LifecycleProcessor 型別的實體物件 lifecycleProcessor,AbstractApplicationContext 中關於 Lifecycle 介面的實現都是委託給 lifecycleProcessor 實現的。如下:

  1. @Override
  2. public void start() {
  3.  getLifecycleProcessor().start();
  4.  publishEvent(new ContextStartedEvent(this));
  5. }
  6.  
  7. @Override
  8. public void stop() {
  9.  getLifecycleProcessor().stop();
  10.  publishEvent(new ContextStoppedEvent(this));
  11. }
  12.  
  13. @Override
  14. public boolean isRunning() {
  15.  return (this.lifecycleProcessor != null && this.lifecycleProcessor.isRunning());
  16. }

在啟動、停止的時候會分別釋出 ContextStartedEvent 和 ContextStoppedEvent 事件。

Closeable

Closeable 介面用於關閉和釋放資源,提供了 close() 以釋放物件所持有的資源。在 ApplicationContext 體系中由AbstractApplicationContext 實現,用於關閉 ApplicationContext 銷毀所有 bean ,此外如果註冊有 JVM shutdown hook,同樣要將其移除。如下:

  1. public void close() {
  2.  synchronized (this.startupShutdownMonitor) {
  3.   doClose();
  4.   // If we registered a JVM shutdown hook, we don't need it anymore now:
  5.   // We've already explicitly closed the context.
  6.   if (this.shutdownHook != null) {
  7.    try {
  8.     Runtime.getRuntime().removeShutdownHook(this.shutdownHook);
  9.    }
  10.    catch (IllegalStateException ex) {
  11.     // ignore - VM is already shutting down
  12.    }
  13.   }
  14.  }
  15. }

呼叫 doClose() 釋出 ContextClosedEvent 事件,銷毀所有 bean(單例),關閉 BeanFactory 。如下:

  1. protected void doClose() {
  2.     // 省略部分程式碼
  3.   try {
  4.    // Publish shutdown event.
  5.    publishEvent(new ContextClosedEvent(this));
  6.   }
  7.   catch (Throwable ex) {
  8.    logger.warn("Exception thrown from ApplicationListener handling ContextClosedEvent", ex);
  9.   }
  10.  
  11.         // 省略部分程式碼
  12.   destroyBeans();
  13.   closeBeanFactory();
  14.   onClose();
  15.  
  16.   this.active.set(false);
  17.  }
  18. }

InitializingBean

InitializingBean 為 bean 提供了初始化方法的方式,它提供的 afterPropertiesSet() 用於執行初始化動作。在 ApplicationContext 體系中,該方法由 AbstractRefreshableConfigApplicationContext 實現,如下:

  1. public void afterPropertiesSet() {
  2.  if (!isActive()) {
  3.   refresh();
  4.  }
  5. }

執行 refresh() ,該方法在 AbstractApplicationContext 中執行,執行整個 Spring 容器的初始化過程。該方法將在下篇文章進行詳細分析說明。

BeanNameAware

設定 bean name 的介面。介面在 AbstractRefreshableConfigApplicationContext 中實現。

  1. public void setBeanName(String name) {
  2.  if (!this.setIdCalled) {
  3.   super.setId(name);
  4.   setDisplayName("ApplicationContext '" + name + "'");
  5.  }
  6. }

由於篇幅問題再加上大部分介面小編都已經在前面文章進行了詳細的闡述,所以本文主要是以 Spring Framework 的 ApplicationContext 為中心,對其結構和功能的實現進行了簡要的說明。這裡不得不說 Spring 真的是一個非常優秀的框架,具有良好的結構設計和介面抽象,它的每一個介面職能單一,且都是具體功能到各個模組的高度抽象,且幾乎每套介面都提供了一個預設的實現(defaultXXX)。對於 ApplicationContext 體系而言,他繼承 Spring 中眾多的核心介面,能夠為客戶端提供一個相對完整的 Spring 容器,介面 ConfigurableApplicationContext 對 ApplicationContext 介面再次進行擴充套件,提供了生命週期的管理功能。抽象類 ApplicationContext 對整套介面提供了大部分的預設實現,將其中“不易變動”的部分進行了封裝,透過“組合”的方式將“容易變動”的功能委託給其他類來實現,同時利用模板方法樣式將一些方法的實現開放出去由子類實現,從而實現“對擴充套件開放,對修改封閉”的設計原則。

最後我們再來領略下圖的風采:

贊(0)

分享創造快樂