程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> JAVA綜合教程 >> Spring容器深入(li),spring容器深入li

Spring容器深入(li),spring容器深入li

編輯:JAVA綜合教程

Spring容器深入(li),spring容器深入li


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

一、IOC

  IoC(Inversion of Control,控制倒轉)。對於spring框架來說,就是由spring來負責控制對象的生命周期和對象間的關系。在一個對象中,如果要使用另外的對象,就必須得到它(自己new一個,或者從JNDI中查詢一個),使用完之後還要將對象銷毀(比如Connection等),對象始終會和其他的接口或類藕合起來。

  所有的類都會在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容器來建立它們之間的關系。

二、IOC的好處

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

  每個開發團隊的成員都只需要關心實現自身的業務邏輯,完全不用去關心其它的人工作進展,因為你的任務跟別人沒有任何關系,你的任務可以單獨測試,你的任務也不用依賴於別人的組件,再也不用扯不清責任了。

  可復用性好,我們可以把具有普遍性的常用組件獨立出來,反復利用到項目中的其它部分,或者是其它項目,當然這也是面向對象的基本特征。

  IOC生成對象的方式轉為外置方式,也就是把對象生成放在配置文件裡進行定義,這樣,當我們更換一個實現子類將會變得很簡單,只要修改配置文件就可以了,完全具有熱插撥的特性。

三、IOC常見的注入方式

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

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

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

四、AOP

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

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

  使用“橫切”技術,AOP把軟件系統分為兩個部分:核心關注點和橫切關注點。業務處理的主要流程是核心關注點,與之關系不大的部分是橫切關注點。橫切關注點的一個特點是,他們經常發生在核心關注點的多處,而各處都基本相似。比如權限認證、日志、事務處理。Aop 的作用在於分離系統中的各種關注點,將核心關注點和橫切關注點分離開來。

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

五、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框架一樣,在運行時完成織入

六、AOP通知類型

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

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

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

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

七、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)。實現了該方法的類一共有三個1、XmlWebApplicationContext 2、AbstractXmlApplicationContext 3、AnnotationConfigWebApplicationContext.在項目啟動的過程中XmlWebApplicationContext真正的被執行。接下來的bean加載過程主要涉及到的就是配置文件解析。配置文件解析大體步驟1、根據命名空間將標簽名稱和解析類放置到map中2、讀取配置文件的過程中遇到標簽就將找到解析類去解析。

八、如何感知到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容器啟動完成後和容器關閉時,做一些處理動作。

九、FactoryBean與BeanFactory

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

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

十、如何在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.1、執行InstantiationAwareBeanPostProcessor的postProcessBeforeInstantiation回調方法
                bean = applyBeanPostProcessorsBeforeInstantiation(mbd.getBeanClass(), beanName);
                if (bean != null) {
                    //3.2、執行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的初始化方法前後,添加一些自己的處理邏輯。

十、如何在bean銷毀的時候做一些事情?

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

十一、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做初始的處理,具體處理方式與子類具體實現有關。

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved