它這種設計策略完全類似於Java實現OOP的設計理念,當然Java本身的設計要比Spring復雜太多太多,但是它們都是構建一個數據結構,然后根據這個數據結構設計它的生存環境,並讓它在這個環境中按照一定的規律不停地運動,在它們的不停運動中設計一系列與環境或者與其他個體完成信息交換。這樣想來我們用到的其他框架都是大概類似的設計理念。
Spring總共有十幾個組件,但是真正核心的組件只有幾個。從圖中可以看出,Spring框架中的核心組件只有三個:Core、Context和Bean。它們構建起了整個Spring的骨骼架構,沒有它們就不可能有AOP、Web等上層的特性功能。如果要在它們三個中選出核心,那就非Bean組件莫屬了。其實Spring就是面向Bean的編程(BOP,Bean Oriented Programming),Bean在Spring中才是真正的主角。
核心組件介紹
可以把Bean比做一場演出中的演員,Context就是這場演出的舞台背景,而Core應該就是演出的道具了。只有它們在一起才能具備能演出一場好戲的最基本的條件。當然有最基本的條件還不能使這場演出脫穎而出,還要它表演的節目足夠精彩,這些節目就是Spring能提供的特色功能了。
Spring解決了一個非常關鍵的問題,它可以讓你把對象之間的依賴關系轉而用配置文件來管理,也就是它的依賴注入機制。而這個注入關系在一個叫Ioc的容器中管理,Bean包裝的是Object,而Object必然有數據,如何給這些數據提供生存環境就是Context要解決的問題,對Context來說它就是要發現每個Bean之間的關系,為它們建立這種關系並且維護好這種關系。所以Context就是一個Bean關系的集合,這個關系集合就叫Ioc容器。一旦建立起這個Ioc容器Spring就可以為你工作了。其實Core就是發現、建立和維護每個Bean之間的關系所需要的一系列工具,從這個角度來看,Core組件叫Util更能讓你理解。
Bean組件
Bean組件在Spring的org.springframework.beans包下。這個包下的所有類主要解決了三件事:Bean的定義、Bean的創建及對Bean的解析。對Spring的使用者來說唯一需要關心的就是Bean的創建,其他兩個由Spring在內部幫你完成了,對你來說是透明的。
Spring Bean的創建是典型的工廠模式,它的頂級接口是BeanFactory。
BeanFactory有三個子類:ListableBeanFactory、HierarchicalBeanFactory和AutowireCapableBeanFactory。但是從圖中我們可以發現最終的默認實現類是DefaultListableBeanFactory,它實現了所有的接口。查閱這些接口的源碼和說明可以發現每個接口都有它使用的場合,它主要是為了區分在Spring內部對象的傳遞和轉化過程中,對對象的數據訪問所做的限制。例如,ListableBeanFactory接口表示這些Bean是可列表的,而HierarchicalBeanFactory表示的是這些Bean是有繼承關系的,也就是每個Bean有可能有父Bean,AutowireCapableBeanFactory接口定義Bean的自動裝配規則。這四個接口共同定義了Bean的集合、Bean之間的關系和Bean的行為。
Bean的定義主要由BeanDefinition描述。
Bean的定義完整地描述了在Spring的配置文件中你定義的節點中所有的信息,包括各種子節點。當Spring成功解析你定義的一個節點后,在Spring的內部它就被轉化成BeanDefinition對象,以后所有的操作都是對這個對象進行的。Bean的解析過程非常復雜,功能被分得很細,因為這里需要被擴展的地方很多,必須保證有足夠的靈活性,以應對可能的變化。Bean的解析主要就是對Spring配置文件的解析。
Context組件
Context在Spring的org.springframework.context包下,它實際上就是給Spring提供一個運行時的環境,用以保存各個對象的狀態。ApplicationContext是Context的頂級父類,它除了能標識一個應用環境的基本信息外,還繼承了5個接口,這5個接口主要是擴展了Context的功能。可以看出 ApplicationContext 繼承了 BeanFactory,這也說明了 Spring 容器中運行的主體對象是 Bean,另外 ApplicationContext 繼承了 ResourceLoader 接口,使得 ApplicationContext 可以訪問到任何外部資源。
ApplicationContext 的子類主要包含兩個方面:
- ConfigurableApplicationContext 表示該 Context 是可修改的,也就是在構建 Context 中用戶可以動態添加或修改已有的配置信息,它下面又有多個子類,其中最經常使用的是可更新的 Context,即 AbstractRefreshableApplicationContext 類。
- WebApplicationContext 顧名思義,就是為 web 准備的 Context 他可以直接訪問到 ServletContext,通常情況下,這個接口使用的少。
再往下分就是按照構建 Context 的文件類型,接着就是訪問 Context 的方式。這樣一級一級構成了完整的 Context 等級層次。
總體來說 ApplicationContext 必須要完成以下幾件事:
- 標識一個應用環境
- 利用 BeanFactory 創建 Bean 對象
- 保存對象關系表
- 能夠捕獲各種事件
Context 作為 Spring 的 Ioc 容器,基本上整合了 Spring 的大部分功能,或者說是大部分功能的基礎。
Core 組件
Core組件作為Spring的核心組件,其中包含了很多關鍵類,一個重要的組成部分就是定義了資源的訪問方式。
可以看出Resource接口封裝了各種可能的資源類型,也就是對使用者來說屏蔽了文件類型的不同。對資源的提供者來說,如何把資源包裝起來交給其他人用這也是一個問題,我們看到Resource接口繼承了InputStreamSource接口,這個接口中有個getInputStream方法,返回的是InputStream類。這樣所有的資源都可以通過InputStream類來獲取,所以也屏蔽了資源的提供者。另外還有一個問題就是加載資源的問題,也就是資源的加載者要統一,從上圖中可以看出這個任務是由ResourceLoader接口完成的,它屏蔽了所有的資源加載者的差異,只需要實現這個接口就可以加載所有的資源,它的默認實現是DefaultResourceLoader。
Context和Resource是如何建立關系的?Context把資源的加載、解析和描述工作委托給了ResourcePatternResolver類來完成,它相當於一個接頭人,它把資源的加載、解析和資源的定義整合在一起便於其他組件使用。Core組件中還有很多類似的方式。
Ioc容器如何工作
IOC作為一個容器,它里面放得都是bean,bean與bean之間的對應關系,而bean之間的對應關系我們開始都是通過xml配置文件來體現的。
那么這里就反饋了如下幾個問題:
1、對應與對象之間的關系是通過xml配置文件來描述的(當然也可以是properties等文件)。
2、描述的文件存放位置在那里,一般來說我們都是放在classpath目錄下的,但是也可以是URL、fileSystem。
3、文件的解析。
4、Bean在容器中的表現形式,也就是它的數據結構。
對於Spring而言,它用Resource、BeanDefinition、BeanDefinitionReader、BeanFactory、ApplicationContext五個組件來實現以上問題,而同時這5個接口定義了 spring ioc 容器的基本代碼組件結構。
下面我們簡單了解這五個結構
Resource
Resource,對資源的抽象,它的每一個實現類都代表了一種資源的訪問策略,如ClasspathResource 、 URLResource ,FileSystemResource 等。
BeanDefinition
用來描述和抽象一個具體的Bean對象,它是描述Bean對象的基本數據結構。配置文件中的每一個bean對應一個BeanDefinition實例。
BeanDefinitionReader
外部資源所表達的語義需要轉化為內部數據結構BeanDefinition,BeanDefinitionReader起到解析的作用。對應不同的描述需要有不同的Reader 。如 XmlBeanDefinitionReader 用來讀取xml 描述配置的 bean 對象。
BeanFactory
BeanFactory是一個純粹的bean容器,它是IOC必備的數據結構,其中BeanDefinition是它的基本結構,它內部維護着一個BeanDefinition map,並可根據BeanDefinition 的描述進行 bean 的創建和管理。
ApplicationContext
這個就是大名鼎鼎的Spring容器,它叫做應用上下文,與我們應用息息相關,它繼承BeanFactory,是BeanFactory的擴展升級版。由於ApplicationContext的結構就決定了它與BeanFactory的不同,其主要區別有:
- 繼承MessageSource,提供國際化的標准訪問策略。
- 繼承ApplicationEventPublisher,提供強大的事件機制。
- 擴展ResourceLoader,可以用來加載多個Resource,可以靈活訪問不同的資源。
- 對Web應用的支持。
如何創建BeanFactory工廠
Ioc容器實際上是Context組件結合其他兩個組件共同構建了一個Bean關系網,如何構建這個關系網?構建的入口就在AbstractApplicationContext類的refresh方法中,這個方法的代碼如下:
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. //獲取實現BeanFactoryPostProcessor接口的子類,並執行它的postProcessBeanFactory方法
invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. //獲取用戶定義的實現了BeanPostProcessor接口的子類,並把它們注冊到BeanFactory對象中的beanPostProcessors變量中
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. //構建bean的關系網
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; } } }
這個方法就是構建整個Ioc容器過程的完整的代碼,了解了里面的每一行代碼基本上就了解大部分Spring的原理和功能。
這段代碼主要包含這樣幾個步驟:
- 構建BeanFactory,以便於產生所需的“演員”。
- 注冊可能感興趣的事件。
- 創建Bean實例對象。
- 觸發被監聽的事件。
第二三句就是在創建和配置 BeanFactory。這里是 refresh 也就是刷新配置,前面介紹了 Context 有可更新的子類,這里正是實現這個功能,當 BeanFactory 已存在是就更新,如果沒有就新創建。下面是更新 BeanFactory 的方法代碼,這個方法實現了AbstractApplicationContext的抽象方法refreshBeanFactory,這段代碼清楚地說明了BeanFactory的創建過程:
AbstractRefreshableApplicationContext.refreshBeanFactory protected final void refreshBeanFactory() throws BeansException { if (hasBeanFactory()) { destroyBeans(); closeBeanFactory(); } try { DefaultListableBeanFactory beanFactory = createBeanFactory(); beanFactory.setSerializationId(getId()); customizeBeanFactory(beanFactory);
//加載、解析Bean的定義,也就是把用戶定義的數據結構轉化為Ioc容器中的特定數據結構 loadBeanDefinitions(beanFactory); synchronized (this.beanFactoryMonitor) { this.beanFactory = beanFactory; } } catch (IOException ex) { throw new ApplicationContextException( "I/O error parsing bean definition source for " + getDisplayName(), ex); } }
注意BeanFactory對象的類型的變化,前面介紹了它有很多子類,在什么情況下使用子類非常關鍵。BeanFactory的原始對象是DefaultListableBeanFactory,這非常關鍵,因為它涉及后面對這個對象的多種操作,下面看一下這個類的繼承關系圖。
創建 BeanFactory 時序圖
Bean 的解析和登記流程時序圖如下:
創建好BeanFactory后,添加一些Spring本身需要的一些工具類,這個操作在AbstractApplicationContext的prepareBeanFactory方法中完成。
在invokeBeanFactoryPostProcessors方法中主要是獲取實現BeanFactoryPostProcessor接口的子類,並執行它的postProcessBeanFactory方法,這個方法的聲明如下:
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
它的參數是beanFactory,說明可以對beanFactory做修改,這里注意,beanFactory是ConfigurableListableBeanFactory類型的,這也印證了前面介紹的不同BeanFactory所使用的場合不同,這里只能是可配置的BeanFactory,防止一些數據被用戶隨意修改。
在registerBeanPostProcessors方法中可以獲取用戶定義的實現了BeanPostProcessor接口的子類,並把它們注冊到BeanFactory對象中的beanPostProcessors變量中。BeanPostProcessor中聲明了兩個方法:postProcessBeforeInitialization和postProcessAfterInitialization,分別用於在Bean對象初始化時執行,可以執行用戶自定義的操作。
后面的幾行代碼是初始化監聽事件和對系統的其他監聽者的注冊,監聽者必須是ApplicationListener的子類。
如何創建Bean實例並構建Bean的關系網
下面就是Bean的實例化代碼,是從finishBeanFactoryInitialization方法開始的。
AbstractApplicationContext.finishBeanFactoryInitialization protected void finishBeanFactoryInitialization( ConfigurableListableBeanFactory beanFactory) { // Stop using the temporary ClassLoader for type matching. beanFactory.setTempClassLoader(null); // Allow for caching all bean definition metadata, not expecting further changes. beanFactory.freezeConfiguration(); // Instantiate all remaining (non-lazy-init) singletons. beanFactory.preInstantiateSingletons(); }
從上面的代碼中可以發現Bean的實例化是在BeanFactory中發生的。PreInstantiateSingletons方法的代碼如下:
DefaultListableBeanFactory.preInstantiateSingletons public void preInstantiateSingletons() throws BeansException { if (this.logger.isInfoEnabled()) { this.logger.info("Pre-instantiating singletons in " + this); } synchronized (this.beanDefinitionMap) { for (String beanName : this.beanDefinitionNames) { RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName); if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) { if (isFactoryBean(beanName)) { final FactoryBean factory = (FactoryBean) getBean(FACTORY_BEAN_PREFIX+ beanName); //FACTORY_BEAN_PREFIX=& boolean isEagerInit; if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) { isEagerInit = AccessController.doPrivileged( new PrivilegedAction<Boolean>() { public Boolean run() { return ((SmartFactoryBean) factory).isEagerInit(); } }, getAccessControlContext()); } else { isEagerInit = factory instanceof SmartFactoryBean && ((SmartFactoryBean) factory).isEagerInit(); } if (isEagerInit) { getBean(beanName); } } else { getBean(beanName); } } } } }
這里出現了一個非常重要的Bean——FactoryBean,可以說Spring一大半的擴展功能都與這個Bean有關,這是個特殊的Bean。它是個工廠Bean,可以產生Bean的Bean,這里的產生Bean是指Bean的實例,如果一個類繼承FactoryBean用戶可以自己定義產生實例對象的方法,只需實現它的getObject方法。然而在Spring內部,這個Bean的實例對象是FactoryBean,通過調用這個對象的getObject方法就能獲取用戶自定義產生的對象,從而為Spring提供了很好的擴展性。Spring獲取FactoryBean本身的對象是通過在前面加上&來完成的。
如何創建Bean的實例對象及如何構建Bean實例對象之間的關聯關系是Spring中的一個核心,這個過程的流程圖如下:
如果是普通的 Bean 就直接創建他的實例,是通過調用 getBean 方法。下面是創建 Bean 實例的時序圖:
還有一個非常重要的部分就是建立 Bean 對象實例之間的關系,這也是 Spring 框架的核心競爭力,何時、如何建立他們之間的關系請看下面的時序圖:
Ioc 容器的擴展點
如何讓這些 Bean 對象有一定的擴展性,就是可以加入用戶的一些操作。對 Spring 的 Ioc 容器來說,主要有以下幾個。
- BeanFactoryPostProcessor, BeanPostProcessor。他們分別是在構建 BeanFactory 和構建 Bean 對象時調用。
- InitializingBean 和 DisposableBean 分別是在 Bean 實例創建和銷毀時被調用。用戶可以實現這些接口中定義的方法,Spring會在適當的時候調用。
- FactoryBean 是個特殊的 Bean,如果一個類繼承FactoryBean,用戶可以自己定義產生實例對象的方法,只需實現它的getObject方法。
這些擴展點通常也是我們使用 Spring 來完成我們特定任務的地方,如何精通 Spring 就看你有沒有掌握好 Spring 有哪些擴展點,並且如何使用他們,要知道如何使用他們就必須了解他們內在的機理。可以用下面一個比喻來解釋。
我們把 Ioc 容器比作一個箱子,這個箱子里有若干個球的模子,可以用這些模子來造很多種不同的球,還有一個造這些球模的機器,這個機器可以產生球模。那么他們的對應關系就是 BeanFactory 就是那個造球模的機器,球模就是 Bean,而球模造出來的球就是 Bean 的實例。
所說的幾個擴展點又在什么地方呢?BeanFactoryPostProcessor 對應到當造球模被造出來時,你將有機會可以對其做出設當的修正,也就是他可以幫你修改球模。而 InitializingBean 和 DisposableBean 是在球模造球的開始和結束階段,你可以完成一些預備和掃尾工作。BeanPostProcessor 就可以讓你對球模造出來的球后做出適當的修正。最后還有一個 FactoryBean,它可是一個神奇的球模。這個球模不是預先就定型了,而是由你來給他確定它的形狀,既然你可以確定這個球模型的形狀,當然他造出來的球肯定就是你想要的球了,這樣在這個箱子里尼可以發現所有你想要的球。
Ioc 容器如何為我所用
我們使用 Spring 必須要首先構建 Ioc 容器,沒有它 Spring 無法工作,ApplicatonContext.xml 就是 Ioc 容器的默認配置文件,Spring 的所有特性功能都是基於這個 Ioc 容器工作的,比如AOP,JDBC。
Ioc 它實際上就是為你構建了一個魔方,Spring 為你搭好了骨骼架構,這個魔方到底能變出什么好的東西出來,這必須要有你的參與。那我們怎么參與?這就是前面說的要了解 Spring 中那有些擴展點,我們通過實現那些擴展點來改變 Spring 的通用行為。至於如何實現擴展點來得到我們想要的個性結果,Spring 中有很多例子,其中 AOP 的實現就是 Spring 本身實現了其擴展點來達到了它想要的特性功能,可以拿來參考。