Spring IoC之ApplicationContext

概述ApplicationContextApplicationContext 的子接口WebApplicationContextConfigurableApplicationContextConfigurableWebApplicationContextClassPathXmlApplicationContextMessageSourceApplicationEventPublisherResourcePatternResolverEnvironmentCapableLifecycleCloseableInitializingBeanBeanNameAware總結參考文獻html

概述

Spring之IoC理論一章中提到關於 IoC 的學習主要涉及到五大模塊,從 Resource 和 ResourceLoader 用於資源管理開始,而後講述 BeanDefinitionReader 如何將 Resource 轉換爲 IoC 容器獨特的數據存儲對象:BeanDefinition,緊接着又介紹了 BeanFactory 是如何實現 getBean()方法來獲取 bean 實例對象。至此關於 IoC 的實現流程其實已經大概介紹完畢了,其中主要基於 BeanFactory 進行分析的,可是在實際生產環境中,咱們使用比較多的是 ApplicationContext,該接口包含 BeanFactory 的全部功能,同時又提供了更多的擴展功能。java

ApplicationContext 相比於 BeanFactory 有如下幾個區別:web

  • 繼承 MessageSource,提供國際化的標準訪問策略。
  • 繼承 ApplicationEventPublisher ,提供強大的事件機制。
  • 擴展 ResourceLoader,能夠用來加載多個 Resource,能夠靈活訪問不一樣的資源。
  • 對 Web 應用的支持。

接着步入正題,咱們簡單學習一下 ApplicationContext。spring

ApplicationContext

下圖是 ApplicationContext 結構類圖:數據庫

能夠看到 ApplicationContext 實現了不少個接口,這裏咱們簡單介紹幾個接口:api

  • BeanFactory:Spring 管理 Bean 的頂層接口,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。session

WebApplicationContext

public interface WebApplicationContext extends ApplicationContext {
    //
    String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";
    //請求範圍的範圍標識符:「 request」。
    String SCOPE_REQUEST = "request";
    //會話範圍的範圍標識符:「會話」。
    String SCOPE_SESSION = "session";
    //SCOPE_APPLICATION
    String SCOPE_APPLICATION = "application";
    //工廠中ServletContext環境Bean的名稱。
    String SERVLET_CONTEXT_BEAN_NAME = "servletContext";
    //工廠中ServletContext init-params環境Bean的名稱。
    String CONTEXT_PARAMETERS_BEAN_NAME = "contextParameters";
    //工廠中ServletContext屬性環境bean的名稱
    String CONTEXT_ATTRIBUTES_BEAN_NAME = "contextAttributes";

    @Nullable
    ServletContext getServletContext();
}
複製代碼

該接口提供 Web 應用程序配置的界面。在應用程序運行時,它是隻讀的,可是若是實現支持,則能夠從新加載。該接口中的 getServletContext()方法向通用 ApplicationContext 接口添加了一個方法,並定義了在引導過程當中必須綁定根上下文的衆所周知的應用程序屬性名稱。app

像通用應用程序上下文同樣,Web 應用程序上下文是分層的。每一個應用程序只有一個根上下文,而應用程序中的每一個 servlet(包括MVC框架中的調度程序servlet)都有本身的子上下文。框架

除了標準的應用程序上下文生命週期功能外,WebApplicationContext 實現還須要檢測ServletContextAware Bean並相應地調用該setServletContext方法。webapp

ConfigurableApplicationContext

public interface ConfigurableApplicationContext extends ApplicationContextLifecycleCloseable {
    //在單個String值中,能夠將任意數量的這些字符視爲多個上下文配置路徑之間的分隔符。
    String CONFIG_LOCATION_DELIMITERS = ",; \t\n";
    //工廠中ConversionService bean的名稱。
    String CONVERSION_SERVICE_BEAN_NAME = "conversionService";
    //工廠中LoadTimeWeaver Bean的名稱。
    String LOAD_TIME_WEAVER_BEAN_NAME = "loadTimeWeaver";
    //Environment工廠中bean的名稱。
    String ENVIRONMENT_BEAN_NAME = "environment";
    //工廠中系統屬性Bean的名稱。
    String SYSTEM_PROPERTIES_BEAN_NAME = "systemProperties";
    //工廠中系統環境bean的名稱。
    String SYSTEM_ENVIRONMENT_BEAN_NAME = "systemEnvironment";
    //Name的關閉鉤子線程:「SpringContextShutdownHook」。
    String SHUTDOWN_HOOK_THREAD_NAME = "SpringContextShutdownHook";

    //設置此應用程序上下文的惟一ID。
    void setId(String var1);

    //設置此應用程序上下文的父級。
    void setParent(@Nullable ApplicationContext var1);

    //設置Environment這個應用程序上下文。
    void setEnvironment(ConfigurableEnvironment var1);

    //Environment以可配置的形式返回此應用程序上下文的,以便進行進一步的自定義。
    ConfigurableEnvironment getEnvironment();

    //添加一個新的BeanFactoryPostProcessor,它將在刷新以前應用於此應用程序上下文的內部Bean工廠,而後再評估任何Bean定義。
    void addBeanFactoryPostProcessor(BeanFactoryPostProcessor var1);

    //添加一個新的ApplicationListener,它將在上下文事件(例如上下文刷新和上下文關閉)時收到通知。
    void addApplicationListener(ApplicationListener<?> var1);

    //在此應用程序上下文中註冊給定的協議解析器,從而容許處理其餘資源協議。
    void addProtocolResolver(ProtocolResolver var1);

    //加載或刷新配置的持久表示形式,多是XML文件,屬性文件或關係數據庫模式。
    void refresh() throws BeansException, IllegalStateException;

    //向JVM運行時註冊一個shutdown掛鉤,除非JVM當時已經關閉,不然在JVM關閉時關閉該上下文。
    void registerShutdownHook();

    //關閉此應用程序上下文,釋放實現可能持有的全部資源和鎖。
    void close();

    //肯定此應用程序上下文是否處於活動狀態,即,是否至少刷新一次而且還沒有關閉。
    boolean isActive();

    //返回此應用程序上下文的內部bean工廠。
    ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;
}
複製代碼

從上面代碼能夠看到 ConfigurableApplicationContext 接口提供的方法都是對 ApplicationContext 進行配置的,例如 setEnvironment()addBeanFactoryPostProcessor,同時它還繼承了以下兩個接口:

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

ConfigurableWebApplicationContext

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

public interface ConfigurableWebApplicationContext extends WebApplicationContextConfigurableApplicationContext {
    //引用上下文路徑和/或Servlet名稱的ApplicationContext ID的前綴。
    String APPLICATION_CONTEXT_ID_PREFIX = WebApplicationContext.class.getName() + ":";
    //工廠中ServletConfig環境Bean的名稱。
    String SERVLET_CONFIG_BEAN_NAME = "servletConfig";

    //爲此Web應用程序上下文設置ServletContext。
    void setServletContext(@Nullable ServletContext var1);

    //爲此Web應用程序上下文設置ServletConfig。
    void setServletConfig(@Nullable ServletConfig var1);

    //返回此Web應用程序上下文的ServletConfig(若是有)。
    @Nullable
    ServletConfig getServletConfig();

    //設置此Web應用程序上下文的名稱空間,以用於構建默認的上下文配置位置。
    void setNamespace(@Nullable String var1);

    //返回此Web應用程序上下文的名稱空間(若是有)。
    @Nullable
    String getNamespace();

    //以init-param樣式設置此Web應用程序上下文的配置位置
    void setConfigLocation(String var1);

    //設置此Web應用程序上下文的配置位置。
    void setConfigLocations(String... var1);

    //返回此Web應用程序上下文的配置位置,null若是未指定,則返回。
    @Nullable
    String[] getConfigLocations();
}
複製代碼

ClassPathXmlApplicationContext

ClassPathXmlApplicationContext 是咱們在學習 Spring 過程當中使用很是多的一個類,通常使用 ApplicationContext 接口,大都是採用該類。

ApplicationContext context = new ClassPathXmlApplicationContext("application_context.xml");

User user = (User) context.getBean("user");
複製代碼

下圖是 ClassPathXmlApplicationContext 的結構類圖:

主要的類層級關係以下:

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

BeanFactory:Spring 容器 Bean 的管理
MessageSource:管理 message ,實現國際化等功能
ApplicationEventPublisher:事件發佈
ResourcePatternResolver:資源加載
EnvironmentCapable:系統 Environment(profile + Properties) 相關
Lifecycle:管理生命週期
Closeable:關閉,釋放資源
InitializingBean:自定義初始化
BeanNameAware:設置 beanName 的 Aware 接口
複製代碼

下面就這些接口來一一分析。

MessageSource

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

private MessageSource messageSource;

public String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException {
    return this.getMessageSource().getMessage(resolvable, locale);
}

private MessageSource getMessageSource() throws IllegalStateException {
    if (this.messageSource == null) {
        throw new IllegalStateException("MessageSource not initialized - call 'refresh' before accessing messages via the context: " + this);
    } else {
        return this.messageSource;
    }
}
複製代碼

該方法具體實如今 AbstractMessageSource 中,具體就不作分析了,有興趣的朋友能夠去研究一下。

此外還有一個 initMessageSource()方法,在 refresh()中被調用,用來初始化一些國際化的屬性。

protected void initMessageSource() {
    ConfigurableListableBeanFactory beanFactory = this.getBeanFactory();
    if (beanFactory.containsLocalBean("messageSource")) {
        this.messageSource = (MessageSource)beanFactory.getBean("messageSource", MessageSource.class);
        if (this.parent != null && this.messageSource instanceof HierarchicalMessageSource) {
            HierarchicalMessageSource hms = (HierarchicalMessageSource)this.messageSource;
            if (hms.getParentMessageSource() == null) {
                hms.setParentMessageSource(this.getInternalParentMessageSource());
            }
        }

        if (this.logger.isTraceEnabled()) {
            this.logger.trace("Using MessageSource [" + this.messageSource + "]");
        }
    } else {
        DelegatingMessageSource dms = new DelegatingMessageSource();
        dms.setParentMessageSource(this.getInternalParentMessageSource());
        this.messageSource = dms;
        beanFactory.registerSingleton("messageSource"this.messageSource);
        if (this.logger.isTraceEnabled()) {
            this.logger.trace("No 'messageSource' bean, using [" + this.messageSource + "]");
        }
    }

}
複製代碼
ApplicationEventPublisher

用於封裝事件發佈功能的接口,向事件監聽器(Listener)發送事件消息。

該接口提供了一個 publishEvent() 用於通知在此應用程序中註冊的全部的監聽器。該方法在 AbstractApplicationContext 中實現。

public void publishEvent(ApplicationEvent event) {
    this.publishEvent(event, (ResolvableType)null);
}

public void publishEvent(Object event) {
    this.publishEvent(event, (ResolvableType)null);
}

protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
    Assert.notNull(event, "Event must not be null");
    Object applicationEvent;
    if (event instanceof ApplicationEvent) {
        applicationEvent = (ApplicationEvent)event;
    } else {
        applicationEvent = new PayloadApplicationEvent(this, event);
        if (eventType == null) {
            eventType = ((PayloadApplicationEvent)applicationEvent).getResolvableType();
        }
    }

    if (this.earlyApplicationEvents != null) {
        this.earlyApplicationEvents.add(applicationEvent);
    } else {
        this.getApplicationEventMulticaster().multicastEvent((ApplicationEvent)applicationEvent, eventType);
    }

    if (this.parent != null) {
        if (this.parent instanceof AbstractApplicationContext) {
            ((AbstractApplicationContext)this.parent).publishEvent(event, eventType);
        } else {
            this.parent.publishEvent(event);
        }
    }

}
複製代碼

若是指定的事件不是 ApplicationEvent,則它將包裝在 PayloadApplicationEvent 中。若是存在父級 ApplicationContext ,則一樣要將 event 發佈給父級 ApplicationContext。

ResourcePatternResolver

ResourcePatternResolver 接口繼承 ResourceLoader 接口,爲將 location 解析爲 Resource 對象的策略接口。它提供的 getResources() 在 AbstractApplicationContext 中實現,在 AbstractApplicationContext 中它持有一個 ResourcePatternResolver 的實例對象。 其定義以下:

public Resource[] getResources(String locationPattern) throws IOException {
    return this.resourcePatternResolver.getResources(locationPattern);
}
複製代碼

該方法的具體實如今 PathMatchingResourcePatternResolver 類中,在 Spring IoC資源管理之ResourceLoader一節中有講解。

EnvironmentCapable

提供當前系統環境 Environment 組件。提供了一個 getEnvironment() 用於返回 Environment 實例對象,該方法在 AbstractApplicationContext 實現。

public ConfigurableEnvironment getEnvironment() {
    if (this.environment == null) {
        this.environment = this.createEnvironment();
    }

    return this.environment;
}
複製代碼

若是持有的 environment 實例對象爲空,則調用 createEnvironment() 建立一個。

 protected ConfigurableEnvironment createEnvironment() {
  return new StandardEnvironment();
 }
複製代碼

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

Lifecycle

一個用於管理聲明週期的接口。

在 AbstractApplicationContext 中存在一個 LifecycleProcessor 類型的實例對象 lifecycleProcessor,AbstractApplicationContext 中關於 Lifecycle 接口的實現都是委託給 lifecycleProcessor 實現的。以下:

public void start() {
    this.getLifecycleProcessor().start();
    this.publishEvent((ApplicationEvent)(new ContextStartedEvent(this)));
}

public void stop() {
    this.getLifecycleProcessor().stop();
    this.publishEvent((ApplicationEvent)(new ContextStoppedEvent(this)));
}

public boolean isRunning() {
    return this.lifecycleProcessor != null && this.lifecycleProcessor.isRunning();
}
複製代碼

在啓動、中止的時候會分別發佈 ContextStartedEvent 和 ContextStoppedEvent 事件。

Closeable

Closeable 接口用於關閉和釋放資源,提供了 close() 以釋放對象所持有的資源。在 ApplicationContext 體系中由AbstractApplicationContext 實現,用於關閉 ApplicationContext 銷燬全部 bean ,此外若是註冊有 JVM shutdown hook,一樣要將其移除。以下:

public void close() {
    synchronized(this.startupShutdownMonitor) {
        this.doClose();
        if (this.shutdownHook != null) {
            try {
                Runtime.getRuntime().removeShutdownHook(this.shutdownHook);
            } catch (IllegalStateException var4) {
            }
        }

    }
}
複製代碼

調用 doClose() 發佈 ContextClosedEvent 事件,銷燬全部 bean(單例),關閉 BeanFactory 。以下:

protected void doClose() {
    if (this.active.get() && this.closed.compareAndSet(falsetrue)) {
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Closing " + this);
        }

        LiveBeansView.unregisterApplicationContext(this);

        try {
            this.publishEvent((ApplicationEvent)(new ContextClosedEvent(this)));
        } catch (Throwable var3) {
            this.logger.warn("Exception thrown from ApplicationListener handling ContextClosedEvent", var3);
        }

        if (this.lifecycleProcessor != null) {
            try {
                this.lifecycleProcessor.onClose();
            } catch (Throwable var2) {
                this.logger.warn("Exception thrown from LifecycleProcessor on context close", var2);
            }
        }

        this.destroyBeans();
        this.closeBeanFactory();
        this.onClose();
        if (this.earlyApplicationListeners != null) {
            this.applicationListeners.clear();
            this.applicationListeners.addAll(this.earlyApplicationListeners);
        }

        this.active.set(false);
    }

}
複製代碼
InitializingBean

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

public void afterPropertiesSet() {
    if (!this.isActive()) {
        this.refresh();
    }

}
複製代碼

執行 refresh() ,該方法在 AbstractApplicationContext 中執行,執行整個 Spring 容器的初始化過程。該方法在 Spring IoC之ClassPathXmlApplicationContext 一節中有介紹。

BeanNameAware

設置 bean name 的接口。接口在 AbstractRefreshableConfigApplicationContext 中實現。

 public void setBeanName(String name) {
  if (!this.setIdCalled) {
   super.setId(name);
   setDisplayName("ApplicationContext '" + name + "'");
  }
 }
複製代碼

總結

本文主要是以 Spring Framework 的 ApplicationContext 爲中心,對其結構和功能的實現進行了簡要的說明。 Spring 是一個很是優秀的框架,具備良好的結構設計和接口抽象,它的每個接口職能單一,且都是具體功能到各個模塊的高度抽象,且幾乎每套接口都提供了一個默認的實現(defaultXXX)。對於 ApplicationContext 體系而言,它繼承 Spring 中衆多的核心接口,可以爲客戶端提供一個相對完整的 Spring 容器,接口 ConfigurableApplicationContext 對 ApplicationContext 接口再次進行擴展,提供了生命週期的管理功能。抽象類 AbstractApplicationContext 對整套接口提供了大部分的默認實現,將其中「不易變更」的部分進行了封裝,經過「組合」的方式將「容易變更」的功能委託給其餘類來實現,同時利用模板方法模式將一些方法的實現開放出去由子類實現,從而實現「對擴展開放,對修改封閉」的設計原則。關於 ApplicationContext 的講解咱們主要以 ClassPathXmlApplicationContext 爲例,感興趣的朋友能夠前往 Spring IoC之ClassPathXmlApplicationContext 。

參考文獻

http://cmsblogs.com/?p=4036

相關文章
相關標籤/搜索