Spring容器深刻(li)

  spring中最經常使用的控制反轉和麪向切面編程。java

1、IOC程序員

  IoC(Inversion of Control,控制倒轉)。對於spring框架來講,就是由spring來負責控制對象的生命週期和對象間的關係。在一個對象中,若是要使用另外的對象,就必須獲得它(本身new一個,或者從JNDI中查詢一個),使用完以後還要將對象銷燬(好比Connection等),對象始終會和其餘的接口或類藕合起來。正則表達式

  全部的類都會在spring容器中登記,告訴spring你是個什麼東西,你須要什麼東西,而後spring會在系統運行到適當的時候,把你要的東西主動給你,同時也把你交給其餘須要你的東西。全部的類的建立、銷燬都由 spring來控制,也就是說控制對象生存週期的再也不是引用它的對象,而是spring。對於某個具體的對象而言,之前是它控制其餘對象,如今是全部對象都被spring控制,因此這叫控制反轉。spring

  IoC的一個重點是在系統運行中,動態的向某個對象提供它所須要的其餘對象。這一點是經過DI(Dependency Injection,依賴注入)來實現的。好比對象A須要操做數據庫,原本須要在A中本身編寫代碼來得到一個Connection對象,有了spring咱們就只須要告訴spring,A中須要一個Connection,至於這個Connection怎麼構造,什麼時候構造,A不須要知道。在系統運行時,spring會在適當的時候製造一個Connection,而後像打針同樣,注射到A當中,這樣就完成了對各個對象之間關係的控制。A須要依賴Connection才能正常運行,而這個Connection是由spring注入到A中的,依賴注入的名字就這麼來的。那麼DI是如何實現的呢? Java 1.3以後一個重要特徵是反射(reflection),它容許程序在運行的時候動態的生成對象、執行對象的方法、改變對象的屬性,spring就是經過反射來實現注入的。數據庫

  在沒有使用Spring的時候,每一個對象在須要使用他的合做對象時,本身均要使用像new object() 這樣的語法來將合做對象建立出來,這個合做對象是由本身主動建立出來的,建立合做對象的主動權在本身手上,本身須要哪一個合做對象,就主動去建立,建立合做對象的主動權和建立時機是由本身把控的,而這樣就會使得對象間的耦合度高了,A對象須要使用合做對象B來共同完成一件事,A要使用B,那麼A就對B產生了依賴,也就是A和B之間存在一種耦合關係,而且是緊密耦合在一塊兒,而使用了Spring以後就不同了,建立合做對象B的工做是由Spring來作的,Spring建立好B對象,而後存儲到一個容器裏面,當A對象須要使用B對象時,Spring就從存放對象的那個容器裏面取出A要使用的那個B對象,而後交給A對象使用,至於Spring是如何建立那個對象,以及何時建立好對象的,A對象不須要關心這些細節問題(你是何時生的,怎麼生出來的我可不關心,能幫我幹活就行),A獲得Spring給咱們的對象以後,兩我的一塊兒協做完成要完成的工做便可。編程

  因此控制反轉IoC(Inversion of Control)是說建立對象的控制權進行轉移,之前建立對象的主動權和建立時機是由本身把控的,而如今這種權力轉移到第三方,好比轉移交給了IoC容器,它就是一個專門用來建立對象的工廠,你要什麼對象,它就給你什麼對象,有了IoC容器,依賴關係就變了,原先的依賴關係就沒了,它們都依賴IoC容器了,經過IoC容器來創建它們之間的關係。緩存

2、IOC的好處安全

  可維護性比較好,很是便於進行單元測試,便於調試程序和診斷故障。代碼中的每個Class均可以單獨測試,彼此之間互不影響,只要保證自身的功能無誤便可,這就是組件之間低耦合或者無耦合帶來的好處。服務器

  每一個開發團隊的成員都只須要關心實現自身的業務邏輯,徹底不用去關心其它的人工做進展,由於你的任務跟別人沒有任何關係,你的任務能夠單獨測試,你的任務也不用依賴於別人的組件,不再用扯不清責任了。app

  可複用性好,咱們能夠把具備廣泛性的經常使用組件獨立出來,反覆利用到項目中的其它部分,或者是其它項目,固然這也是面向對象的基本特徵。

  IOC生成對象的方式轉爲外置方式,也就是把對象生成放在配置文件裏進行定義,這樣,當咱們更換一個實現子類將會變得很簡單,只要修改配置文件就能夠了,徹底具備熱插撥的特性。

3、IOC常見的注入方式

  接口注入(Spring不支持),接口注入模式由於歷史較爲悠久,在不少容器中都已經獲得應用。但因爲其在靈活性、易用性上不如其餘兩種注入模式,於是在 IOC 的專題世界內並不被看好。

  setter注入,對於習慣了傳統 javabean 開發的程序員,經過 setter 方法設定依賴關係更加直觀。 若是依賴關係較爲複雜,那麼構造子注入模式的構造函數也會至關龐大,而此時設值注入模式則更爲簡潔。若是用到了第三方類庫,可能要求咱們的組件提供一個默認的構造函數,此時構造子注入模式也不適用。

  構造器注入,在構造期間完成一個完整的、合法的對象。 全部依賴關係在構造函數中集中呈現。 依賴關係在構造時由容器一次性設定,組件被建立以後一直處於相對「不變」的穩定狀態。 只有組件的建立者關心其內部依賴關係,對調用者而言,該依賴關係處於「黑盒」之中。

4、AOP

  AOP(Aspect-OrientedProgramming,面向切面編程)。例如日誌功能。日誌代碼每每水平地散佈在全部對象層次中,而與它所散佈到的對象的核心功能毫無關係。對於其餘類型的代碼,如安全性、異常處理和透明的持續性也是如此。這種散佈在各處的無關的代碼被稱爲橫切(cross-cutting)代碼,在OOP設計中,它致使了大量代碼的重複,而不利於各個模塊的重用。

  而AOP技術則偏偏相反,它利用一種稱爲「橫切」的技術,剖解開封裝的對象內部,並將那些影響了多個類的公共行爲封裝到一個可重用模塊,並將其名爲「Aspect」,即切面。所謂「切面」,簡單地說,就是將那些與業務無關,卻爲業務模塊所共同調用的邏輯或責任封裝起來,便於減小系統的重複代碼,下降模塊間的耦合度,並有利於將來的可操做性和可維護性。AOP表明的是一個橫向的關係。

  使用「橫切」技術,AOP把軟件系統分爲兩個部分:核心關注點和橫切關注點。業務處理的主要流程是核心關注點,與之關係不大的部分是橫切關注點。橫切關注點的一個特色是,他們常常發生在覈心關注點的多處,而各處都基本類似。好比權限認證、日誌、事務處理。Aop 的做用在於分離系統中的各類關注點,將核心關注點和橫切關注點分離開來。

  實現AOP的技術,主要分爲兩大類:一是採用動態代理技術,利用截取消息的方式,對該消息進行裝飾,以取代原有對象行爲的執行;二是採用靜態織入的方式,引入特定的語法建立「方面」,從而使得編譯器能夠在編譯期間織入有關「方面」的代碼。

5、AOP概念

  切面(Aspect):一個關注點的模塊化,這個關注點實現可能另外橫切多個對象。事務管理是J2EE應用中一個很好的橫切關注點例子。切面用spring的 Advisor或攔截器實現。

  鏈接點(Joinpoint): 程序執行過程當中明確的點,如方法的調用或特定的異常被拋出。

  通知(Advice): 在特定的鏈接點,AOP框架執行的動做。各類類型的通知包括「around」、「before」和「throws」通知。通知類型將在下面討論。許多AOP框架包括Spring都是以攔截器作通知模型,維護一個「圍繞」鏈接點的攔截器鏈。Spring中定義了四個advice: BeforeAdvice, AfterAdvice, ThrowAdvice和DynamicIntroductionAdvice。

  切入點(Pointcut): 指定一個通知將被引起的一系列鏈接點的集合。AOP框架必須容許開發者指定切入點:例如,使用正則表達式。 Spring定義了Pointcut接口,用來組合MethodMatcher和ClassFilter,能夠經過名字很清楚的理解, MethodMatcher是用來檢查目標類的方法是否能夠被應用此通知,而ClassFilter是用來檢查Pointcut是否應該應用到目標類上。

  引入(Introduction): 添加方法或字段到被通知的類。 Spring容許引入新的接口到任何被通知的對象。例如,你可使用一個引入使任何對象實現 IsModified接口,來簡化緩存。Spring中要使用Introduction, 可有經過DelegatingIntroductionInterceptor來實現通知,經過DefaultIntroductionAdvisor來配置Advice和代理類要實現的接口。

  目標對象(Target Object): 包含鏈接點的對象。也被稱做被通知或被代理對象。POJO

  AOP代理(AOP Proxy): AOP框架建立的對象,包含通知。 在Spring中,AOP代理能夠是JDK動態代理或者CGLIB代理。

  織入(Weaving): 組裝方面來建立一個被通知對象。這能夠在編譯時完成(例如使用AspectJ編譯器),也能夠在運行時完成。Spring和其餘純Java AOP框架同樣,在運行時完成織入

6、AOP通知類型

  前置通知(before advice):在切入點以前執行。

  後置通知(after returning advice):在切入點執行完成後,執行通知。

  環繞通知(around advice):包圍切入點,調用方法先後完成自定義行爲。

  異常通知(after throwing advice):在切入點拋出異常後,執行通知。

7、Spring啓動過程

  經過對ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");這條語句的跟蹤,最重要的是調用了AbstractApplicationContext類的refresh()方法。refresh()方法有如下幾個步驟:

  1.第一個方法是prepareRefresh(),prepareRefresh()只包含了兩個方法,這兩個方法加載了服務器的一些信息和判斷必填項是否都完整。第二個方法:ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();refreshBeanFactory()這個方法,若是容器如今已經啓動,而咱們要新啓動一個容器,那麼首要的就是銷燬之前的容器,清空容器和容器中註冊了的bean。新建容器對象DefaultListableBeanFactory beanFactory = createBeanFactory();新建一個DefaultListableBeanFactory.注意在這裏面有一個方法getInternalParentBeanFactory(),這個方法得到並設置了父容器。固然在spring啓動的過程當中這個父容器是空的。

  2.判斷bean是否容許覆蓋,bean是否容許循環引用,把java註解和spring標準註解都放到了容器裏面來。

  3.要加載bean loadBeanDefinitions(beanFactory)。實現了該方法的類一共有三個一、XmlWebApplicationContext 二、AbstractXmlApplicationContext 三、AnnotationConfigWebApplicationContext.在項目啓動的過程當中XmlWebApplicationContext真正的被執行。接下來的bean加載過程主要涉及到的就是配置文件解析。配置文件解析大致步驟一、根據命名空間將標籤名稱和解析類放置到map中二、讀取配置文件的過程當中遇到標籤就將找到解析類去解析。

8、如何感知到Spring容器啓動成功這件事情?

  spring提供了事件監聽器的處理機制,spring提供了內置的幾類的事件:

    ContextClosedEvent 當使用ConfigurableApplicationContext接口的close()方法關閉ApplicationContext容器時觸發該事件。

    ContextRefreshedEvent ApplicationContext容器初始化或者刷新時觸發該事件。

    ContextStartedEvent 當使用ConfigurableApplicationContext接口的start()方法啓動ApplicationContext容器時觸發該事件。

    ContextStoppedEvent 當使用ConfigurableApplicationContext接口的stop()方法中止ApplicationContext容器時觸發該事件。

    RequestHandleEvent。

在spring容器啓動完成後會觸發ContextRefreshedEvent事件,在spring容器啓動過程當中調用AbstractApplicationContext的refresh()方法,其中調用了finishRefresh()用來發布這個事件。

ApplicationEventMulticaster在接收到ApplicationEvent事件以後,經過multicastEvent方法,通知全部的觀察者ApplicationListener。

好比hsf中經過建立一個ContextRefreshedEvent,ContextClosedEvent事件監聽器,在spring容器啓動完成後和容器關閉時,作一些處理動做。

9、FactoryBean與BeanFactory

  Spring中有兩種類型的Bean,一種是普通Bean,另外一種是工廠Bean,即FactoryBean,這兩種Bean都被容器管理,但工廠Bean跟普通Bean不一樣,其返回的對象不是指定類的一個實例,其返回的是該FactoryBean的getObject方法所返回的對象。

  BeanFactory是IoC容器的核心接口。它的職責包括:實例化、定位、配置應用程序中的對象及創建這些對象間的依賴。從本質上講,BeanFactory僅僅只是一個維護bean定義以及相互依賴關係的高級工廠接口。經過BeanFactory咱們能夠訪問bean定義。

10、如何在Bean初始化先後作一些事情

  首先看下AbstractAutowireCapableBeanFactory的createBean方法:(刪去些佔地方的try catch)。

protected Object createBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) throws BeanCreationException {
    resolveBeanClass(mbd, beanName); /1解析Bean的class
    mbd.prepareMethodOverrides(); //2 方法注入準備
    Object bean = resolveBeforeInstantiation(beanName, mbd); //3 第一個BeanPostProcessor擴展點
    if (bean != null) { //4 若是3處的擴展點返回的bean不爲空,直接返回該bean,後續流程不須要執行
        return bean;
    }
    Object beanInstance = doCreateBean(beanName, mbd, args); //5 執行spring的建立bean實例的流程啦
    return beanInstance;
}

接着:

protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
        Object bean = null;
        if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
            // Make sure bean class is actually resolved at this point.
            if (mbd.hasBeanClass() && !mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
                //3.一、執行InstantiationAwareBeanPostProcessor的postProcessBeforeInstantiation回調方法
                bean = applyBeanPostProcessorsBeforeInstantiation(mbd.getBeanClass(), beanName);
                if (bean != null) {
                    //3.二、執行InstantiationAwareBeanPostProcessor的postProcessAfterInitialization回調方法
                    bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
                }
            }
            mbd.beforeInstantiationResolved = (bean != null);
        }
        return bean;
}

接着:

protected Object applyBeanPostProcessorsBeforeInstantiation(Class beanClass, String beanName)
            throws BeansException {
 
        for (Iterator it = getBeanPostProcessors().iterator(); it.hasNext();) {
            BeanPostProcessor beanProcessor = (BeanPostProcessor) it.next();
            if (beanProcessor instanceof InstantiationAwareBeanPostProcessor) {
                InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) beanProcessor;
                Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);
                if (result != null) {
                    return result;
                }
            }
        }
        return null;
    }

接着:

public interface BeanPostProcessor {
    Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
    Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}

經過代碼咱們能夠看到BeanPostProcessor,能夠在spring容器實例化bean以後,在執行bean的初始化方法先後,添加一些本身的處理邏輯。

10、如何在bean銷燬的時候作一些事情?

  有兩種方法:A.利用destroy-method配置 B.實現DisposableBean接口。實現DisposableBean接口,在destroy()方法裏作一些操做。或者對配置文件加入destroy-method屬性指定方法;若是二者同時出現,先執行DisposableBean接口的destroy()方法,而後再執行destroy-method屬性指定方法。

11、Bean的生命週期

  何時初始化Bean?當scope=singleton,即默認狀況,會在容器初始化時實例化。但咱們能夠指定Bean節點的lazy-init=」true」來延遲初始化bean,這時候,只有第一次獲取bean纔會初始化bean,即第一次請求該bean時才初始化。以下配置所示:

<bean id=」xxx」 class=」examples.test.OrderServiceBean」 lazy-init=」true」 />

若是想對全部bean都應用延遲初始化,能夠在根節點beans設置default-lazy-init=」true」,以下所示:

<beans default-lazy-init=」true」 >

scope=prototype時,也會延遲初始化bean,即第一次請求該bean時才初始化(如調用getBean()方法時)。

  Bean的生命週期,構造器、init方法、獲取bean後的操做、destroy方法(ctx.close時執行)。注意:若是bean的scope設爲prototype時,當ctx.close時,destroy方法不會被調用。緣由:對於prototype做用域的bean,有一點很是重要,那就是Spring不能對一個prototype bean的整個生命週期負責:容器在初始化、配置、裝飾或者是裝配完一個prototype實例後,將它交給客戶端,隨後就對該prototype實例漠不關心了。無論何種做用域,容器都會調用全部對象的初始化生命週期回調方法。但對prototype而言,任何配置好的析構生命週期回調方法都將不會 被調用。清除prototype做用域的對象並釋聽任何prototype bean所持有的昂貴資源,都是客戶端代碼的職責。(讓Spring容器釋放被prototype做用域bean佔用資源的一種可行方式是,經過使用bean的後置處理器,該處理器持有要被清除的bean的引用。)

在spring容器啓動時,refresh方法中,調用invokeBeanFactoryPostProcessors(beanFactory);

 

     在 invokeBeanFactoryPostProcessors 這個方法裏,獲取實現 BeanFactoryPostProcessor 接口的 bean ,將其排序後,依次調用invokeBeanFactoryPostProcessors。

最終調用了postProcessor.postProcessBeanFactory(beanFactory)方法,該方法會對bean作初始的處理,具體處理方式與子類具體實現有關。

 

相關文章
相關標籤/搜索