在java屆,有位名叫Rod Johnson的牛人,發現最初的java企業級開發處于混沌狀態。
于是,它決心編寫一個能夠解決問題的通用的基礎架構。
因為它深信面向接口編程能夠將變化控制到最小,同時也利于擴展和變化。于是,它編寫了如下的接口。
在混沌狀態最先要創造的是一切對象的母親BeanFactory,有了它,就能夠得到一切它孕育的對象和屬性,也就是說首先要造蓋亞--大地之母。
有了最初的母親BeanFactory,johnson想,如果我要得到一組Bean對象而不單單是某個或某幾個呢?另外,如果母親的孩子也要孕育對象呢?于是,johnson創造了ListableBeanFactory以操作一組bean對象,比如getBeansOfType就能夠根據得到同類型的一組Bean;創造了HierarchicalBeanFactory來解決多個BeanFactory的層次問題,比如getParentBeanFactory就能夠得到BeanFactory的父Factory。
這個BeanFactory最終是要在某個應用上使用的,那么,需要給予BeanFactory在一個應用中活動的能力。在BeanFactory中,只需要考慮跟bean相關的行為,比如怎么得到bean,bean的類型等;而如果要賦予其在應用中的能力,則就需要考慮更多,比如應用的名字,啟動時間,id等等跟應用本身相關的行為和屬性,于是johnson想到了創造ApplicationContext。johnson想,這個ApplicationContext一定要能做許多事,要能夠處理參數化和國際化的文本信息,于是增加了MessageSource接口;要能發布事件以便解耦組件,于是有了ApplicationEventPublisher接口;要能得到資源文件,于是有了ResourcePatternResolver接口;要能有在不同環境有不同處理對象的能力,于是有了EnvironmentCapable接口。
ApplicationContext繼承了所有這些接口。
但是最重要的是,無論是BeanFactory還是ApplicationContext,它們都需要有可配置的能力,于是有了子接口ConfigurableBeanFactory和ConfigurableApplicationContext;另外,web在當時是非常重要的趨勢,而且相比其他應用有些獨特,需要得到ServletContext,于是有了WebApplicationContext。
到目前為止,johnson都是面向接口進行行為的抽象思考,并未具體實現他們。
看著創造出來的BeanFactory和ApplicationContext,johnson意識到這一天的工作遠遠沒有結束,因為并沒有真正解決怎么能夠讓整套體系運轉起來,于是,johnson開始思索如何實現他們。
johoson首先想到的還是這個實現應該具備什么能力?當然要把之前提到的AutowireCapableBeanFactory,ListableBeanFactory和ConfigurableBeanFactory都包括進去。因此創造了ConfigurableListableBeanFactory。其次,需要考慮對于bean對象的幾種能力,一是起別名的能力;二是保存單例對象的能力;三是緩存的能力;他們分別在SimpleAliasRegistry類,DefaultSingletonBeanRegistry類和FactoryBeanRegistrySupport類中實現。
最終,創造出了DefaultListableBeanFactory,它是spring中一切ioc工廠的原型,是BeanFactory第一個真正的孩子,這個孩子非常重要,已經成為獨立的創建ioc容器的基礎,如果有擴展和使用,大多是繼承它或者組合使用它。
如果要初始化一個DefaultListableBeanFactory,可以用如下代碼
1
2
3
4
|
ClassPathResource res = new ClassPathResource( "applicationContext.xml" ); DefaultListableBeanFactory f = new DefaultListableBeanFactory(); XmlBeanDefinitionReader r = new XmlBeanDefinitionReader(f); r.loadBeanDefinitions(res); |
接下來johnson想,BeanFactory有了一個功能比較全面的默認實現,那么ApplicationContext呢?于是johnson孜孜不倦的創造了3個重要的ApplicationContext實現:FileSystemXmlApplicationContext, ClassPathXmlApplicationContext, AnnotationConfigWebApplicationContext(其實還有很多,比如處理portlet的, 處理web的)
johnson最先考慮的是如何去做spring的啟動流程,它應該放到一個比較抽象的層次以便下層的所有類能夠復用。于是他用一個AbstractApplicationContext實現了ConfigurableApplicationContext。還有一個很重要的功能,即將一個文件以資源的形式加載進來,這需要將資源抽象為Resource類,將定位資源的具體實現抽象到ResourceLoader,AbstractApplicationContext同樣需要繼承DefaultResourceLoader以提供這個功能。AbstractApplicationContext完成了整個啟動流程(上帝將它安排在第二天完成),唯獨沒有做對BeanFactory的管理。于是,它的子類AbstractRefreshableApplicationContext專門做了這件事,實現了refreshBeanFactory, closeBeanFactory, getBeanFactory專門對BeanFactory的生命周期做了一些管理,但是AbstractRefreshableApplicationContext仍然沒有加載所有配置好的Bean。到哪里加載配置好的資源,實際上到了下層的子類去做,比如FileSystemXmlApplicationContext,就是到文件系統去讀一個xml形式的applicationContext;ClassPathXmlApplicationContext則是到類加載路徑下去讀這個applicationContext。而AnnotationConfigWebApplicationContext則從類文件的annotation中加載bean,spring的掃描也從此開始。
看著主要的框架已經建立起來,johnson滿意的笑著睡著了。
頭一日,johnson完成了一個spring的整體框架。
第二日,johnson準備實際去處理前面遺留的問題。比如spring的容器初始化過程。如圖,johnson將這個過程分為很多子過程,這些子過程都在圍繞著如何將bean載入這一宏偉的目標而努力。
這個過程放在AbstractApplicationContext中的refresh方法中。代碼如下,johnson將refresh的過程分為很多子過程,并且這些子過程在同一個抽象層級上,這種寫法是為了給后人一個榜樣。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
public void refresh() throws BeansException, IllegalStateException { synchronized ( this .startupShutdownMonitor) { // Prepare this context for refreshing. prepareRefresh(); // Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory); // Initialize message source for this context. initMessageSource(); // Initialize event multicaster for this context. initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. onRefresh(); // Check for listener beans and register them. registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh(); } catch (BeansException ex) { // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } } } |
如果更高層次一些看,實際上這些過程圍繞著幾個方面來做:1. 刷新的生命周期;2. 對beanFactory的初始化及準備;3. 生成并注冊beanDefinition;4. beanFactory后處理器;5.設置消息,事件及監聽器。
1. 刷新的生命周期
prepareRefresh,這個過程主要記錄日志表示spring啟動了,初始化property資源(比如serlvet中一些資源初始化),以及property資源的驗證(比如只寫了key沒有value)。
onRefresh,目的是提供給一些特殊的ApplicationContext,使他們有能夠在刷新過程中能夠擴展的能力。目前使用到的大多是為servlet application context設置theme
finishRefresh,做一些收尾的工作,如初始化LifecycleProcessor,發布refresh結束的事件等。
cancelRefresh,主要是在異常產生時將當前的狀態改變為非active。
2. 對beanFactory的初始化及準備
johnson想,我們應該讓beanFactory在初始化時就把bean透明的加載并注冊好,這樣對外界而言,我的封裝就非常成功,因此這步實際上做了很多事。下圖省略了許多步驟,只列出關鍵點。
AbstractApplicationContext會調用refreshBeanFactory,它首先會檢查并關閉已有的beanFactory,其次新建一個beanFactory,然后利用該factory裝載所有BeanDefinition
其中,loadBeanDefinitions會交給子類做不同的實現,比如AbstractXmlApplicationContext主要是通過xml讀取;AnnotationConfigWebApplicationContext的實現則會調用掃描器掃描類中的bean
3. 生成并注冊beanDefinition
當解析完xml配置以后,DefaultBeanDefinitionDocumentReader的parseDefaultElement方法會根據xml中的元素做對應的處理。其中,遇到bean元素時會最終調用BeanDefinitionReaderUtils中的registerBeanDefinition方法,該方法傳入的參數為BeanDefinitionRegistry,實際上是回調了DefaultListableBeanFactory的registerBeanDefinition方法來注冊beanDefinition(DefaultListableBeanFactory實現了BeanDefinitionRegistry)。
4. beanFactory后處理器
beanFactory后處理器是spring提供出來的讓其子類靈活擴展的方式。spring中分為2個步驟:postProcessBeanFactory,invokeBeanFactoryPostProcessors。registerBeanPostProcessors則是實例化并調用所有的BeanPostProcessor,用來在bean初始化前和初始化后對bean做擴展。
5. 設置消息,事件及監聽器
設置默認消息源為DelegatingMessageSource,如工廠里已經有messageSource則使用該messageSource,事件多播器為SimpleApplicationEventMulticaster,如工廠里已經有applicationEventMulticaster,則使用該applicationEventMulticaster,并注冊所有的應用程序Listener以便能夠接收事件
消息源:MessageSource是國際化資源文件的重要方法,spring在applicationContext就支持消息源。
spring中提供了MessageSource的默認實現,使用java.util.ResourceBundle來提取消息。spring通過配置一個特殊id為messageSource的bean并制定i18n的文件名,就能夠從ApplicationContext.getMessage()直接訪問message。如果在jsp中,還能通過spring:message這個tag訪問到message。
事件:事件是比較重要的解耦機制,spring在核心ApplicationContext就引入了它,其原理比較簡單,一方面是事件產生方,能夠發送事件;一方面似乎事件監聽方,能夠響應事件。而具體實現基本上都是在產生方hold住一個事件監聽者集合,并將所有監聽方“注冊”(即加入)到這個事件監聽者集合。
spring中將ApplicationContext當做事件產生方,使用applicationListeners作為監聽者集合,applicationEventMulticaster用來做事件發布。
事件發布的幾個步驟:
訂閱:最初addApplicationListener將applicationListener加入監聽者集合。
發布:ApplicationContext繼承了ApplicationEventPublisher,因而實現了publishEvent,該方法首先會遍歷本applicationContext的applicationListeners集合,對每個listener調用onApplicationEvent,因此每個listener都會被通知到;這步完成后會對applicationContext的parent的所有applicationListeners做同樣的事件發布。
事件發布非常常用,不僅我們自己的應用可以使用這個事件發布,spring框架自身也在使用事件發布。下面是一些spring中事件的應用:
在finishRefresh中會發布ContextRefreshedEvent表明refresh結束借此通知listener。在ApplicationContext中start和stop方法會發布事件表示context開始或結束。
johnson將spring的主框架與運行流程創造完畢之后,發覺spring中提供了許多靈活擴展的地方。于是johnson準備在第三日將這些靈活擴展的用法公布出來。
1. BeanPostProcessor。BeanPostProcessor提供了bean創建完成后的擴展接口,當你需要在bean創建完后對其做一定處理,則BeanPostProcessor是首選的方式。
2. Aware。注入的bean需要了解其容器的某些部分,spring通過Aware完成回調,如BeanNameAware,可以讓bean得知自己的名字, BeanFactoryAware可以讓bean了解到BeanFactory, ApplicationContextAware,可以讓bean操作ApplicationContext。通過這種方式,注入spring的bean能夠做更加廣泛的事情。
對于BeanPostProcessor的擴展,spring自身有一個例子,即如何識別Aware bean的例子。Aware bean是比較特殊的bean,需要spring對其額外注入一些屬性,那么注入的過程spring會怎么做呢?實際上spring并沒有將他寫在核心的處理過程里面,而是放到了ApplicationContextAwareProcessor這個BeanPostProcessor,通過BeanPostProcessor的postProcessBeforeInitialization最終invokeAwareInterfaces以判斷該bean的類型并注入相應的屬性。這種做法利用了BeanPostProcessor完成了另一個擴展用法,實在是高超。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
private void invokeAwareInterfaces(Object bean) { if (bean instanceof Aware) { if (bean instanceof EnvironmentAware) { ((EnvironmentAware) bean).setEnvironment( this .applicationContext.getEnvironment()); } if (bean instanceof EmbeddedValueResolverAware) { ((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver( new EmbeddedValueResolver( this .applicationContext.getBeanFactory())); } if (bean instanceof ResourceLoaderAware) { ((ResourceLoaderAware) bean).setResourceLoader( this .applicationContext); } if (bean instanceof ApplicationEventPublisherAware) { ((ApplicationEventPublisherAware) bean).setApplicationEventPublisher( this .applicationContext); } if (bean instanceof MessageSourceAware) { ((MessageSourceAware) bean).setMessageSource( this .applicationContext); } if (bean instanceof ApplicationContextAware) { ((ApplicationContextAware) bean).setApplicationContext( this .applicationContext); } } } |
關于Aware的用法則更多了,比如如下代碼能夠感知ApplicationContext,spring在創建完這個bean之后便會注入ApplicationContext,于是我們就可以使用該context完成事件發布。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
public class HelloBean implements ApplicationContextAware { private ApplicationContext applicationContext; private String helloWord = "Hello!World!" ; public void setApplicationContext(ApplicationContext context) { this .applicationContext = context; } public void setHelloWord(String helloWord) { this .helloWord = helloWord; } public String getHelloWord() { applicationContext.publishEvent( new PropertyGettedEvent( "[" + helloWord + "] is getted" )); return helloWord; } } |
3. BeanFactoryPostProcessor,這個PostProcessor通常是用來處理在BeanFactory創建后的擴展接口。一個例子如下,當注入這個bean以后,它便會在BeanFactory創建完畢自動打印注入的bean數量:
1
2
3
4
5
6
7
8
9
|
public class BeanCounter implements BeanFactoryPostProcessor{ @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { System.out.println(beanFactory.getBeanDefinitionCount()); } } |
4. FactoryBean。FactoryBean是一種特殊的bean,這種bean允許注入到spring容器并用其生成真正的bean,所以可以這樣定義,FactoryBean本身是一種bean,這種bean又有能夠提供bean的能力。下面從FactoryBean的調用開始,講到spring是如何使用這個bean的。
要想區分普通bean和FactoryBean,spring也必須有判斷他們并特殊處理的過程,這個過程就在AbstractBeanFactory的getObjectForBeanInstance中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
protected Object getObjectForBeanInstance( Object beanInstance, String name, String beanName, RootBeanDefinition mbd) { // Don't let calling code try to dereference the factory if the bean isn't a factory. if (BeanFactoryUtils.isFactoryDereference(name) && !(beanInstance instanceof FactoryBean)) { throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass()); } // Now we have the bean instance, which may be a normal bean or a FactoryBean. // If it's a FactoryBean, we use it to create a bean instance, unless the // caller actually wants a reference to the factory. if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) { return beanInstance; } Object object = null ; if (mbd == null ) { object = getCachedObjectForFactoryBean(beanName); } if (object == null ) { // Return bean instance from factory. FactoryBean<?> factory = (FactoryBean<?>) beanInstance; // Caches object obtained from FactoryBean if it is a singleton. if (mbd == null && containsBeanDefinition(beanName)) { mbd = getMergedLocalBeanDefinition(beanName); } boolean synthetic = (mbd != null && mbd.isSynthetic()); object = getObjectFromFactoryBean(factory, beanName, !synthetic); } return object; } |
可以看出來,如果是普通bean,就直接返回了,而如果是FactoryBean,最終調用會調用factory.getObject從而返回具體對象。如果將整個spring看做一個抽象工廠,生產抽象的bean時,則FactoryBean就是具體工廠,生產你需要的對象。
spring中FactoryBean用法很多,舉個比較常見的例子,集成hibernate的sessionFactory時一般會注入LocalSessionFactoryBean,但是這個sessionFactory實際上不是普通的bean,可以簡單在配置文件中注入就能生產,它有很多定制的部分,于是spring讓這個bean成為一個FactoryBean并控制其生產的對象。