Java面試題2.0--spring ioc

 
 

歡迎關注《Java面試題2.0》合集發佈頁,持續更新中!

 
 
 
什麼是IOC,什麼又是DI,他們有什麼區別?
 
一、IOC介紹
IOC是控制反轉。
創建對象實例的控制權從代碼控制剝離到IOC容器控制(之前的寫法,由程序代碼直接操控使用new關鍵字),實際就是你在xml文件控制,控制權的轉移是所謂反轉,側重於原理。
 
二、DI介紹
DI是依賴注入
創建對象實例時,爲這個對象注入屬性值或其它對象實例,側重於實現。
 
三、區別
1.它們是spring核心思想的不同方面的描述。
2.依賴注入和控制反轉是對同一件事情的不同描述,從某個方面講,就是它們描述的角度不同。
 
依賴注入是從應用程序的角度在描述,可以把依賴注入描述完整點:應用程序依賴容器創建並注入它所需要的外部資源;
而控制反轉是從容器的角度在描述,描述完整點:容器控制應用程序,由容器反向的嚮應用程序注入應用程序所需要的外部資源。
 
有哪些不同類型的IOC(依賴注入)方式?
 
構造器依賴注入:構造器依賴注入通過容器觸發一個類的構造器來實現的,該類有一系列參數,每個參數代表一個對其他類的依賴。
Setter方法注入:Setter方法注入是容器通過調用無參構造器或無參static工廠 方法實例化bean之後,調用該bean的setter方法,即實現了基於setter的依賴注入。
 
 
哪種依賴注入方式你建議使用,構造器注入,還是 Setter方法注入?
 
你兩種依賴方式都可以使用,構造器注入和Setter方法注入。最好的解決方案是用構造器參數實現強制依賴,setter方法實現可選依賴。
 
spring中的核心類有那些,各有什麼作用?
 
答:BeanFactory:產生一個新的實例,可以實現單例模式
BeanWrapper:提供統一的get及set方法
ApplicationContext:提供框架的實現,包括BeanFactory的所有功能
 
Spring中用到的設計模式,並舉具體示例
 
Spring框架中使用到了大量的設計模式,下面列舉了比較有代表性的:
 
代理模式—在AOP和remoting中被用的比較多。
單例模式—在spring配置文件中定義的bean默認爲單例模式。
工廠模式—BeanFactory用來創建對象的實例。
 
什麼是bean裝配?
 
裝配,或bean 裝配是指在Spring 容器中把bean組裝到一起,前提是容器需要知道bean的依賴關係,如何通過依賴注入來把它們裝配到一起。
 
什麼是bean的自動裝配?
 
Spring 容器能夠自動裝配相互合作的bean,這意味着容器不需要<constructor-arg>和<property>配置,能通過Bean工廠自動處理bean之間的協作。
 
解釋不同方式的自動裝配
 
有五種自動裝配的方式,可以用來指導Spring容器用自動裝配方式來進行依賴注入。
 
no:默認的方式是不進行自動裝配,通過顯式設置ref 屬性來進行裝配。
byName:通過參數名 自動裝配,Spring容器在配置文件中發現bean的autowire屬性被設置成byname,之後容器試圖匹配、裝配和該bean的屬性具有相同名字的bean。
byType::通過參數類型自動裝配,Spring容器在配置文件中發現bean的autowire屬性被設置成byType,之後容器試圖匹配、裝配和該bean的屬性具有相同類型的bean。如果有多個bean符合條件,則拋出錯誤。
constructor:這個方式類似於byType, 但是要提供給構造器參數,如果沒有確定的帶參數的構造器參數類型,將會拋出異常。
autodetect:首先嚐試使用constructor來自動裝配,如果無法工作,則使用byType方式。
 
自動裝配有哪些侷限性 ?
 
自動裝配的侷限性是:
 
重寫: 你仍需用 <constructor-arg>和 <property> 配置來定義依賴,意味着總要重寫自動裝配。
基本數據類型:你不能自動裝配簡單的屬性,如基本數據類型,String字符串,和類。
模糊特性:自動裝配不如顯式裝配精確,如果有可能,建議使用顯式裝配。
 
 
構造方法注入和設值注入有什麼區別?
 
請注意以下明顯的區別:
在設值注入方法支持大部分的依賴注入,如果我們僅需要注入int、string和long型的變量,我們不要用設值的方法注入。
對於基本類型,如果我們沒有注入的話,可以爲基本類型設置默認值。
在構造方法注入不支持大部分的依賴注入,因爲在調用構造方法中必須傳入正確的構造參數,否則的話爲報錯。
 
設值注入不會重寫構造方法的值。
在使用設值注入時有可能還不能保證某種依賴是否已經被注入,也就是說這時對象的依賴關係有可能是不完整的。而在另一種情況下,構造器注入則不允許生成依賴關係不完整的對象。
 
Spring Bean的作用域之間有什麼區別?
 
Spring容器中的bean可以分爲5個範圍。所有範圍的名稱都是自說明的,但是爲了避免混淆,還是讓我們來解釋一下:
 
singleton:這種bean範圍是默認的,這種範圍確保不管接受到多少個請求,每個容器中只有一個bean的實例,單例的模式由bean factory自身來維護。
prototype:原形範圍與單例範圍相反,爲每一個bean請求提供一個實例。
request:在請求bean範圍內會每一個來自客戶端的網絡請求創建一個實例,在請求完成以後,bean會失效並被垃圾回收器回收。
Session:與請求範圍類似,確保每個session中有一個bean的實例,在session過期後,bean會隨之失效。
global-session:global-session和Portlet應用相關。當你的應用部署在Portlet容器中工作時,它包含很多portlet。如果你想要聲明讓所有的portlet共用全局的存儲變量的話,那麼這全局變量需要存儲在global-session中。
 
全局作用域與Servlet中的session作用域效果相同。
 
請介紹一下Spring框架中Bean的生命週期
 
一、Bean的定義
Spring通常通過配置文件定義Bean。如:  
這個配置文件就定義了一個標識爲 HelloWorld 的Bean。在一個配置文檔中可以定義多個Bean。
 
二、Bean的初始化
有兩種方式初始化Bean。
1、在配置文檔中通過指定init-method 屬性來完成
2、實現 org.springframwork.beans.factory.InitializingBean接口  
 
那麼,當這個Bean的所有屬性被Spring的BeanFactory設置完後,會自動調用afterPropertiesSet()方法對Bean進行初始化,於是,配置文件就不用指定 init-method屬性了。
 
三、Bean的調用
有三種方式可以得到Bean並進行調用:
1、使用BeanWrapper
2、使用BeanFactory
3、使用ApplicationConttext
 
四、Bean的銷燬
1、使用配置文件中的 destory-method 屬性
2、實現 org.springframwork.bean.factory.DisposebleBean接口
 
Spring中IOC的作用與原理?對象創建的過程。
 
Spring 中的 IoC 的實現原理,就是工廠模式加反射機制。
可以把IOC容器看作是一個工廠,這個工廠裏要生產的對象都在配置文件中給出定義,然後利用編程語言的的反射編程,根據配置文件中給出的類名生成相應的對象。從實現來看,IOC是把以前在工廠方法裏寫死的對象生成代碼,改變爲由配置文件來定義,也就是把工廠和對象生成這兩者獨立分隔開來,目的就是提高靈活性和可維護性
 
 
 
ApplicationContext通常的實現是什麼?
 
ApplicationContext是Spring的上下文,定義了IOC容器初始化的過程,從類結構上可以看到,它也是一個BeanFactory,還是一個ResourceLoader資源加載器,同時它還是ApplicationContext,擴展了IOC容器初始化的過程,在其註冊Bean時還是使用上文提到的DefaultListableBeanFactory這個BeanFactory,而ApplicationContext存在的意義就是完善(增添)了IOC容器的功能,使獲取Bean更加靈活,功能更多樣。
 
可以看到最底層有四種不同的ApplicationContext,它們各自有各自不同的方法加載Bean,從類名上也都可以看出來,AnnotationConfigWebApplicationContext與XmlWebApplicationContext主要用於Web,一種是根據註解的方式加載Bean信息,一種是根據xml文件加載Bean信息。而FileSystemXmlApplicationContext與ClassPathXmlApplicationContext一種是從文件系統資源來加載Bean定義,一種是從路徑獲取加載Bean定義,其實這些ApplicationContext大同小異,他們都間接繼承了AbstractApplicationContext與其子類,這兩個類實現了大部分初始化IOC容器的方法,而那四種ApplicationContext的區別只在讀取Bean資源的方式上,與應用場景不同上,本質的IOC容器初始化原理都是相同的。
 
FileSystemXmlApplicationContext :此容器從一個XML文件中加載beans的定義,XML Bean 配置文件的全路徑名必須提供給它的構造函數。
 
ClassPathXmlApplicationContext:此容器也從一個XML文件中加載beans的定義,這裏,你需要正確設置classpath因爲這個容器將在classpath裏找bean配置。
 
WebXmlApplicationContext:此容器加載一個XML文件,此文件定義了一個WEB應用的所有bean。
 
Bean 工廠和 Application contexts 有什麼區別?
 
beanFactory是最底層的容器,提供了實例化bean以及獲取bean的方法。
ApplicationContext 繼承beanFactory接口,提供了更多的功能,比如國際化、訪問資源、aop等。
 
兩者子裝載bean的方式上有所差異:beanFactory在初始化的時候不會創建bean,有方法來獲取的時候纔會去創建。
ApplicationContext一開始就會創建好所有的bean。
 
factoryBean與beanFactory的區別
 
BeanFactory是個Factory,也就是IOC容器或對象工廠,FactoryBean是個Bean。在Spring中,所有的Bean都是由BeanFactory(也就是IOC容器)來進行管理的。但對FactoryBean而言,這個Bean不是簡單的Bean,而是一個能生產或者修飾對象生成的工廠Bean,它的實現與設計模式中的工廠模式和修飾器模式類似。
 
BeanFactory
 
Spring 對Bean的操作是典型的工廠模式,上面定義了許多不同的BeanFactory,都是具有它各種各樣不同的功能的,這些BeanFactory們爲操作Bean帶來了極大的便利。最底層的DefaultListableBeanFactory實現了以上所有接口,我們使用的默認的BeanFactory即爲此類.
 
包含方法:
getBean:獲取Bean實例的方法。
containsBean:驗證IOC容器是否有某個bean的方法。
getType:獲取bean的class類型的方法。
getAliases:獲取bean的別名的方法。
 
FactoryBean
 
它是一個特殊的Bean,從名字就可以看出來,它是由Bean結尾的,意味着是一個Bean,但此Bean是工廠類型的Bean,什麼意思呢?普通Bean只是IOC容器管理的真正代碼中使用的對象,但工廠Bean不是我們要使用的對象,它負責生產真正的Bean,它是一個工廠,但它也是由工廠(BeanFactory)生產出來的,也就是說,FactroyBean是用來生產Bean而生的。
 
FactoryBean是Spring定義的一個特殊的Bean,包含的方法:
getObeject:定義了一個獲取Bean對象的方法。
getObjectType:定義了一個獲取Bean的Class對象的方法。
isSingleton:判斷此Factory生成的Bean是否爲單例Bean,默認爲true。
 
Factory是一個特殊的Bean ,主要用途是用來生產Bean的,請注意IOC容器中singletonObjects這個Map將會存放所有初始化好的單例Bean(包括那些單例的FactoryBean),而factoryBeanObjectCache存放的則是那些FactoryBean生產出來的Bean,也就是說,IOC容器不僅會存放FactoryBean在容器中,還會存放FactoryBean生產的Bean在容器中,雙重緩存加快獲取Bean的效率。這裏讀者要理清兩者的關係。
 
bean的創建過程
 
IOC容器對資源的定位(根據路徑定位資源)、加載(根據一定規則加載資源成爲一個可解析的對象)、註冊(將封裝好了的Bean信息對象存放進BeanFactory中的Map裏,並且使用List存放BeanName)。然而,這個過程只是一個開始,此時的Bean還未初始化,BeanFactory中只是存放了Bean的描述定義對象而已,真正初始化是在getBean()方法被調用。
 
BeanFactory會遍歷所有的Bean,如果是單例且lazy-init=false纔會進行初始化,而真正初始化的工作將從getBean方法 中展開
 
getBean向IOC容器獲取Bean,判斷此Bean是否可以實例化,然後創建Bean實例,並且封裝成BeanWrapper返回
 
bean的生命週期,以及初始化前後進行處理
 
bean的生命週期分爲三個階段:bean創建—初始化----銷燬的過程
 
在bean創建之後,我們可以在bean的初始化和銷燬的前後對bean做一些處理,加入我們自己的邏輯,以下四種方式可以讓我們在bean初始化和銷燬的時候執行邏輯:
 
1)、指定初始化和銷燬方法;通過@Bean指定init-method和destroy-method;
2)、通過讓Bean實現InitializingBean(定義初始化邏輯),DisposableBean(定義銷燬邏輯);
3)、可以使用JSR250;
 
@PostConstruct:在bean創建完成並且屬性賦值完成;來執行初始化方法
@PreDestroy:在容器銷燬bean之前通知我們進行清理工作
 
4)、BeanPostProcessor【interface】:bean的後置處理器;
 
在bean初始化前後進行一些處理工作;
postProcessBeforeInitialization:在初始化之前工作
postProcessAfterInitialization:在初始化之後工作
 
調用順序
BeanPostProcessor.postProcessBeforeInitialization
 
初始化:
對象創建完成,並賦值好,調用初始化方法。。。
 
BeanPostProcessor.postProcessAfterInitialization
 
銷燬:
 
單實例:容器關閉的時候
多實例:容器不會管理這個bean;容器不會調用銷燬方法;
 
 
bean實例化過程中,對信息進行處理:
 
如果你希望修改Bean的配置信息, 並且在Bean初始化的時候可以被使用到: 可以實現BeanFactoryPostProcessor接口,在相應回調方法中實現相應的邏輯。具體應用:配置Bean時佔位符的替換,亦或是某些Bean的屬性轉換(電話號碼中間幾位的屏蔽),總而言之如果希望修改Bean配置信息,按照你希望的配置來進行初始化,就實現此接口。
 
 
如果你希望在Bean初始化完成之後對Bean有什麼操作: 可以實現BeanPostProcessor接口。具體應用:AOP,AOP的原理其實是動態代理,實現AOP的核心類就實現了BeanPostProcessor接口,並且保證在Bean初始化(實例化與依賴注入)之後進行後處理,判斷Bean是否應該被AOP,如果需要,則動態代理此Bean,返回代理後的proxy對象,IOC容器將使用proxy對象作爲此Bean,完成AOP的操作。
 
 
如果你希望某個Bean按照你的方式進行初始化: 而不是IOC那些實例化、依賴注入、各種回調方法,那麼你可以實現InstantiationAwareBeanPostProcessor,實現前處理方法來自定義Bean的初始化過程,返回實例即爲此Bean,如果返回null的話IOC容器將繼續初始化該Bean,所以可以判斷某個Bean是自定義的初始化,剩下的Bean依然可以正常初始化。
 
 
如果你希望修改某個Bean的依賴注入屬性,亦或是控制某個Bean不要進行依賴注入: 你可以實現InstantiationAwareBeanPostProcessor這個接口的依賴注入回調方法或是後處理方法。前者可以返回一個你修改後的屬性封裝對象,在接下來的處理中會針對你修改後的屬性封裝對象進行依賴注入,如果返回null將不會進行依賴注入。而後者只控制是否進行依賴注入,兩者的區別與時序希望讀者區分清楚,它們的共同點在都可以控制是否進行依賴注入,但側重點不同。
 
 
如果你希望獲取容器的某個資源:例如ClassLoader、BeanFactory、ApplicationContext、ResourceLoader等等一系列資源,你可以實現相對應的Aware接口,並且將類交給IOC容器管理,此類作爲Bean將在你實現的Aware接口的對應方法中注入你所想要的屬性。亦或是使用依賴注入的方式,也可以得到你想要的資源,原理在上面的3.3節其他的Aware接口中有提到,註冊了對應的依賴:
 
 
如果你希望在Bean初始化完,執行一個類似初始化的方法:你可以實現InitializingBean接口,在對應方法中實現你想初始化的邏輯,時機是在Bean實例化、依賴注入、BeanPostProcessor的前處理之後,後處理之前將會執行的。
如果你希望註冊監聽器:實現ApplicationListener接口,並將類交給IOC容器管理,就可以自動註冊監聽器了,然後在對應方法中寫具體通知方法之後需要做的事件邏輯。註冊完監聽器,就少不了發佈事件,你可以在任意地方獲取到上下文對象(方式有兩種,上面都有提到),然後調用上下文對象的publishEvent方法即可發佈事件,你所配置的監聽器都會收到消息並執行你自定義的邏輯。
 
 
如果你希望伴隨IOC容器有一個生命週期的動作:你可以讓類實現Lifecycle接口,實現對應start/stop方法,其將會在容器初始化完成後執行start,在start中開始此Bean的生命週期的邏輯,達到一個生命週期的效果。
 
 
講到這裏,IOC容器的介紹也就告一段落了,我希望讀者看到這裏,能意識到IOC容器並不是解耦那麼簡單,因爲有了IOC容器,纔有AOP,纔有各種各樣靈活的處理,如果沒有IOC容器去管理那些對象,我們需要做的事情可多太多太多了,IOC容器管理對象,極大簡化了我們的開發過程
 
 
 
 
 
 
 
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章