Spring 框架面試題總結(待續更新中...)
一. 什麼是Spring?
答:Spring的核心是一個輕量級的容器(Container),它是實現IoC(Inversion of Control)容器和非侵入性(No intrusive)的框架,並提供AOP(Aspect-oriented Programming)的實現方式,提供對持久層(Persistence)、事務(Transcation)的支援;提供MVC Web框架的實現,並對一些常用的企業服務API提供了一致的模型封裝,是一個全方位的應用程式框架,除此之外,對現存的各種框架(Structs、JSF、Hibernate、Ibatis、Webwork等),Spring也提供了與他們相整合的方案。
細化:
1、Spring的核心是一個輕量級(Lightweight)的容器(Container)。
2、Spring是實現IoC(Inversion of Control)容器和非入侵性(No intrusive)的框架。
3、Spring提供AOP(Aspect-oriented programming)的實現方式,把應用業務邏輯和系統服務分開。
4、Spring提供對持久層(Persistence)、事物(Transcation)的支援。
5、Spring供MVC Web框架的實現,並對一些常用的企業服務API(Application Interface)提供一致的模型封裝。
6、Spring提供了對現存的各種框架(Structs、JSF、Hibernate、Ibatis、Webwork等)相整合的方案。
總之,Spring是一個全方位的應用程式框架。
二、Spring的核心
1、IoC/DI(控制反轉/依賴注入):
1)控制反轉可以分開來解讀,控制和反轉-> 首先控制什麼?對什麼進行控制? 其實此處的控制指的是程式中的一些物件或者變數的控制權,在傳統的程式中都是由應用程式自己控制物件建立或者變數賦值,這是一種主動式的控制,導致元件之間的完全耦合;現在將一些物件或者變數的建立控制權交給一個叫做Ioc容器的東西,由這個容器來控制應用程式中所需要的資源,這樣就變成了被動的控制,對元件之間的關係進行解耦,所以所謂的反轉就是將控制權由應用程式轉交到Ioc容器。
2)依賴注入:同樣將DI分開來解讀,依賴和注入-> 首先依賴什麼?誰依賴誰? 其實應用程式中所需要的資源建立和獲取都是要依賴於Ioc容器,需要IoC容器裝配類之間的關係,即應用程式依賴於IoC容器的裝配功能;相反,注入就是IoC容器嚮應用程式中進行注入應用程式所需要的資源,由應用程式主動裝配物件的依賴變應用程式被動接受依賴,所以IoC容器也叫DI容器。
綜上所述,控制權的主動與被動式其實都是相對於(參考物)IoC容器來說的,也可以將IoC模式看做是工廠模式的昇華,不同的是Spring中的控制反轉所用到的是Java的“反射”技術,通過在XML檔案中定義應用程式所需要的類,再由IoC容器根據xml檔案建立這些物件。所以這也形成了工廠與物件生成這兩者獨立分隔開,目的就是提高靈活性和可維護性。之所以將物件生成放到xml檔案中,如果我們想要換一個實現的類將變得很簡單,只需要修改xml檔案即可。
2、AOP(面向切面程式設計):將具體的通用的應用從業務邏輯中分離出來,各自做各自專業的事情。
1)切面:簡單的理解就是一個類,每個或者物件
2)連線點:類中的方法(可以切入的地方)
3)通知:切面在某個連線點執行的操作(分為: Before advice , After returning advice , After throwing advice , After (finally) advice , Around advice );
4)切入點:符合切點表示式的連線點,也就是真正被切入的地方
5)引入:
6)目標物件:
三、Spring能做什麼?
四、Spring注入Bean的方式
(1)設值注入(set方法)
(2)構造器注入
(3)工廠方法
Spring載入Bean的流程:
(1)建立一個上下文context = createApplicationContext;
(2)context中都會有一個BeanFactory(預設是DefaultListableBeanFactory),在該類的子類類xmlBeanFactory中進行xml檔案的解析;
(3)在類XmlBeanDefinitionParser 中用Dom解析xml檔案(DefaultXmlBeanDefinitionParser),解析xml檔案中所有bean,並將bean放到BeanDefinitionHolder中,封裝成BeanDefinition;
(4)再進行bean的註冊,具體在BeanDefinitionReaderUtils類呼叫DefaultListableBeanFactory類的registerBeanDefinition進行bean的註冊,在這裡用了一HashMap存放bean,其中用Beanname作為鍵值,其封裝好的beanDefinition作為值。還有用一個List存放所有的bean的名字。
設值注入和構造器注入的區別
1) 在設定注入需要該Bean包含這些屬性的setter方法;
2) 與傳統的JavaBean的寫法更相似,程式開發人員更容易理解、接收。通過setter方法設定依賴關係顯得更加只管;
3) 對於複雜的依賴關係,如果採用構造注入,會導致構造器引數列表複雜,難以閱讀。Spring在建立Bean例項時,需要同時例項化其它依賴的全部例項,因而導致效能下降。而使用設值注入,則能避免這些問題;
4) 尤其是在某些屬性可選的情況況下,多引數的構造器顯得更加複雜;
但是在其他的場景,構造器注入顯得更好的效能:
1) 構造注入需要該Bean包含帶有這些屬性的構造器;
2) 構造注入可以在構造器中決定依賴關係的注入順序,優先依賴的優先注入;
3) 對於依賴關係無需變化的Bean,構造注入更有用處。因為沒有Setter方法,所有的依賴關係全部在構造器內設定。因此,無需擔心後續的程式碼對依賴關係產生破壞
4) 依賴關係只能在構造器中設定,則只有元件的建立者才能改變元件的依賴關係。對元件的呼叫者而言,元件內部的依賴關係完全透明,更符合高內聚的原則.
(1)在使用設值注入時有可能還不能保證某種依賴是否已經被注入,也就是說這時物件的依賴關係有可能是不完整的。而在另一種情況下,構造器注入則不允許生成依賴關係不完整的物件。
(2)在設值注入時如果物件A和物件B互相依賴,在建立物件A時Spring會丟擲sObjectCurrentlyInCreationException異常,因為在B物件被建立之前A物件是不能被建立的,反之亦然。所以Spring用設值注入的方法解決了迴圈依賴的問題,因物件的設值方法是在物件被建立之前被呼叫的。
注:採用以設值注入為主,構造注入為輔的注入策略。對於依賴關係無需變化的注入,儘量採用構造注入;而其他的依賴關係的注入,則考慮採用設值注入。
五、Bean的生命週期(以BeanFactory)
1) Bean的建立,由BeanFactory讀取Bean定義檔案,並建立Bean例項;
2) 執行Bean的屬性注入,Setter注入;
3) 如果Bean類實現了org.springframework.beans.factory.BeanNameAware介面,則執行其setBeanName方法;
4) 如果Bean類實現了org.springframework.beans.factory.BeanFactoryAware介面,則執行其setBeanFactory方法;
5) 如果容器中有實現org.springframework.beans.factory.BeanPostProcessors介面的例項,則任何Bean在初始化之前都會執行這個例項的processBeforeInitialization()方法;
6) 如果Bean類實現了org.springframework.beans.factory.InitializingBean介面,則執行其afterPropertiesSet()方法;
7) 呼叫Bean的初始化方法”init-method” (!!注意,init-method方法沒有引數);
8) 如果容器中有實現org.springframework.beans.factory.BeanPostProcessors介面的例項,則任何Bean在初始化之後都會執行這個例項的processAfterInitialization()方法;
9) 使用Bean做一些業務邏輯….
10) 使用完,容器關閉,如果Bean類實現了org.springframework.beans.factory.DisposableBean介面,則執行它的destroy()方法;
11) 在容器關閉時,可以在Bean定義檔案中使用“destory-method”定義的方法,銷燬Bean (!!注意,destory-method方法沒有引數);
ApplicationContext中bean的生命週期也是類似的。
六、BeanFactory 和ApplicationContext的區別
- 首先BeanFactory和ApplicationContext都是介面,並且ApplicationContext是BeanFactory的子介面。
- 其次BeanFactory是Spring中最底層的介面,提供了最簡單的容器的功能,只提供了例項化物件和拿物件的功能,
ApplicationContext(應用上下文)它是Spring的一個更高階的容器,提供了更多的有用的功能;
1)ApplicationContext繼承了BeanFactory介面,所以,ApplicationContext也能像BeanFactory從容器中得到Bean(繼承至 ListableBeanFactory);
2)ApplicationContext提供的額外的功能:
國際化的功能,訊息傳送、響應機制(繼承至MessageSource );統一載入資源的功能(繼承至ResourceLoader);強大的事件機制( 繼承至ApplicationEventPublisher);對Web應用的支援() - 它們的載入方式不同:
1) BeanFactory採用的是延遲載入的形式來注入Bean,即只有在使用某個bean的時候,才對該Bean進行載入例項化.好處是節約記憶體,缺點是速度比較慢.
2) ApplicationContext則相反的,它是在Ioc容易啟動時就一次性建立所有的Bean,這樣的好處是可以馬上發現Spring配置檔案中的錯誤。壞處就是浪費記憶體。
ApplicationContext的三種較常見的實現方式:
1) ClassPathXmlApplicationContext:從classpath的xml配置檔案中讀取上下文,並生成上下文定義,應用程式上下文從程式環境中取得。
ApplicationContext context = new ClassPathXmlApplicationContext("****.xml");
2) FileSystemXmlApplicationContext:由檔案系統中的xml配置檔案讀取上下文。
ApplicationContext context = new FileSystemXmlApplicationContext("****.xml");
3) XmlWebApplicationContext:由Web應用的Xml檔案讀取上下文。
七、Spring Bean的作用域
1) Singleton: 這是預設的作用域,這種範圍確保不管接受多少個請求,每個容器中只有一個bean的例項,單例模式有BeanFactory自身維護;
2) Prototype: 原形範圍與單例範圍相反,為每一個bean請求提供一個例項;
3) Request: 在請求bean範圍內會為每一個來自客戶端的網路請求建立一個例項,在請求完成以後,bean會失效並被垃圾回收器回收;
4) Session: 與請求範圍類似,確保每個session中有一個bean的例項,在session過期後,bean會隨之失效;
5) global-session: global-session和Portlet應用相關。當你的應用部署在Portlet容器中工作時,它包含很多portlet。如果你想要宣告讓所有的portlet共用全域性的儲存變數的話,那麼這全域性變數需要儲存在global-session中。
八、Bean的自動裝配模式
- no: spring預設的設定,在該設定下自動裝配是關閉的,開發者需要在配置檔案中用標籤明確依賴關係;
- byName: 該選項可以根據bean名稱設定依賴關係。當向一個bean中自動裝配一個屬性時,容器將根據bean的名稱自動在在配置檔案中查詢一個匹配的bean。如果找到的話,就裝配這個屬性,如果沒找到的話就報錯。
- byType: 該選項可以根據bean型別設定依賴關係。當向一個bean中自動裝配一個屬性時,容器將根據bean的型別自動在在配置檔案中查詢一個匹配的bean。如果找到的話,就裝配這個屬性,如果沒找到的話就報錯。
- constructor: 構造器自動裝載,僅僅適用於與有構造器相同引數的bean,如果在容器中沒有找到與構造器引數型別一致的bean,那麼將會丟擲異常。
- autodetect: 該模式自動探測使用構造器自動裝配或者byType自動裝配。先嚐試找合適的帶引數的建構函式,若沒有則自動選擇byType的自動裝配模式。
基於註解的自動裝配,spring預設是關閉註解模式的,所以需要在配置檔案中設定
九、各種註解的解釋
@Required: 驗證bean是否被正確的設定了,需要在Ioc容器中註冊RequiredAnnotationBeanPostProcessor,它是Spring中的後置處理用來驗證被@Required 註解的bean屬性是否被正確的設定了,若沒有屬性被@Required註解過的話,後置處理器會丟擲一個BeanInitializationException異常。
@AutoWired: 該註解用於在bean的設值方法上自動裝配bean的屬性,一個引數或者帶有任意名稱或帶有多個引數的方法。(嘗試用byType 自動裝配)
- @Qualifer: 該註解一般和@AutoWired一起使用,用於幫助Ioc容器能知道當前註解的屬性應該自動裝配哪一個。(對於在xml檔案中定義了多個同類的bean,但是id不同,若單單用@AutoWired進行註解,Ioc容器無法分辨出應該自動裝配哪一個bean)
十、Spring 中的所有哪些不同型別的事件?
Spring的ApplicationContext提供了支援事務和程式碼中新增監聽器的功能。
可以建立一個bean其實現ApplicationListener介面(型別是ApplicationEvent),當一個ApplicationEvent 被髮布後,bean就自動被通知,並且在方法onApplicationEvent(…)方法中處理該事件。
public class AllApplicationEventListener implements ApplicationListener < ApplicationEvent >
{
@Override
public void onApplicationEvent(ApplicationEvent applicationEvent)
{
//process event
}
}
在Spring中有5中標準的事件:
1. 上下文更新事件(ContextRefreshedEvent):該事件會在ApplicationContext被初始化或者更新時釋出,也可以在呼叫ConfigurableApplicationContext介面中的refresh()方法時被觸發。
2. 上下文開始事件(ContextStartedEvent): 當容器呼叫ConfigurableApplicationContext的Start()方法開始/重新開始容器時被觸發。
3. 上下文停止事件(ContextStoppedEvent): 當容器呼叫ConfigurableApplicationContext的Stop()方法停止容器時觸發該事件。
4. 上下文關閉事件(ContextClosedEvent): 當ApplicationContext被關閉時觸發該事件。容器被關閉時,其管理的所有單例Bean都被銷燬。
5. 請求處理事件(RequestHandledEvent): 在Web應用中,當一個http請求(request)結束觸發該事件。
spring也讓使用者可以自定義事件型別,繼承ApplicationEvent。
public class CustomApplicationEvent extends ApplicationEvent
{
public CustomApplicationEvent ( Object source, final String msg )
{
super(source);
System.out.println("Created a Custom event");
}
}
為了監聽這個事件,還需要建立一個監聽器:
public class CustomEventListener implements ApplicationListener < CustomApplicationEvent >
{
@Override
public void onApplicationEvent(CustomApplicationEvent applicationEvent) {
//handle event
}
}
之後通過applicationContext介面的publishEvent()方法來發布自定義事件。
CustomApplicationEvent customEvent = new CustomApplicationEvent(applicationContext, "Test message");
applicationContext.publishEvent(customEvent);
十一、Spring 框架中都用到了哪些設計模式?
- 代理模式—在AOP和remoting中被用的比較多。
- 單例模式—在spring配置檔案中定義的bean預設為單例模式。
- 模板方法—用來解決程式碼重複的問題。比如. RestTemplate, JmsTemplate, JpaTemplate。
- 前端控制器—Spring提供了DispatcherServlet來對請求進行分發。
- 檢視幫助(View Helper )—Spring提供了一系列的JSP標籤,高效巨集來輔助將分散的程式碼整合在視圖裡。
- 依賴注入—貫穿於BeanFactory / ApplicationContext介面的核心理念。
- 工廠模式—BeanFactory用來建立物件的例項。