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

【死磕 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)

分享創造快樂