用spring框架作了幾年的開發,只停留在會用的階段上,然而spring的設計思想和原理確實一個巨大的寶庫。大部分人僅僅知道怎麼去配,或着加上什麼屬性就能達到什麼效果,這些東西均可以經過查文檔,查google來解決。關鍵是在怎麼理解它,把它的思想變爲本身的東西。博主這幾天沒有學習(裝逼),感受內心甚是空虛,索性研究了下一直很好奇的spring大佬,畢竟寫代碼每天都在用。html
前方高能,非戰鬥人員迅速投入戰鬥:web
1.spring ioc容器面試
2.ApplicationContext與BeanFactory探究.spring
3.bean的加載數據庫
4.FactoryBean緩存
5.spring Aop實現原理及實戰安全
文章篇幅有限,只對spring幾個重要的知識點進行簡單闡述,有興趣的能夠看看《spring源碼深度解析》,本文也是基於該書總結出來的。session
不少人一提ioc,便張口就來:控制反轉。究竟哪些方面被反轉了呢?答案是依賴對象的得到被反轉了。不少時候,咱們經過多個對象之間的協做來完成一個功能,若是獲取所依賴對象靠自身來實現,這將致使代碼的耦合度高和難以測試。固然,控制反轉還有一個好聽的名字:依賴注入。框架
spring ioc經過引入xml配置,由ioc容器來管理對象的生命週期,依賴關係等。ide
它的加載過程是什麼樣的呢?
BeanDefinition是一個接口,用於屬性承載,好比<bean>元素標籤擁有class、scope、lazy-init等配置。bean的定義方式有千千萬萬種,不管是何種標籤,不管是何種資源定義,不管是何種容器,只要按照spring的規範編寫xml配置文件,最終的bean定義內部表示都將轉換爲內部的惟一結構:BeanDefinition。當BeanDefinition註冊完畢之後,spring的BeanFactory就能夠隨時根據須要進行實例化了。
實例化的工做會在容器啓動後過AbstractApplicationContext中reflash方法自動進行。咱們經常使用的ApplicationContext實現類ClassPathXmlApplicationContext繼承了AbstractApplicationContext類,繼承關係以下圖.
AbstractApplicationContext裏的reflash方法是spring初始ioc容器一個很是重要的方法,無論你是ApplicationContext哪一個實現類,最終都會進入這個方法。
@Override public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // 設置和校驗系統變量和環境變量的值 prepareRefresh(); //主要是建立beanFactory,同時加載配置文件.xml中的beanDefinition //經過String[] configLocations = getConfigLocations()獲取資源路徑,而後加載beanDefinition ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); //給beanFactory註冊一些標準組建,如ClassLoader,StandardEnvironment,BeanProcess prepareBeanFactory(beanFactory); try { //提供給子類實現一些postProcess的註冊,如AbstractRefreshableWebApplicationContext註冊一些Servlet相關的 //postProcess,真對web進行生命週期管理的Scope,經過registerResolvableDependency()方法註冊指定ServletRequest,HttpSession,WebRequest對象的工廠方法 postProcessBeanFactory(beanFactory); //調用全部BeanFactoryProcessor的postProcessBeanFactory()方法 invokeBeanFactoryPostProcessors(beanFactory); //註冊BeanPostProcessor,BeanPostProcessor做用是用於攔截Bean的建立 registerBeanPostProcessors(beanFactory); //初始化消息Bean initMessageSource(); //初始化上下文的事件多播組建,ApplicationEvent觸發時由multicaster通知給ApplicationListener initApplicationEventMulticaster(); //ApplicationContext初始化一些特殊的bean onRefresh(); //註冊事件監聽器,事件監聽Bean統一註冊到multicaster裏頭,ApplicationEvent事件觸發後會由multicaster廣播 registerListeners(); //非延遲加載的單例Bean實例化 finishBeanFactoryInitialization(beanFactory); finishRefresh(); } catch (BeansException ex) { logger.warn("Exception encountered during context initialization - cancelling refresh attempt", ex); destroyBeans(); cancelRefresh(ex); throw ex; } } }
代碼邏輯清晰的值得mark一下。這個方法的做用是建立加載Spring容器配置(包括.xml配置,property文件和數據庫模式等)。
BeanFactory體系結構是典型的工廠方法模式,即什麼樣的工廠生產什麼樣的產品。要知道工廠是如何產生對象的,咱們須要看具體的IOC容器實現,具體的實現有:如 DefaultListableBeanFactory 、 XmlBeanFactory 、 ApplicationContext 等。那麼,究竟BeanFactory裏究竟是什麼樣的呢?
package org.springframework.beans.factory; public interface BeanFactory { /** * 用來引用一個實例,或把它和工廠產生的Bean區分開,就是說,若是一個FactoryBean的名字爲a,那麼,&a會獲得那個Factory */ String FACTORY_BEAN_PREFIX = "&"; /* * 四個不一樣形式的getBean方法,獲取實例 */ Object getBean(String name) throws BeansException; <T> T getBean(String name, Class<T> requiredType) throws BeansException; <T> T getBean(Class<T> requiredType) throws BeansException; Object getBean(String name, Object... args) throws BeansException; boolean containsBean(String name); // 是否存在 boolean isSingleton(String name) throws NoSuchBeanDefinitionException;// 是否爲單實例 boolean isPrototype(String name) throws NoSuchBeanDefinitionException;// 是否爲原型(多實例) boolean isTypeMatch(String name, Class<?> targetType) throws NoSuchBeanDefinitionException;// 名稱、類型是否匹配 Class<?> getType(String name) throws NoSuchBeanDefinitionException; // 獲取類型 String[] getAliases(String name);// 根據實例的名字獲取實例的別名 }
咱們能夠看出BeanFactory裏只對IOC容器的基本行爲做了定義,根本不關心你的bean是如何定義怎樣加載的,它規定了全部的容器至少須要實現的標準。說到實現,BeanFactory有幾個比較重要的實現類須要知道,ref:【Spring4揭祕 BeanFactory】基本容器-BeanFactory。那麼BeanFactory的基本實現類XmlBeanFactory與咱們經常使用的ApplicationContext有什麼區別呢?答案是bean的加載。
咱們先看一道面試常常會問到的問題:spring的bean在何時實例化? ——第一:若是你使用BeanFactory,如XmlBeanFactory做爲Spring Bean的工廠類,則全部的bean都是在第一次使用該bean的時候實例化 。第二:若是你使用ApplicationContext做爲Spring Bean的工廠類,則又分爲如下幾種狀況:
1.若是bean的scope是singleton的,而且lazy-init爲false(默認是false,因此能夠不用設置),則ApplicationContext啓動的時候就實例化該bean,而且將實例化的bean放在一個線程安全的 ConcurrentHashMap 結構的緩存中,下次再使用該Bean的時候,直接從這個緩存中取 。
2.若是bean的scope是singleton的,而且lazy-init爲true,則該bean的實例化是在第一次使用該bean的時候進行實例化 。
3.若是bean的scope是prototype的,則該bean的實例化是在第一次使用該Bean的時候進行實例化 。
ClassPathXmlApplicationContext有幾個重載的構造函數最終都會調用父類AbstractApplicationContext的reflash方法,reflash方法在前文有介紹,做用是建立加載Spring容器配置。AbstractApplicationContext也有getBean方法:
AbstractApplicationContext下的代碼: public Object getBean(String name) throws BeansException { //Bean的獲取外部容器交給了內部容器 return getBeanFactory().getBean(name); }
內部容器由DefaultListableBeanFactory承當,但真實的getBean方法實現是由其父類AbstractBeanFactory實現的,AbstractBeanFactory類一樣實現了BeanFactory接口的方法,它有四個重載的getBean方法,無論哪個都會去調用doGetBean方法:
那麼doGetBean裏幹了什麼事情呢?
protected <T> T doGetBean( final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly) throws BeansException { //bean name處理,去除FactoryBean前綴等 final String beanName = transformedBeanName(name); Object bean = null; //先從singleton緩存中查看是否已經實例化過該Bean,根據是否有緩存分爲兩個分支分別處理 Object sharedInstance = getSingleton(beanName); if (sharedInstance != null && args == null) { // 分支一,若緩存中獲取到了而且該BeanDefinition信息代表該bean是singleton的,直接將獲取到的緩存Bean //(有多是半成品)交給getObjectForBeanInstance處理 /*.........省略logger部分代碼............*/ //調用getObjectForBeanInstance處理 bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); }else { // 分之二:沒有緩存,則須要從頭實例化該bean // We're assumably within a circular reference. if (isPrototypeCurrentlyInCreation(beanName)) { throw new BeanCurrentlyInCreationException(beanName);} // 檢查BeanDefinition是否在當前工廠或父工廠 BeanFactory parentBeanFactory = getParentBeanFactory(); if (parentBeanFactory != null && !containsBeanDefinition(beanName)) { // Not found -> check parent. String nameToLookup = originalBeanName(name); if (args != null) { // 父工廠getBean return parentBeanFactory.getBean(nameToLookup, args); } else { // No args -> delegate to standard getBean method. return parentBeanFactory.getBean(nameToLookup, requiredType); } } //將bean加入「正在建立」的集合,完成後會remove,對應afterSingletonCreation/afterPrototypeCreation方法 if (!typeCheckOnly) { markBeanAsCreated(beanName); } final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); checkMergedBeanDefinition(mbd, beanName, args); // 解決依賴關係,將依賴的bean提早實例化 String[] dependsOn = mbd.getDependsOn(); if (dependsOn != null) { for (int i = 0; i < dependsOn.length; i++) { String dependsOnBean = dependsOn[i]; getBean(dependsOnBean); registerDependentBean(dependsOnBean, beanName); } } // 這裏又須要根據bean的類型分爲三種狀況:singleton、prototype、request/session if (mbd.isSingleton()) { //經過自定義ObjectFactory實例化Bean,此結果多是半成品(是FactoryBean等) sharedInstance = getSingleton(beanName, new ObjectFactory() { public Object getObject() throws BeansException { try { //真正實例化裝配的邏輯在createBean方法中 return createBean(beanName, mbd, args); } catch (BeansException ex) { destroySingleton(beanName); throw ex; } } }); //上一步半成品的Bean交給getObjectForBeanInstance方法處理 bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } else if (mbd.isPrototype()) { Object prototypeInstance = null; try { beforePrototypeCreation(beanName); //真正實例化裝配的邏輯在createBean方法中 prototypeInstance = createBean(beanName, mbd, args); } finally { afterPrototypeCreation(beanName); } //上一步半成品的Bean交給getObjectForBeanInstance方法處理 bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd); } else { //request、session 的bean String scopeName = mbd.getScope(); final Scope scope = (Scope) this.scopes.get(scopeName); if (scope == null) { throw new IllegalStateException("No Scope registered for scope '" + scopeName + "'"); } try { Object scopedInstance = scope.get(beanName, new ObjectFactory() { public Object getObject() throws BeansException { beforePrototypeCreation(beanName); try { //真正實例化裝配的邏輯在createBean方法中 return createBean(beanName, mbd, args); } finally { afterPrototypeCreation(beanName); } } }); //上一步半成品的Bean交給getObjectForBeanInstance方法處理 bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd); } catch (IllegalStateException ex) { throw new BeanCreationException(beanName, "Scope '" + scopeName + "' is not active for the current thread; " + "consider defining a scoped proxy for this bean if you intend to refer to it from a singleton", ex); } } } if (requiredType != null && bean != null && !requiredType.isAssignableFrom(bean.getClass())) { throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass()); } return bean; }
bean的加載經歷了一個複雜的過程,上面代碼主要作了如下幾件事(此段摘抄自《spring源碼深度解析》):
1.轉換對應的beanName。若是name=「&aa」的,會去除&符號。或者<bean>標籤帶有alias(別名的意思),則取alias所表示最終的beanName。
2.嘗試從緩存中加載單例bean。若是加載不成功,會再次嘗試從singletonFactories中加載。
3.bean的實例化。假如咱們須要對工廠bean進行處理,那麼這裏獲得的實際上是工廠bean 的初始狀態。真正幹活的則是getObjectForBeanInstance定義factory-method方法返回的bean。
4.原型模式的依賴檢查。若是A類有B的屬性,B中有A的屬性,則會產生循環依賴。spring如何解決循環依賴問題
5.將存儲的Xml配置文件的GernericBeanDefinition轉換爲RootBeanDefinition。前文提到的用於承載屬性的BeanDefinition有三個實現,GernericBeanDefinition,RootBeanDefinition和ChildBeanDefinition,若是父類bean不爲空的話,這裏會把全部的屬性一併合併父類屬性,由於後續全部的Bean都是針對RootBeanDefinition的。
6.尋找依賴。在初始化一個bean的時候,會首先初始化這個bean所對應的依賴。
7.根據不一樣的scope建立bean。scope屬性默認是singleton,還有prototype、request等。
8.類型轉換。若是bean是個String,而requiredType傳入了Integer,而後返回bean,加載結束。
其中,最重要的步驟是(7),spring的經常使用特性都在那裏實現.
首先要分辨BeanFactory 與 FactoryBean的區別, 兩個名字很像,因此容易搞混。這裏作一個簡單的比喻你就明白了:
1.FactoryBean:工廠類接口,用戶能夠經過實現該接口定製實例化 bean的邏輯。咱們把bean比做是人,那麼FactoryBean則是女媧,首先它自己有人的特徵,但它可以生產人。
2.BeanFactory :BeanFactory定義了 IOC 容器的最基本形式。若是bean還比做是人,那麼它能夠理解成三界,三界裏有各類功能的人,它是一個容器,能夠管理不少的人。
FactoryBean裏幹了什麼事情?
public interface FactoryBean<T> { //返回由FactoryBean建立的Bean實例,若是isSingleton返回true,則該實例會放到spring容器中單例緩存池中. T getObject() throws Exception; //返回FactoryBean建立的bean類型. Class<?> getObjectType(); //返回由FactoryBean建立的bean實例的做用域是singleton仍是prototype boolean isSingleton(); }
它的做用不在這裏作闡述,ref:Spring的FactoryBean使用
寫到這裏,博主總結一下閱讀spring源碼的心得:
1.學習spring思想和編碼規範。spring的不少函數代碼量大,邏輯複雜,而spring的編碼風格就是將複雜的邏輯分解,分紅N個小函數的嵌套,每一層都是對下一層的總結和概要。博主在工做中最佩服的一個大神說過:學習spring源碼思想爲我所用,哪怕是一天學習一個變量名,他在工做中設計不少小組件的時候都是基於spring思想和規範。他說,不要迷茫學什麼技術,其實天天只要進步一點點就好,突破的是本身,而不是某個領域。用10年其實才敢說入門一門技術。
2.跟了spring代碼的函數,你會或多或少發現一些規律:一個真正幹活的函數實際上是以do開頭的,如doGetBean,而給咱們錯覺的函數,如getBean和createBean等等方法,其實只是從全局角度作一些統籌工做。
3.放棄閱讀源碼是一個不明智的選擇,由於你失去了跟大師學習的機會。當你硬着頭皮讀完一個框架的源碼,則其餘框架都是相通的。
4.博主的下一篇文章:spring AOP是什麼?你都拿它作什麼?由於篇幅有限,而博主的已經搞了一天代碼,手已經快練成麒麟臂,Aop又是一個重要且內容比較多的部分,因此打算單獨拿出來搞事情。