概念
Context也就是我們常說的spring容器,打個比方,context就像是一家公司,beans則是公司的工廠,除了工廠,公司還有翻譯,倉庫以及辦公場所等等。
下面就看看context的主要構成部件。
Context構成部件
上圖是ApplicationContext的實體靜態結構,它繼承了六個實體。雖然是繼承,但其實context和他們的關係更像是聚合。Spring使用繼承主要是爲了在context上也同時體現這6個實體的特徵。在實現層面,context事實上是個包裝類,最終通過聚合的實體類完成相應行爲,而ApplicationContext接口本身並沒有什麼實質意義的方法。
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
MessageSource, ApplicationEventPublisher, ResourcePatternResolver {
String getId();
String getApplicationName();
String getDisplayName();
long getStartupDate();
ApplicationContext getParent();
AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;
}
ApplicationContext的實質功能都是繼承於以下6個實體:
MessageSource: 用於國際化的接口,可以將其理解爲公司的翻譯。用戶可以通過bean配置自定義MessageSource–要求name爲“messageSource”,spring會在容器refresh時自動探測並且初始化它。
ApplicationEventPublisher: 用於發佈應用事件,例如ContextRefreshed,stopped, started等。它就像是企業郵箱,通過它可以接收到公司的事件通知。用戶通過配置ApplicationListener類型的bean即可訂閱這類事件,spring通過getBeansOfType取得所有的listener,並依次通知。
ResourcePatternResolver: 對ResourceLoader的擴展,後者只支持對具體路徑資源的加載,而前者則支持對某pattern路徑資源的加載,默認是ant風格的模式。Resource是特指配置文件,或者class路徑(裏的掃描路徑)。我們可以將它理解爲打單的機器,將各個地方發過來的單子打出來。
ListableBeanFactory和HierachicalBeanFactory: 自然的context也是個工廠,context裏持有的依然是DefaultListableBeanFactory,通過它完成工廠的相應行爲。介紹下這兩個工廠,前者主要用於取得批量bean,比如getBeansOfType;後一個工廠則主要體現層級概念,但是context的parentFactory也是一個context,這是因爲context具有beanFactory的所有特徵。
EnviromentCapable: 則類似是公司的行政部門,負責辦公場所等設施維護。它關聯着Enviroment。Enviroment也類似context是個包裝類,雖然繼承了PropertyResolver,但在實現類裏是委託給ConfigurablePropertyResolver處理的。Enviroment代表應用環境,比如測試環境還是生產環境又或者開發環境。
用戶可以通過或者@Profile指定某個配置的profile。然後通過activeProfile指定應用環境,從而會enable相應profile的beans
activeProfile可以通過5種方式指定:
1). servlet config init param,這種方式只適用於spring mvc的dispatcherServlet配置上
2). servlet context init param
3). system property
4). system env。以上四種方式設置的key均爲spring.profiles.active
5.) @ActiveProfile,這種方式只適用於junit單元測試
Context通過繼承獲得了工廠,事件發佈,環境定義,資源加載以及國際化的能力。
context靜態結構
這一節我只抽一些比較重要的接口的源碼講述,主要還是注重概念和原理,後面會專門出一篇講context的動態處理過程,那裏面會對具體實現類做詳述。這一篇澤主要是建立對context靜態類結構的理解。
下圖是web application context的類圖,可以和構成部件的結構圖結合着看,上面的圖每個實體都是spring context的一個接口。
常見的WebApplicationContext實現主要有兩個,分別是XmlWebApplicationContext和AnnotationConfigWebApplicationContext。他們共同的父類爲AbstractRefreshableWebApplicationContext,我們從它出發,看一下context的類結構。
LifeCycle和Closeable代表着容器的整個生命週期,被ConfigurableApplicationContext繼承,使繼承該接口的context具有生命週期的特徵。
public interface Lifecycle {
void start();
void stop();
boolean isRunning();
}
用戶可以配置Lifecycle類型bean,在context start以及stop時也會相應的啓動LifeCycle bean的start或者stop。對於允許autoStartup的SmartLifecycle,context refresh的過程中會自動啓動。
ConfigurableApplicationContext繼承了生命週期,代表一個可以修改相關屬性行爲的context,上一節提到的Enviroment和ApplicationListener等等都可以通過它設置。同時整個context的核心refresh方法也是定義在他裏面,所以所有的context都繼承了它。
public interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle, Closeable {
void setId(String id);
void setParent(ApplicationContext parent);
ConfigurableEnvironment getEnvironment();
void setEnvironment(ConfigurableEnvironment environment);
//添加bean後處理器,[beans架構](http://blog.csdn.net/szwandcj/article/details/50688616)架構裏詳細講過後處理器
void addBeanFactoryPostProcessor(BeanFactoryPostProcessor beanFactoryPostProcessor);
void addApplicationListener(ApplicationListener<?> listener);
//context核心方法,refresh是整個容器構建的過程
void refresh() throws BeansException, IllegalStateException;
//註冊jdk進程退出時的hook
void registerShutdownHook();
void close();
boolean isActive();
ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;
}
實現這個接口的AbstractApplicationContext很自然的也承擔了context的絕大部分職能,上一節提過的context的六個方面的功能就幾乎都是由它實現,除BeanFactory以外,其它的4個實體都是聚合在這個類裏。而DefaultListableBeanFactory–那個最著名的BeanFactory則是由它的子類AbstractRefreshableApplicationContext生成,它自己通過模板方法使用。
AbstractRefreshableApplicationContext定義了loadBeanDefinitions模板方法,由具體的實現提供。它創建bean factory並load配置文件。beans配置文件load過程參考這裏
AbstractRefreshableConfigApplicationContext主要用於設置資源的路徑。幾乎所有的ApplicationContext都會繼承這個類,除非不需要讀取配置資源。
AbstractRefreshableWebApplicationContext主要針對web的一些特性提供一些context屬性行爲設置能力,例如覆蓋默認的enviroment(默認的由AbstractApplicationContext提供),生成StandardServletEnviroment,又例如覆蓋ResourcePatternResolver以及對BeanFactory的後處理等。
ConfigurableWebApplicationContext則是web application context的通用接口,爲了支持spring-mvc和spring-web定義了一些set方法,例如設置configLocations(ConfigurableApplicationContext接口並不支持setConfigLocation,因爲這個接口是跟着ClassPathXmlApplicationContext一起發佈的,而web和mvc是後來加上的功能)和nameSpace等。可以把它看成是專門支持web和mvc的一個接口(web和mvc中生成context是通過class.newInstance的無參構造,所以無法將這些信息作爲參數傳入,必須顯示提供set方法以設置屬性)
public interface ConfigurableWebApplicationContext extends WebApplicationContext, ConfigurableApplicationContext {
void setServletContext(ServletContext servletContext);
void setServletConfig(ServletConfig servletConfig);
ServletConfig getServletConfig();
void setNamespace(String namespace);
String getNamespace();
void setConfigLocation(String configLocation);
void setConfigLocations(String... configLocations);
String[] getConfigLocations();
}
Enviroment實現
再具體說下Enviroment的實現,還是挺有意思的。下圖是enviroment的序列圖
主要說下MutablePropertySources和PropertySource。
最終屬性值是通過PropertySource取得,它有多種實現,分別代表一種來源的屬性。第二節提到的4種來源則分別對應着ServletConfigPropertySource,ServletContextPropertySource,MapPropertySource和SystemEnvironmentPropertySource。
MutablePropertySources裏持有一個property的list,對它迭代直到從一個PropertySource裏取出對應key的值就停止,從這可以就看出spring.profiles.active是有先後優先級的。
有興趣的可以看下源碼,會稍微有些繁瑣。有一點需要注意:spring web和mvc模塊會在refresh applicationContext之前調用相應enviroment#initPropertySources生成ServletConfigPropertySource和ServletContextPropertySource。相應的MapPropertySource和SystemEnvironmentPropertySource則是默認生成的。所以如果不是web項目,就只能通過配置system properties或者env指定activeProfile。