1. 程式人生 > >Spring常見面試題總結(超詳細回答)

Spring常見面試題總結(超詳細回答)

1、Spring是什麼?

        Spring是一個輕量級的IoC和AOP容器框架。目的是解決企業應用開發的複雜性,使用基本的JavaBean來完成以前只可能由EJB完成的事情,並提供了更多的企業應用功能,Spring的用途不僅限於伺服器端的開發,從簡單性、可測試性和鬆耦合的角度而言,任何Java應用都可以從Spring中受益。

2、Spring 的優點?

(1)spring屬於低侵入式設計,程式碼的汙染極低;

(2)spring的DI機制降低了業務物件替換的複雜性;

(3)容器提供了AOP技術,利用它很容易實現如許可權攔截,執行期監控等功能;

(4)降低了元件之間的耦合性 ,實現了軟體各層之間的解耦; 

(5)容器提供單例模式支援;

(6)可以使用容器提供的眾多服務,如事務管理,訊息服務等;

(7)容器提供了眾多的輔助類,能加快應用的開發;

(8)spring對於主流的應用框架提供了整合支援,如hibernate,JPA,Struts等 

(9)獨立於各種應用伺服器 

(10)Spring的高度開放性,並不強制應用完全依賴於Spring,開發者可以自由選擇spring的部分或全部。

3、Spring的AOP理解:

        AOP,一般稱為面向方面(切面)程式設計,作為面向物件的一種補充,用於解剖封裝好的物件內部,找出其中對多個物件產生影響的公共行為,並將其封裝為一個可重用的模組,這個模組被命名為“切面”(Aspect),切面將那些與業務無關,卻被業務模組共同呼叫的邏輯提取並封裝起來,減少了系統中的重複程式碼,降低了模組間的耦合度,同時提高了系統的可維護性。可用於許可權認證、日誌、事務處理。

        AOP實現的關鍵在於AOP框架自動建立的AOP代理,AOP代理主要分為靜態代理和動態代理。靜態代理的代表為AspectJ;動態代理則以Spring AOP為代表。

(1)AspectJ是靜態代理的增強,所謂靜態代理,就是AOP框架會在編譯階段生成AOP代理類,因此也稱為編譯時增強,他會在編譯階段將AspectJ織入到Java位元組碼中,執行的時候就是增強之後的AOP物件。

(2)Spring AOP使用的動態代理,所謂的動態代理就是說AOP框架不會去修改位元組碼,而是每次執行時在記憶體中臨時為方法生成一個AOP物件,這個AOP物件包含了目標物件的全部方法,並且在特定的切點做了增強處理,並回調原物件的方法。

Spring AOP中的動態代理主要有兩種方式,JDK動態代理和CGLIB動態代理:

        ①JDK動態代理通過反射來接收被代理的類,並且要求被代理的類必須實現一個介面。JDK動態代理的核心是InvocationHandler介面和Proxy類。生成的代理物件的方法呼叫都會委託到InvocationHandler.invoke()方法,當我們呼叫代理類物件的方法時,這個“呼叫”會轉送到invoke方法中,代理類物件作為proxy引數傳入,引數method標識了我們具體呼叫的是代理類的哪個方法,args為這個方法的引數。

        ②如果目標類沒有實現介面,那麼Spring AOP會選擇使用CGLIB來動態代理目標類。CGLIB(Code Generation Library),是一個程式碼生成的類庫,可以在執行時動態的生成指定類的一個子類物件,並覆蓋其中特定方法,覆蓋方法時可以新增增強程式碼,從而實現AOP。CGLIB是通過繼承的方式做的動態代理,因此如果某個類被標記為final,那麼它是無法使用CGLIB做動態代理的。

(3)靜態代理與動態代理區別在於生成AOP代理物件的時機不同,相對來說AspectJ的靜態代理方式具有更好的效能,但是AspectJ需要特定的編譯器進行處理,而Spring AOP則無需特定的編譯器處理。

4、Spring的IoC理解:

(1)IOC就是控制反轉。就是物件的建立權反轉交給Spring,由容器控制程式之間的依賴關係,作用是實現了程式的解耦合,而非傳統實現中,由程式程式碼直接操控。(依賴)控制權由應用程式碼本身轉到了外部容器,由容器根據配置檔案去建立例項並管理各個例項之間的依賴關係,控制權的轉移,是所謂反轉,並且由容器動態的將某種依賴關係注入到元件之中。BeanFactory 是Spring IoC容器的具體實現與核心介面,提供了一個先進的配置機制,使得任何型別的物件的配置成為可能,用來包裝和管理各種bean。

(2)最直觀的表達就是,IOC讓物件的建立不用去new了,可以由spring自動生產,這裡用的就是java的反射機制,通過反射在執行時動態的去建立、呼叫物件。spring就是根據配置檔案在執行時動態的去建立物件,並呼叫物件的方法的。

(3)Spring的IOC有三種注入方式 : 
        第一是根據屬性注入,也叫set方法注入; 
        第二種是根據構造方法進行注入; 
        第三種是根據註解進行注入。

詳細的說:

(4)IoC,控制反轉:將物件交給容器管理,你只需要在spring配置檔案總配置相應的bean,以及設定相關的屬性,讓spring容器生成類的例項物件以及管理物件。在spring容器啟動的時候,spring會把你在配置檔案中配置的bean都初始化以及裝配好,然後在你需要呼叫的時候,就把它已經初始化好的那些bean分配給你需要呼叫這些bean的類。就是將物件的控制權反轉給spring容器管理。

(5)DI機制(Dependency Injection,依賴注入):可以說是IoC的其中一個內容,在容器例項化物件的時候主動的將被呼叫者(或者說它的依賴物件)注入給呼叫物件。比如物件A需要操作資料庫,以前我們總是要在A中自己編寫程式碼來獲得一個Connection物件,有了 spring我們就只需要告訴spring,A中需要一個Connection,至於這個Connection怎麼構造,何時構造,A不需要知道。在系統執行時,spring會在適當的時候製造一個Connection,然後像打針一樣,注射到A當中,這樣就完成了對各個物件之間關係的控制。

IoC讓相互協作的元件保持鬆散的耦合,而AOP程式設計允許你把遍佈於應用各層的功能分離出來形成可重用的功能元件。

5、BeanFactory和ApplicationContext有什麼區別?

        BeanFactory和ApplicationContext是Spring的兩大核心介面,而其中ApplicationContext是BeanFactory的子介面。它們都可以當做Spring的容器,生成Bean例項的,並管理容器中的Bean。

(1)BeanFactory:是Spring裡面最底層的介面,提供了最簡單的容器的功能,負責讀取bean配置文件,管理bean的載入與例項化,維護bean之間的依賴關係,負責bean的生命週期,但是無法支援spring的aop功能和web應用。

(2)ApplicationContext介面作為BeanFactory的派生,因而具有BeanFactory所有的功能。而且ApplicationContext還在功能上做了擴充套件,以一種更面向框架的方式工作以及對上下文進行分層和實現繼承,相較於BeanFactorty,ApplicationContext還提供了以下的功能: 

①預設初始化所有的Singleton,也可以通過配置取消預初始化。

②繼承MessageSource,因此支援國際化。

③資源訪問,比如訪問URL和檔案。

④事件機制。

⑤同時載入多個配置檔案。

⑥以宣告式方式啟動並建立Spring容器。

⑦載入多個(有繼承關係)上下文 ,使得每一個上下文都專注於一個特定的層次,比如應用的web層。

(3)①BeanFactroy採用的是延遲載入形式來注入Bean的,即只有在使用到某個Bean時(呼叫getBean()),才對該Bean進行載入例項化,這樣,我們就不能發現一些存在的Spring的配置問題。如果Bean的某一個屬性沒有注入,BeanFacotry載入後,直至第一次使用呼叫getBean方法才會丟擲異常。

        ②而ApplicationContext則相反,它是在容器啟動時,一次性建立了所有的Bean。這樣,在容器啟動時,我們就可以發現Spring中存在的配置錯誤,這樣有利於檢查所依賴屬性是否注入。 ApplicationContext啟動後預載入所有的單例項Bean,通過預載入單例項bean ,確保當你需要的時候,你就不用等待,因為它們已經建立好了。

        ③相對於基本的BeanFactory,ApplicationContext 唯一的不足是佔用記憶體空間。當應用程式配置Bean較多時,程式啟動較慢。

(4)BeanFactory通常以程式設計的方式被建立,ApplicationContext還能以宣告的方式建立,如使用ContextLoader。

(5)BeanFactory和ApplicationContext都支援BeanPostProcessor、BeanFactoryPostProcessor的使用,但兩者之間的區別是:BeanFactory需要手動註冊,而ApplicationContext則是自動註冊。

6、 解釋Spring支援的幾種bean的作用域。

Spring容器中的bean可以分為5個範圍:

(1)singleton:這種bean範圍是預設的,這種範圍確保不管接受到多少個請求,每個容器中只有一個bean的例項,單例的模式由bean factory自身來維護。
(2)prototype:原形範圍與單例範圍相反,為每一個bean請求提供一個例項。
(3)request:在請求bean範圍內會每一個來自客戶端的網路請求建立一個例項,在請求完成以後,bean會失效並被垃圾回收器回收。
(4)Session:與請求範圍類似,確保每個session中有一個bean的例項,在session過期後,bean會隨之失效。
(5)global-session:global-session和Portlet應用相關。當你的應用部署在Portlet容器中工作時,它包含很多portlet。如果你想要宣告讓所有的portlet共用全域性的儲存變數的話,那麼這全域性變數需要儲存在global-session中。全域性作用域與Servlet中的session作用域效果相同。

7、請解釋Spring Bean的生命週期?

 首先說一下Servlet的生命週期:例項化,初始init,接收請求service,銷燬destroy;

 Spring上下文中的Bean生命週期也類似,如下:

(1)例項化一個Bean--也就是我們常說的new;

(2)按照Spring上下文對例項化的Bean進行配置--也就是IOC注入;

(3)如果這個Bean已經實現了BeanNameAware介面,會呼叫它實現的setBeanName(String)方法,此處傳遞的就是Spring配置檔案中Bean的id值;

(4)如果這個Bean已經實現了BeanFactoryAware介面,會呼叫它實現的setBeanFactory(setBeanFactory(BeanFactory)傳遞的是Spring工廠自身(可以用這個方式來獲取其它Bean,只需在Spring配置檔案中配置一個普通的Bean就可以);

(5)如果這個Bean已經實現了ApplicationContextAware介面,會呼叫setApplicationContext(ApplicationContext)方法,傳入Spring上下文(同樣這個方式也可以實現步驟4的內容,但比4更好,因為ApplicationContext是BeanFactory的子介面,有更多的實現方法);

(6)如果這個Bean關聯了BeanPostProcessor介面,將會呼叫postProcessBeforeInitialization(Object obj, String s)方法,BeanPostProcessor經常被用作是Bean內容的更改,並且由於這個是在Bean初始化結束時呼叫那個的方法,也可以被應用於記憶體或快取技術;

(7)如果Bean在Spring配置檔案中配置了init-method屬性會自動呼叫其配置的初始化方法。

(8)如果這個Bean關聯了BeanPostProcessor介面,將會呼叫postProcessAfterInitialization(Object obj, String s)方法、;

注:以上工作完成以後就可以應用這個Bean了,那這個Bean是一個Singleton的,所以一般情況下我們呼叫同一個id的Bean會是在內容地址相同的例項,當然在Spring配置檔案中也可以配置非Singleton。

(9)當Bean不再需要時,會經過清理階段,如果Bean實現了DisposableBean這個介面,會呼叫那個其實現的destroy()方法;

(10)最後,如果這個Bean的Spring配置中配置了destroy-method屬性,會自動呼叫其配置的銷燬方法。

 另外我們這裡描述的是應用Spring上下文Bean的生命週期,如果應用Spring的工廠也就是BeanFactory的話去掉第5步就Ok了。

8、Spring中bean的載入過程:

(1)獲取配置檔案資源;

(2)對獲取的xml資源進行一定的處理檢驗;

(3)處理包裝資源;

(4)解析處理包裝過後的資源;

(5)載入提取bean並註冊(新增到beanDefinitionMap中)。

9、Spring框架中的單例Beans是執行緒安全的麼?

        Spring框架並沒有對單例bean進行任何多執行緒的封裝處理。關於單例bean的執行緒安全和併發問題需要開發者自行去搞定。但實際上,大部分的Spring bean並沒有可變的狀態(比如Serview類和DAO類),所以在某種程度上說Spring的單例bean是執行緒安全的。如果你的bean有多種狀態的話(比如 View Model 物件),就需要自行保證執行緒安全。
最淺顯的解決辦法就是將多型bean的作用域由“singleton”變更為“prototype”。

10、Spring如何處理執行緒併發問題?

Spring使用ThreadLocal解決執行緒安全問題。

我們知道在一般情況下,只有有狀態的Bean才可以在多執行緒環境下共享,在Spring中,絕大部分Bean都可以宣告為singleton作用域。就是因為Spring對一些Bean(如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder等)中非執行緒安全狀態採用ThreadLocal進行處理,讓它們也成為執行緒安全的狀態,因為有狀態的Bean就可以在多執行緒中共享了。

ThreadLocal和執行緒同步機制都是為了解決多執行緒中相同變數的訪問衝突問題。

(1)在同步機制中,通過物件的鎖機制保證同一時間只有一個執行緒訪問變數。這時該變數是多個執行緒共享的,使用同步機制要求程式慎密地分析什麼時候對變數進行讀寫,什麼時候需要鎖定某個物件,什麼時候釋放物件鎖等繁雜的問題,程式設計和編寫難度相對較大。

(2)而ThreadLocal則從另一個角度來解決多執行緒的併發訪問。ThreadLocal會為每一個執行緒提供一個獨立的變數副本,從而隔離了多個執行緒對資料的訪問衝突。因為每一個執行緒都擁有自己的變數副本,從而也就沒有必要對該變數進行同步了。ThreadLocal提供了執行緒安全的共享物件,在編寫多執行緒程式碼時,可以把不安全的變數封裝進ThreadLocal。

(3)概括起來說,對於多執行緒資源共享的問題,同步機制採用了“以時間換空間”的方式,而ThreadLocal採用了“以空間換時間”的方式。前者僅提供一份變數,讓不同的執行緒排隊訪問,而後者為每一個執行緒都提供了一份變數,因此可以同時訪問而互不影響。

11、請解釋Spring自動裝配模式的區別?

在Spring框架中共有5種自動裝配:

(1)no:這是Spring框架的預設設定,在該設定下自動裝配是關閉的,開發者需要自行在bean定義中用標籤明確的設定依賴關係。

(2)byName:該選項可以根據bean名稱設定依賴關係。當向一個bean中自動裝配一個屬性時,容器將根據bean的名稱自動在配置檔案中查詢一個匹配的bean。如果找到的話,就裝配這個屬性,如果沒找到的話就報錯。

(3)byType:該選項可以根據bean型別設定依賴關係。當向一個bean中自動裝配一個屬性時,容器將根據bean的型別自動在在配置檔案中查詢一個匹配的bean。如果找到的話,就裝配這個屬性,如果沒找到的話就報錯。

(4)constructor:構造器的自動裝配和byType模式類似,但是僅僅適用於與有構造器相同引數的bean,如果在容器中沒有找到與構造器引數型別一致的bean,那麼將會丟擲異常。

(5)autodetect:該模式自動探測使用構造器自動裝配或者byType自動裝配。首先,首先會嘗試找合適的帶引數的構造器,如果找到的話就是用構造器自動裝配,如果在bean內部沒有找到相應的構造器或者是無參構造器,容器就會自動選擇byTpe的自動裝配方式。

12、Spring 控制器的載入過程:(XML版)

(1)Web容器建立;

(2)上下文建立,但未初始化;

(3)監聽器建立,並註冊到Context上;

(4)上下文初始化;

(5)通知到監聽者,Spring配置檔案/@Configuration載入;

(6)Load-on-startup>0的ServletConfig建立,springMVC的DispatcherServlet此時建立。

PS:Spring容器時SpringMVC的父容器。Spring的AOP在Spring的上下文建立時就會建立;如果想要代理SpringMVC的控制層,需要將配置寫到SpringMVC的配置檔案下。

13、Spring 框架中都用到了哪些設計模式?

(1)代理模式—在AOP和remoting中被用的比較多。

(2)單例模式—在spring配置檔案中定義的bean預設為單例模式。

(3)工廠模式—BeanFactory用來建立物件的例項。

(4)模板方法—用來解決程式碼重複的問題。比如. RestTemplate, JmsTemplate, JpaTemplate。

(5)前端控制器—Spring提供了DispatcherServlet來對請求進行分發。

(6)檢視幫助(View Helper )—Spring提供了一系列的JSP標籤,高效巨集來輔助將分散的程式碼整合在視圖裡。

(7)依賴注入—貫穿於BeanFactory / ApplicationContext介面的核心理念。

14、Spring事務的種類和各自的區別:

spring支援程式設計式事務管理和宣告式事務管理兩種方式:

(1)程式設計式事務管理使用TransactionTemplate或者直接使用底層的PlatformTransactionManager。對於程式設計式事務管理,spring推薦使用TransactionTemplate。

(2)宣告式事務管理建立在AOP之上的。其本質是對方法前後進行攔截,然後在目標方法開始之前建立或者加入一個事務,在執行完目標方法之後根據執行情況提交或者回滾事務。宣告式事務最大的優點就是不需要通過程式設計的方式管理事務,這樣就不需要在業務邏輯程式碼中摻雜事務管理的程式碼,只需在配置檔案中做相關的事務規則宣告(或通過基於@Transactional註解的方式),便可以將事務規則應用到業務邏輯中。

(3)顯然宣告式事務管理要優於程式設計式事務管理,這正是spring倡導的非侵入式的開發方式。宣告式事務管理使業務程式碼不受汙染,一個普通的POJO物件,只要加上註解就可以獲得完全的事務支援。和程式設計式事務相比,宣告式事務唯一不足地方是,後者的最細粒度只能作用到方法級別,無法做到像程式設計式事務那樣可以作用到程式碼塊級別。

15、spring的事務傳播行為:
spring事務的傳播行為說的是當一個方法呼叫另一個方法時,事務該如何操作。
1PROPAGATION_REQUIRED:如果當前沒有事務,就建立一個新事務,如果當前存在事務,就加入該事務,該設定是最常用的設定。

(2)PROPAGATION_SUPPORTS:支援當前事務,如果當前存在事務,就加入該事務,如果當前不存在事務,就以非事務執行。‘

(3)PROPAGATION_MANDATORY:支援當前事務,如果當前存在事務,就加入該事務,如果當前不存在事務,就丟擲異常。

(4)PROPAGATION_REQUIRES_NEW:建立新事務,無論當前存不存在事務,都建立新事務。

(5)PROPAGATION_NOT_SUPPORTED:以非事務方式執行操作,如果當前存在事務,就把當前事務掛起。

(6)PROPAGATION_NEVER:以非事務方式執行,如果當前存在事務,則丟擲異常。

(7)PROPAGATION_NESTED:如果當前存在事務,則在巢狀事務內執行。如果當前沒有事務,則執行與PROPAGATION_REQUIRED類似的操作。

16、Spring事務的實現方式和實現原理:

(1)劃分處理單元——IOC:

由於spring解決的問題是對單個數據庫進行區域性事務處理的,具體的實現首相用spring中的IOC劃分了事務處理單元。並且將對事務的各種配置放到了ioc容器中(設定事務管理器,設定事務的傳播特性及隔離機制)。

(2)AOP攔截需要進行事務處理的類:

Spring事務處理模組是通過AOP功能來實現宣告式事務處理的,具體操作(比如事務實行的配置和讀取,事務物件的抽象),用TransactionProxyFactoryBean介面來使用AOP功能,生成proxy代理物件,通過TransactionInterceptor完成對代理方法的攔截,將事務處理的功能編織到攔截的方法中。 

讀取ioc容器事務配置屬性,轉化為spring事務處理需要的內部資料結構(TransactionAttributeSourceAdvisor),轉化為TransactionAttribute表示的資料物件。 

3)對事物處理實現(事務的生成、提交、回滾、掛起):

spring委託給具體的事務處理器實現。實現了一個抽象和適配。適配的具體事務處理器:DataSource資料來源支援、hibernate資料來源事務處理支援、JDO資料來源事務處理支援,JPA、JTA資料來源事務處理支援。這些支援都是通過設計PlatformTransactionManager、AbstractPlatforTransaction一系列事務處理的支援。為常用資料來源支援提供了一系列的TransactionManager。

4)結合:

PlatformTransactionManager實現了TransactionInterception介面,讓其與TransactionProxyFactoryBean結合起來,形成一個Spring宣告式事務處理的設計體系。

17.解釋一下Spring AOP裡面的幾個名詞:

(1)切面(Aspect):一個關注點的模組化,這個關注點可能會橫切多個物件。事務管理是J2EE應用中一個關於橫切關注點的很好的例子。 在Spring AOP中,切面可以使用通用類(基於模式的風格) 或者在普通類中以 @Aspect 註解(@AspectJ風格)來實現。

(2)連線點(Joinpoint):在程式執行過程中某個特定的點,比如某方法呼叫的時候或者處理異常的時候。 在Spring AOP中,一個連線點 總是 代表一個方法的執行。 通過宣告一個org.aspectj.lang.JoinPoint型別的引數可以使通知(Advice)的主體部分獲得連線點資訊。

(3)通知(Advice):在切面的某個特定的連線點(Joinpoint)上執行的動作。通知有各種型別,其中包括“around”、“before”和“after”等通知。 通知的型別將在後面部分進行討論。許多AOP框架,包括Spring,都是以攔截器做通知模型, 並維護一個以連線點為中心的攔截器鏈。

(4)切入點(Pointcut):匹配連線點(Joinpoint)的斷言。通知和一個切入點表示式關聯,並在滿足這個切入點的連線點上執行(例如,當執行某個特定名稱的方法時)。 切入點表示式如何和連線點匹配是AOP的核心:Spring預設使用AspectJ切入點語法。

(5)引入(Introduction):(也被稱為內部型別宣告(inter-type declaration))。宣告額外的方法或者某個型別的欄位。 Spring允許引入新的介面(以及一個對應的實現)到任何被代理的物件。例如,你可以使用一個引入來使bean實現 IsModified 介面,以便簡化快取機制。

(6)目標物件(Target Object): 被一個或者多個切面(aspect)所通知(advise)的物件。也有人把它叫做 被通知(advised) 物件。 既然Spring AOP是通過執行時代理實現的,這個物件永遠是一個 被代理(proxied) 物件。

(7)織入(Weaving):把切面(aspect)連線到其它的應用程式型別或者物件上,並建立一個被通知(advised)的物件。 這些可以在編譯時(例如使用AspectJ編譯器),類載入時和執行時完成。 Spring和其他純Java AOP框架一樣,在執行時完成織入。

切入點(pointcut)和連線點(join point)匹配的概念是AOP的關鍵,這使得AOP不同於其它僅僅提供攔截功能的舊技術。 切入點使得定位通知(advice)可獨立於OO層次。 例如,一個提供宣告式事務管理的around通知可以被應用到一組橫跨多個物件中的方法上(例如服務層的所有業務操作)。

18、通知有哪些型別?

(1)前置通知(Before advice):在某連線點(join point)之前執行的通知,但這個通知不能阻止連線點前的執行(除非它丟擲一個異常)。

(2)返回後通知(After returning advice):在某連線點(join point)正常完成後執行的通知:例如,一個方法沒有丟擲任何異常,正常返回。 
(3)丟擲異常後通知(After throwing advice):在方法丟擲異常退出時執行的通知。 
(4)後通知(After (finally) advice):當某連線點退出的時候執行的通知(不論是正常返回還是異常退出)。 
(5)環繞通知(Around Advice):包圍一個連線點(join point)的通知,如方法呼叫。這是最強大的一種通知型別。 環繞通知可以在方法呼叫前後完成自定義的行為。它也會選擇是否繼續執行連線點或直接返回它們自己的返回值或丟擲異常來結束執行。 
環繞通知是最常用的一種通知型別。大部分基於攔截的AOP框架,例如Nanning和JBoss4,都只提供環繞通知。