參考這篇文章:
http://www.ibm.com/developerworks/cn/java/j-lo-spring-principle/
Spring內部分為Beans, Context 和 Core。
再來一個來自(Link)的圖:
Spring 框架是一個分層架構,由 7 個定義良好的模塊組成。Spring 模塊構建在核心容器之上,核心容器定義了創建、配置和管理 bean 的方式
Spring 的設計理念
前面介紹了 Spring 的三個核心組件,如果再在它們三個中選出核心的話,那就非 Beans 組件莫屬了,為何這樣說,其實 Spring 就是面向 Bean 的編程(BOP,Bean Oriented Programming),Bean 在 Spring 中才是真正的主角。
核心組件如何協同工作
前面說 Bean 是 Spring 中關鍵因素,那 Context 和 Core 又有何作用呢?前面吧 Bean 比作一場演出中的演員的話,那 Context 就是這場演出的舞台背景,而 Core 應該就是演出的道具了。
Context 就是一個 Bean 關系的集合,這個關系集合又叫 Ioc 容器,一旦建立起這個 Ioc 容器后 Spring 就可以為你工作了。那 Core 組件又有什么用武之地呢?其實 Core 就是發現、建立和維護每個 Bean 之間的關系所需要的一些列的工具,從這個角度看來,Core 這個組件叫 Util 更能讓你理解。
核心組件詳解
Bean 組件
Bean 組件在 Spring 的 org.springframework.beans 包下。這個包下的所有類主要解決了三件事:Bean 的定義、Bean 的創建以及對 Bean 的解析。對 Spring 的使用者來說唯一需要關心的就是 Bean 的創建,其他兩個由 Spring 在內部幫你完成了,對你來說是透明的。
Spring Bean 的創建時典型的工廠模式,他的頂級接口是 BeanFactory
查閱這些接口的源碼和說明發現,每個接口都有他使用的場合,它主要是為了區分在 Spring 內部在操作過程中對象的傳遞和轉化過程中,對對象的數據訪問所做的限制。例如 ListableBeanFactory 接口表示這些 Bean 是可列表的,而 HierarchicalBeanFactory 表示的是這些 Bean 是有繼承關系的,也就是每個 Bean 有可能有父 Bean。AutowireCapableBeanFactory 接口定義 Bean 的自動裝配規則。這四個接口共同定義了 Bean 的集合、Bean 之間的關系、以及 Bean 行為。
BeanDefinition主要有 BeanDefinition 描述,如下圖說明了這些類的層次關系:
Bean 的定義就是完整的描述了在 Spring 的配置文件中你定義的 <bean/> 節點中所有的信息,包括各種子節點。當 Spring 成功解析你定義的一個 <bean/> 節點后,在 Spring 的內部他就被轉化成 BeanDefinition 對象。以后所有的操作都是對這個對象完成的。
Bean 的解析主要就是對 Spring 配置文件的解析。這個解析過程主要通過下圖中的類完成:
Bean的解析類:
Context 組件
Context 在 Spring 的 org.springframework.context 包下,前面已經講解了 Context 組件在 Spring 中的作用,他實際上就是給 Spring 提供一個運行時的環境,用以保存各個對象的狀態。下面看一下這個環境是如何構建的。
ApplicationContext 是 Context 的頂級父類,他除了能標識一個應用環境的基本信息外,他還繼承了五個接口,這五個接口主要是擴展了 Context 的功能。下面是 Context 的類結構圖:
從上圖中可以看出 ApplicationContext 繼承了 BeanFactory,這也說明了 Spring 容器中運行的主體對象是 Bean,另外 ApplicationContext 繼承了 ResourceLoader 接口,使得 ApplicationContext 可以訪問到任何外部資源,這將在 Core 中詳細說明。
ApplicationContext 的子類主要包含兩個方面:
- ConfigurableApplicationContext 表示該 Context 是可修改的,也就是在構建 Context 中用戶可以動態添加或修改已有的配置信息,它下面又有多個子類,其中最經常使用的是可更新的 Context,即 AbstractRefreshableApplicationContext 類。
- WebApplicationContext 顧名思義,就是為 web 准備的 Context 他可以直接訪問到 ServletContext,通常情況下,這個接口使用的少。
總體來說 ApplicationContext 必須要完成以下幾件事:
- 標識一個應用環境
- 利用 BeanFactory 創建 Bean 對象
- 保存對象關系表
- 能夠捕獲各種事件
Context 作為 Spring 的 Ioc 容器,基本上整合了 Spring 的大部分功能,或者說是大部分功能的基礎。
Core 組件
Core 組件作為 Spring 的核心組件,他其中包含了很多的關鍵類,其中一個重要組成部分就是定義了資源的訪問方式。這種把所有資源都抽象成一個接口的方式很值得在以后的設計中拿來學習。
下圖是 Resource 相關的類結構圖:
從上圖可以看出 Resource 接口封裝了各種可能的資源類型,也就是對使用者來說屏蔽了文件類型的不同。
我們看到 Resource 接口繼承了 InputStreamSource 接口,這個接口中有個 getInputStream 方法,返回的是 InputStream 類。這樣所有的資源都被可以通過 InputStream 這個類來獲取,所以也屏蔽了資源的提供者。
另外還有一個問題就是加載資源的問題,也就是資源的加載者要統一,從上圖中可以看出這個任務是由 ResourceLoader 接口完成,他屏蔽了所有的資源加載者的差異,只需要實現這個接口就可以加載所有的資源,他的默認實現是 DefaultResourceLoader。
下面看一下 Context 和 Resource 是如何建立關系的?首先看一下他們的類關系圖:
從上圖可以看出,Context 是把資源的加載、解析和描述工作委托給了 ResourcePatternResolver 類來完成,他相當於一個接頭人,他把資源的加載、解析和資源的定義整合在一起便於其他組件使用。
Ioc 容器如何工作
如何創建 BeanFactory 工廠
正如圖 2 描述的那樣,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. 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; } } }
這個方法就是構建整個 Ioc 容器過程的完整的代碼,了解了里面的每一行代碼基本上就了解大部分 Spring 的原理和功能了。
這段代碼主要包含這樣幾個步驟:
- 構建 BeanFactory,以便於產生所需的“演員”
- 注冊可能感興趣的事件
- 創建 Bean 實例對象
- 觸發被監聽的事件
BeanFactory 的原始對象是 DefaultListableBeanFactory,這個非常關鍵,因為他設計到后面對這個對象的多種操作.
創建 BeanFactory 時序圖
解析和登記 Bean 對象時序圖
如何創建 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); 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 實例創建流程圖
如果是普通的 Bean 就直接創建他的實例,是通過調用 getBean 方法。下面是創建 Bean 實例的時序圖:
還有一個非常重要的部分就是建立 Bean 對象實例之間的關系,這也是 Spring 框架的核心競爭力,何時、如何建立他們之間的關系請看下面的時序圖:
Bean 對象關系建立
Spring 能為我們做什么,Spring 的 Ioc 容器又能做什么呢?我們使用 Spring 必須要首先構建 Ioc 容器,沒有它 Spring 無法工作,ApplicatonContext.xml 就是 Ioc 容器的默認配置文件,Spring 的所有特性功能都是基於這個 Ioc 容器工作的,比如后面要介紹的 AOP。
Spring 中 AOP 特性詳解
動態代理的實現原理
要了解 Spring 的 AOP 就必須先了解的動態代理的原理,因為 AOP 就是基於動態代理實現的。動態代理還要從 JDK 本身說起。
在 Jdk 的 java.lang.reflect 包下有個 Proxy 類,它正是構造代理類的入口。方法 newProxyInstance 就是創建代理對象的方法。
這個方法需要三個參數:ClassLoader,用於加載代理類的 Loader 類,通常這個 Loader 和被代理的類是同一個 Loader 類。Interfaces,是要被代理的那些那些接口。InvocationHandler,就是用於執行除了被代理接口中方法之外的用戶自定義的操作,他也是用戶需要代理的最終目的。用戶調用目標方法都被代理到 InvocationHandler 類中定義的唯一方法 invoke 中。
下面還是看看 Proxy 如何產生代理類的過程,他構造出來的代理類到底是什么樣子?
圖 17. 創建代理對象時序圖
其實從上圖中可以發現正在構造代理類的是在 ProxyGenerator 的 generateProxyClass 的方法中。
假如有這樣一個接口,如下: 清單 7. SimpleProxy 類 public interface SimpleProxy { public void simpleMethod1(); public void simpleMethod2(); } 代理來生成的類結構如下: 清單 8. $Proxy2 類 public class $Proxy2 extends java.lang.reflect.Proxy implements SimpleProxy{ java.lang.reflect.Method m0; java.lang.reflect.Method m1; java.lang.reflect.Method m2; java.lang.reflect.Method m3; java.lang.reflect.Method m4; int hashCode(); boolean equals(java.lang.Object); java.lang.String toString(); void simpleMethod1(); void simpleMethod2(); } 這個類中的方法里面將會是調用 InvocationHandler 的 invoke 方法,而每個方法也將對應一個屬性變量,
這個屬性變量 m 也將傳給 invoke 方法中的 Method 參數。整個代理就是這樣實現的。
Spring AOP 如何實現
下面是 Spring 創建的代理對象的時序圖:
Spring 代理對象的產生
Spring 是如何調用攔截器的,下面是這個過程的時序圖:
Spring 調用攔截器
以上所說的都是 Jdk 動態代理,Spring 還支持一種 CGLIB 類代理,略。
Spring 中設計模式分析
代理模式原理
代理模式就是給某一個對象創建一個代理對象,而由這個代理對象控制對原對象的引用,而創建這個代理對象就是可以在調用原對象是可以增加一些額外的操作。下面是代理模式的結構:
Spring 中如何實現代理模式
Spring Aop 中 Jdk 動態代理就是利用代理模式技術實現的。在 Spring 中除了實現被代理對象的接口外,還會有 org.springframework.aop.SpringProxy 和 org.springframework.aop.framework.Advised 兩個接口。Spring 中使用代理模式的結構圖如下:
代理對象是通過 InvocationHandler 來持有對目標對象的引用的。
Spring 中一個真實的代理對象結構如下:
public class $Proxy4 extends java.lang.reflect.Proxy implements org.springframework.aop.framework.PrototypeTargetTests$TestBean org.springframework.aop.SpringProxy org.springframework.aop.framework.Advised { java.lang.reflect.Method m16; java.lang.reflect.Method m9; java.lang.reflect.Method m25; java.lang.reflect.Method m5; java.lang.reflect.Method m2; java.lang.reflect.Method m23; java.lang.reflect.Method m18; java.lang.reflect.Method m26; java.lang.reflect.Method m6; java.lang.reflect.Method m28; java.lang.reflect.Method m14; java.lang.reflect.Method m12; java.lang.reflect.Method m27; java.lang.reflect.Method m11; java.lang.reflect.Method m22; java.lang.reflect.Method m3; java.lang.reflect.Method m8; java.lang.reflect.Method m4; java.lang.reflect.Method m19; java.lang.reflect.Method m7; java.lang.reflect.Method m15; java.lang.reflect.Method m20; java.lang.reflect.Method m10; java.lang.reflect.Method m1; java.lang.reflect.Method m17; java.lang.reflect.Method m21; java.lang.reflect.Method m0; java.lang.reflect.Method m13; java.lang.reflect.Method m24; int hashCode(); int indexOf(org.springframework.aop.Advisor); int indexOf(org.aopalliance.aop.Advice); boolean equals(java.lang.Object); java.lang.String toString(); void sayhello(); void doSomething(); void doSomething2(); java.lang.Class getProxiedInterfaces(); java.lang.Class getTargetClass(); boolean isProxyTargetClass(); org.springframework.aop.Advisor; getAdvisors(); void addAdvisor(int, org.springframework.aop.Advisor) throws org.springframework.aop.framework.AopConfigException; void addAdvisor(org.springframework.aop.Advisor) throws org.springframework.aop.framework.AopConfigException; void setTargetSource(org.springframework.aop.TargetSource); org.springframework.aop.TargetSource getTargetSource(); void setPreFiltered(boolean); boolean isPreFiltered(); boolean isInterfaceProxied(java.lang.Class); boolean removeAdvisor(org.springframework.aop.Advisor); void removeAdvisor(int)throws org.springframework.aop.framework.AopConfigException; boolean replaceAdvisor(org.springframework.aop.Advisor, org.springframework.aop.Advisor) throws org.springframework.aop.framework.AopConfigException; void addAdvice(org.aopalliance.aop.Advice) throws org.springframework.aop.framework.AopConfigException; void addAdvice(int, org.aopalliance.aop.Advice) throws org.springframework.aop.framework.AopConfigException; boolean removeAdvice(org.aopalliance.aop.Advice); java.lang.String toProxyConfigString(); boolean isFrozen(); void setExposeProxy(boolean); boolean isExposeProxy(); }
策略模式
下面是策略模式的結構:
Spring 中策略模式的實現
Spring 中策略模式使用有多個地方,如 Bean 定義對象的創建以及代理對象的創建等。這里主要看一下代理對象創建的策略模式的實現。
前面已經了解 Spring 的代理方式有兩個 Jdk 動態代理和 CGLIB 代理。這兩個代理方式的使用正是使用了策略模式。它的結構圖如下所示:
上面結構圖中與標准的策略模式結構稍微有點不同,這里抽象策略是 AopProxy 接口,Cglib2AopProxy 和 JdkDynamicAopProxy 分別代表兩種策略的實現方式,ProxyFactoryBean 就是代表 Context 角色,它根據條件選擇使用 Jdk 代理方式還是 CGLIB 方式,而另外三個類主要是來負責創建具體策略對象,ProxyFactoryBean 是通過依賴的方法來關聯具體策略對象的,它是通過調用策略對象的 getProxy(ClassLoader classLoader) 方法來完成操作。
以下來自:http://www.ibm.com/developerworks/cn/java/wa-spring1/
每個模塊的功能如下:
- 核心容器:核心容器提供 Spring 框架的基本功能。核心容器的主要組件是
BeanFactory
,它是工廠模式的實現。BeanFactory
使用控制反轉 (IOC) 模式將應用程序的配置和依賴性規范與實際的應用程序代碼分開。 - Spring 上下文:Spring 上下文是一個配置文件,向 Spring 框架提供上下文信息。Spring 上下文包括企業服務,例如 JNDI、EJB、電子郵件、國際化、校驗和調度功能。
- Spring AOP:通過配置管理特性,Spring AOP 模塊直接將面向方面的編程功能集成到了 Spring 框架中。所以,可以很容易地使 Spring 框架管理的任何對象支持 AOP。Spring AOP 模塊為基於 Spring 的應用程序中的對象提供了事務管理服務。通過使用 Spring AOP,不用依賴 EJB 組件,就可以將聲明性事務管理集成到應用程序中。
- Spring DAO:JDBC DAO 抽象層提供了有意義的異常層次結構,可用該結構來管理異常處理和不同數據庫供應商拋出的錯誤消息。異常層次結構簡化了錯誤處理,並且極大地降低了需要編寫的異常代碼數量(例如打開和關閉連接)。Spring DAO 的面向 JDBC 的異常遵從通用的 DAO 異常層次結構。
- Spring ORM:Spring 框架插入了若干個 ORM 框架,從而提供了 ORM 的對象關系工具,其中包括 JDO、Hibernate 和 iBatis SQL Map。所有這些都遵從 Spring 的通用事務和 DAO 異常層次結構。
- Spring Web 模塊:Web 上下文模塊建立在應用程序上下文模塊之上,為基於 Web 的應用程序提供了上下文。所以,Spring 框架支持與 Jakarta Struts 的集成。Web 模塊還簡化了處理多部分請求以及將請求參數綁定到域對象的工作。
- Spring MVC 框架:MVC 框架是一個全功能的構建 Web 應用程序的 MVC 實現。通過策略接口,MVC 框架變成為高度可配置的,MVC 容納了大量視圖技術,其中包括 JSP、Velocity、Tiles、iText 和 POI。
IOC 和 AOP
Inversion of Control
Aspect Oriented Programming
控制反轉模式(也稱作依賴性介入)的基本概念是:不創建對象,但是描述創建它們的方式。在代碼中不直接與對象和服務連接,但在配置文件中描述哪一個組件需要哪一項服務。容器 (在 Spring 框架中是 IOC 容器) 負責將這些聯系在一起。
類型 1 | 服務需要實現專門的接口,通過接口,由對象提供這些服務,可以從對象查詢依賴性(例如,需要的附加服務) |
---|---|
類型 2 | 通過 JavaBean 的屬性(例如 setter 方法)分配依賴性 |
類型 3 | 依賴性以構造函數的形式提供,不以 JavaBean 屬性的形式公開 |
Spring 框架的 IOC 容器采用類型 2 和類型3 實現。
面向方面的編程
面向方面的編程,即 AOP,是一種編程技術,它允許程序員對橫切關注點或橫切典型的職責分界線的行為(例如日志和事務管理)進行模塊化。AOP 的核心構造是方面,它將那些影響多個類的行為封裝到可重用的模塊中。
AOP 和 IOC 是補充性的技術,它們都運用模塊化方式解決企業應用程序開發中的復雜問題。在典型的面向對象開發方式中,可能要將日志記錄語句放在所有方法和 Java 類中才能實現日志功能。在 AOP 方式中,可以反過來將日志服務模塊化,並以聲明的方式將它們應用到需要日志的組件上。當然,優勢就是 Java 類不需要知道日志服務的存在,也不需要考慮相關的代碼。所以,用 Spring AOP 編寫的應用程序代碼是松散耦合的。
AOP 的功能完全集成到了 Spring 事務管理、日志和其他各種特性的上下文中。
IOC 容器
Spring 設計的核心是 org.springframework.beans
包. 這個包通常不是由用戶直接使用,而是由服務器將其用作其他多數功能的底層中介。下一個最高級抽象是 BeanFactory
接口,它是工廠設計模式的實現,允許通過名稱創建和檢索對象。BeanFactory
也可以管理對象之間的關系。
BeanFactory
支持兩個對象模型。
- 單態 模型提供了具有特定名稱的對象的共享實例,可以在查詢時對其進行檢索。Singleton 是默認的也是最常用的對象模型。對於無狀態服務對象很理想。
- 原型 模型確保每次檢索都會創建單獨的對象。在每個用戶都需要自己的對象時,原型模型最適合。
BeanFactory 接口
因為 org.springframework.beans.factory.BeanFactory
是一個簡單接口,所以可以針對各種底層存儲方法實現。最常用的 BeanFactory
定義是 XmlBeanFactory
,它根據 XML 文件中的定義裝入 bean。
BeanFactory factory = new XMLBeanFactory(new FileInputSteam("mybean.xml"));
在 XML 文件中定義的 Bean 是被消極加載的,這意味在需要 bean 之前,bean 本身不會被初始化。要從 BeanFactory
檢索 bean,只需調用 getBean()
方法,傳入將要檢索的 bean 的名稱即可。
MyBean mybean = (MyBean) factory.getBean("mybean");
每個 bean 的定義都可以是 POJO (用類名和 JavaBean 初始化屬性定義) 或 FactoryBean
。FactoryBean
接口為使用 Spring 框架構建的應用程序添加了一個間接的級別。(注:理解FactoryBean是創建Bean的Bean)
IOC 示例
我用開啟在線信用帳戶的用例作為起點。對於該實現,開啟信用帳戶要求用戶與以下服務進行交互:
- 信用級別評定服務,查詢用戶的信用歷史信息。
- 遠程信息鏈接服務,插入客戶信息,將客戶信息與信用卡和銀行信息連接起來,以進行自動借記(如果需要的話)。
- 電子郵件服務,向用戶發送有關信用卡狀態的電子郵件。
參考資料
學習
- “Spring 系列: Spring 框架簡介”(developerWorks,2005 年 8 月):在這由三部分組成的介紹 Spring 框架的系列文章的第一期中,將開始學習如何用 Spring 技術構建輕量級的、強壯的 J2EE 應用程序。
- “AOP@Work: 用 AspectJ 和 Spring 進行依賴項插入”(developerWorks,2006 年 1 月):依賴項插入和面向方面編程是互補的技術,所以想把它們結合在一起使用是很自然的。本文探索兩者之間的關系,並了解怎樣才能把它們組合在一起,來促進高級的依賴項插入場景。
- “Spring 2 和 JPA 簡介”(developerWorks,2006 年 8 月):您將學習如何用 Spring 2 框架從頭開始創建服務器應用程序。
- 查看本文作者的其它文章:“Tomcat 系統原理分析”和“Tomcat 設計模式分析”。
(完)