Spring常見面試題(2021最新版)

本文收錄于《面試小抄》系列,Github地址(可下載pdf):https://github.com/cosen1024/Java-Interview 國內(nèi)Gitee(可下載pdf):https://gitee.com/cosen1024/Java-Interview

1. 什么是依賴注入?可以通過多少種方式完成依賴注入?

在依賴注入中,您不必創(chuàng)建對象,但必須描述如何創(chuàng)建它們。您不是直接在代碼中將組件和服務(wù)連接在一起,而是描述配置文件中哪些組件需要哪些服務(wù)。由 IoC 容器將它們裝配在一起。

通常,依賴注入可以通過三種方式完成,即:

  • 構(gòu)造函數(shù)注入
  • setter 注入
  • 接口注入

在 Spring Framework 中,僅使用構(gòu)造函數(shù)和 setter 注入。

2. 區(qū)分 BeanFactory 和 ApplicationContext?

BeanFactory ApplicationContext
它使用懶加載 它使用即時加載
它使用語法顯式提供資源對象 它自己創(chuàng)建和管理資源對象
不支持國際化 支持國際化
不支持基于依賴的注解 支持基于依賴的注解

BeanFactory和ApplicationContext的優(yōu)缺點分析:

BeanFactory的優(yōu)缺點:

  • 優(yōu)點:應(yīng)用啟動的時候占用資源很少,對資源要求較高的應(yīng)用,比較有優(yōu)勢;
  • 缺點:運(yùn)行速度會相對來說慢一些。而且有可能會出現(xiàn)空指針異常的錯誤,而且通過Bean工廠創(chuàng)建的Bean生命周期會簡單一些。

ApplicationContext的優(yōu)缺點:

  • 優(yōu)點:所有的Bean在啟動的時候都進(jìn)行了加載,系統(tǒng)運(yùn)行的速度快;在系統(tǒng)啟動的時候,可以發(fā)現(xiàn)系統(tǒng)中的配置問題。
  • 缺點:把費時的操作放到系統(tǒng)啟動中完成,所有的對象都可以預(yù)加載,缺點就是內(nèi)存占用較大。

3. spring 提供了哪些配置方式?

  • 基于 xml 配置

bean 所需的依賴項和服務(wù)在 XML 格式的配置文件中指定。這些配置文件通常包含許多 bean 定義和特定于應(yīng)用程序的配置選項。它們通常以 bean 標(biāo)簽開頭。例如:

<bean id="studentbean" class="org.edureka.firstSpring.StudentBean">
 <property name="name" value="Edureka"></property>
</bean>
  • 基于注解配置

您可以通過在相關(guān)的類,方法或字段聲明上使用注解,將 bean 配置為組件類本身,而不是使用 XML 來描述 bean 裝配。默認(rèn)情況下,Spring 容器中未打開注解裝配。因此,您需要在使用它之前在 Spring 配置文件中啟用它。例如:

<beans>
<context:annotation-config/>
<!-- bean definitions go here -->
</beans>
  • 基于 Java API 配置

Spring 的 Java 配置是通過使用 @Bean 和 @Configuration 來實現(xiàn)。

  1. @Bean 注解扮演與 <bean /> 元素相同的角色。
  2. @Configuration 類允許通過簡單地調(diào)用同一個類中的其他 @Bean 方法來定義 bean 間依賴關(guān)系。

例如:

@Configuration
public class StudentConfig {
    @Bean
    public StudentBean myStudent() {
        return new StudentBean();
    }
}

4. Spring 中的 bean 的作用域有哪些?

  • singleton : 唯一 bean 實例,Spring 中的 bean 默認(rèn)都是單例的。
  • prototype : 每次請求都會創(chuàng)建一個新的 bean 實例。
  • request : 每一次HTTP請求都會產(chǎn)生一個新的bean,該bean僅在當(dāng)前HTTP request內(nèi)有效。
  • session : :在一個HTTP Session中,一個Bean定義對應(yīng)一個實例。該作用域僅在基于web的Spring ApplicationContext情形下有效。
  • global-session: 全局session作用域,僅僅在基于portlet的web應(yīng)用中才有意義,Spring5已經(jīng)沒有了。Portlet是能夠生成語義代碼(例如:HTML)片段的小型Java Web插件。它們基于portlet容器,可以像servlet一樣處理HTTP請求。但是,與 servlet 不同,每個 portlet 都有不同的會話

5. 如何理解IoC和DI?

IOC就是控制反轉(zhuǎn),通俗的說就是我們不用自己創(chuàng)建實例對象,這些都交給Spring的bean工廠幫我們創(chuàng)建管理。這也是Spring的核心思想,通過面向接口編程的方式來是實現(xiàn)對業(yè)務(wù)組件的動態(tài)依賴。這就意味著IOC是Spring針對解決程序耦合而存在的。在實際應(yīng)用中,Spring通過配置文件(xml或者properties)指定需要實例化的java類(類名的完整字符串),包括這些java類的一組初始化值,通過加載讀取配置文件,用Spring提供的方法(getBean())就可以獲取到我們想要的根據(jù)指定配置進(jìn)行初始化的實例對象。

  • 優(yōu)點:IOC或依賴注入減少了應(yīng)用程序的代碼量。它使得應(yīng)用程序的測試很簡單,因為在單元測試中不再需要單例或JNDI查找機(jī)制。簡單的實現(xiàn)以及較少的干擾機(jī)制使得松耦合得以實現(xiàn)。IOC容器支持勤性單例及延遲加載服務(wù)。

DI:DI—Dependency Injection,即“依賴注入”:組件之間依賴關(guān)系由容器在運(yùn)行期決定,形象的說,即由容器動態(tài)的將某個依賴關(guān)系注入到組件之中。依賴注入的目的并非為軟件系統(tǒng)帶來更多功能,而是為了提升組件重用的頻率,并為系統(tǒng)搭建一個靈活、可擴(kuò)展的平臺。通過依賴注入機(jī)制,我們只需要通過簡單的配置,而無需任何代碼就可指定目標(biāo)需要的資源,完成自身的業(yè)務(wù)邏輯,而不需要關(guān)心具體的資源來自何處,由誰實現(xiàn)。

6. 將一個類聲明為Spring的 bean 的注解有哪些?

我們一般使用 @Autowired 注解自動裝配 bean,要想把類標(biāo)識成可用于 @Autowired 注解自動裝配的 bean 的類,采用以下注解可實現(xiàn):

  • @Component :通用的注解,可標(biāo)注任意類為 Spring 組件。如果一個Bean不知道屬于哪個層,可以使用@Component 注解標(biāo)注。
    8 @Repository : 對應(yīng)持久層即 Dao 層,主要用于數(shù)據(jù)庫相關(guān)操作。
  • @Service : 對應(yīng)服務(wù)層,主要涉及一些復(fù)雜的邏輯,需要用到 Dao層。
  • @Controller : 對應(yīng) Spring MVC 控制層,主要用戶接受用戶請求并調(diào)用 Service 層返回數(shù)據(jù)給前端頁面。

7. Spring 中的 bean 生命周期?

Bean的生命周期是由容器來管理的。主要在創(chuàng)建和銷毀兩個時期。

創(chuàng)建過程:

1,實例化bean對象,以及設(shè)置bean屬性;
2,如果通過Aware接口聲明了依賴關(guān)系,則會注入Bean對容器基礎(chǔ)設(shè)施層面的依賴,Aware接口是為了感知到自身的一些屬性。容器管理的Bean一般不需要知道容器的狀態(tài)和直接使用容器。但是在某些情況下是需要在Bean中對IOC容器進(jìn)行操作的。這時候需要在bean中設(shè)置對容器的感知。SpringIOC容器也提供了該功能,它是通過特定的Aware接口來完成的。
比如BeanNameAware接口,可以知道自己在容器中的名字。
如果這個Bean已經(jīng)實現(xiàn)了BeanFactoryAware接口,可以用這個方式來獲取其它Bean。
(如果Bean實現(xiàn)了BeanNameAware接口,調(diào)用setBeanName()方法,傳入Bean的名字。
如果Bean實現(xiàn)了BeanClassLoaderAware接口,調(diào)用setBeanClassLoader()方法,傳入ClassLoader對象的實例。
如果Bean實現(xiàn)了BeanFactoryAware接口,調(diào)用setBeanFactory()方法,傳入BeanFactory對象的實例。)
3,緊接著會調(diào)用BeanPostProcess的前置初始化方法postProcessBeforeInitialization,主要作用是在Spring完成實例化之后,初始化之前,對Spring容器實例化的Bean添加自定義的處理邏輯。有點類似于AOP。
4,如果實現(xiàn)了BeanFactoryPostProcessor接口的afterPropertiesSet方法,做一些屬性被設(shè)定后的自定義的事情。
5,調(diào)用Bean自身定義的init方法,去做一些初始化相關(guān)的工作。
6,調(diào)用BeanPostProcess的后置初始化方法,postProcessAfterInitialization去做一些bean初始化之后的自定義工作。
7,完成以上創(chuàng)建之后就可以在應(yīng)用里使用這個Bean了。

銷毀過程:

當(dāng)Bean不再用到,便要銷毀
1,若實現(xiàn)了DisposableBean接口,則會調(diào)用destroy方法;
2,若配置了destry-method屬性,則會調(diào)用其配置的銷毀方法;

總結(jié)

主要把握創(chuàng)建過程和銷毀過程這兩個大的方面;
創(chuàng)建過程:首先實例化Bean,并設(shè)置Bean的屬性,根據(jù)其實現(xiàn)的Aware接口(主要是BeanFactoryAware接口,BeanFactoryAware,ApplicationContextAware)設(shè)置依賴信息,
接下來調(diào)用BeanPostProcess的postProcessBeforeInitialization方法,完成initial前的自定義邏輯;afterPropertiesSet方法做一些屬性被設(shè)定后的自定義的事情;調(diào)用Bean自身定義的init方法,去做一些初始化相關(guān)的工作;然后再調(diào)用postProcessAfterInitialization去做一些bean初始化之后的自定義工作。這四個方法的調(diào)用有點類似AOP。
此時,Bean初始化完成,可以使用這個Bean了。
銷毀過程:如果實現(xiàn)了DisposableBean的destroy方法,則調(diào)用它,如果實現(xiàn)了自定義的銷毀方法,則調(diào)用之。

8. 什么是 AOP?

AOP(Aspect-Oriented Programming), 即 面向切面編程, 它與 OOP( Object-Oriented Programming, 面向?qū)ο缶幊? 相輔相成, 提供了與 OOP 不同的抽象軟件結(jié)構(gòu)的視角.
在 OOP 中, 我們以類(class)作為我們的基本單元, 而 AOP 中的基本單元是 Aspect(切面)

9. AOP 有哪些實現(xiàn)方式?

實現(xiàn) AOP 的技術(shù),主要分為兩大類:

  • 靜態(tài)代理 - 指使用 AOP 框架提供的命令進(jìn)行編譯,從而在編譯階段就可生成 AOP 代理類,因此也稱為編譯時增強(qiáng);
    • 編譯時編織(特殊編譯器實現(xiàn))
    • 類加載時編織(特殊的類加載器實現(xiàn))。
  • 動態(tài)代理 - 在運(yùn)行時在內(nèi)存中“臨時”生成 AOP 動態(tài)代理類,因此也被稱為運(yùn)行時增強(qiáng)。
    • JDK 動態(tài)代理:通過反射來接收被代理的類,并且要求被代理的類必須實現(xiàn)一個接口 。JDK 動態(tài)代理的核心是 InvocationHandler 接口和 Proxy 類 。
    • CGLIB動態(tài)代理: 如果目標(biāo)類沒有實現(xiàn)接口,那么 Spring AOP 會選擇使用 CGLIB 來動態(tài)代理目標(biāo)類 。CGLIB ( Code Generation Library ),是一個代碼生成的類庫,可以在運(yùn)行時動態(tài)的生成某個類的子類,注意, CGLIB 是通過繼承的方式做的動態(tài)代理,因此如果某個類被標(biāo)記為 final ,那么它是無法使用 CGLIB 做動態(tài)代理的。

10. Spring AOP and AspectJ AOP 有什么區(qū)別?

Spring AOP 基于動態(tài)代理方式實現(xiàn);AspectJ 基于靜態(tài)代理方式實現(xiàn)。
Spring AOP 僅支持方法級別的 PointCut;提供了完全的 AOP 支持,它還支持屬性級別的 PointCut。

11. Spring中出現(xiàn)同名bean怎么辦?

  • 同一個配置文件內(nèi)同名的Bean,以最上面定義的為準(zhǔn)
  • 不同配置文件中存在同名Bean,后解析的配置文件會覆蓋先解析的配置文件
  • 同文件中ComponentScan和@Bean出現(xiàn)同名Bean。同文件下@Bean的會生效,@ComponentScan掃描進(jìn)來不會生效。通過@ComponentScan掃描進(jìn)來的優(yōu)先級是最低的,原因就是它掃描進(jìn)來的Bean定義是最先被注冊的~

12. Spring 怎么解決循環(huán)依賴問題?

spring對循環(huán)依賴的處理有三種情況:
①構(gòu)造器的循環(huán)依賴:這種依賴spring是處理不了的,直 接拋出BeanCurrentlylnCreationException異常。
②單例模式下的setter循環(huán)依賴:通過“三級緩存”處理循環(huán)依賴。
③非單例循環(huán)依賴:無法處理。

下面分析單例模式下的setter循環(huán)依賴如何解決

Spring的單例對象的初始化主要分為三步:


(1)createBeanInstance:實例化,其實也就是調(diào)用對象的構(gòu)造方法實例化對象

(2)populateBean:填充屬性,這一步主要是多bean的依賴屬性進(jìn)行填充

(3)initializeBean:調(diào)用spring xml中的init 方法。

從上面講述的單例bean初始化步驟我們可以知道,循環(huán)依賴主要發(fā)生在第一、第二部。也就是構(gòu)造器循環(huán)依賴和field循環(huán)依賴。

A的某個field或者setter依賴了B的實例對象,同時B的某個field或者setter依賴了A的實例對象”這種循環(huán)依賴的情況。A首先完成了初始化的第一步(createBeanINstance實例化),并且將自己提前曝光到singletonFactories中,此時進(jìn)行初始化的第二步,發(fā)現(xiàn)自己依賴對象B,此時就嘗試去get(B),發(fā)現(xiàn)B還沒有被create,所以走create流程,B在初始化第一步的時候發(fā)現(xiàn)自己依賴了對象A,于是嘗試get(A),嘗試一級緩存singletonObjects(肯定沒有,因為A還沒初始化完全),嘗試二級緩存earlySingletonObjects(也沒有),嘗試三級緩存singletonFactories,由于A通過ObjectFactory將自己提前曝光了,所以B能夠通過ObjectFactory.getObject拿到A對象(雖然A還沒有初始化完全,但是總比沒有好呀),B拿到A對象后順利完成了初始化階段1、2、3,完全初始化之后將自己放入到一級緩存singletonObjects中。此時返回A中,A此時能拿到B的對象順利完成自己的初始化階段2、3,最終A也完成了初始化,進(jìn)去了一級緩存singletonObjects中,而且更加幸運(yùn)的是,由于B拿到了A的對象引用,所以B現(xiàn)在hold住的A對象完成了初始化。

13. SpringMVC 工作原理了解嗎?

原理如下圖所示:

上圖的一個筆誤的小問題:Spring MVC 的入口函數(shù)也就是前端控制器 DispatcherServlet 的作用是接收請求,響應(yīng)結(jié)果。

流程說明(重要):

  1. 客戶端(瀏覽器)發(fā)送請求,直接請求到 DispatcherServlet。
  2. DispatcherServlet 根據(jù)請求信息調(diào)用 HandlerMapping,解析請求對應(yīng)的 Handler。
  3. 解析到對應(yīng)的 Handler(也就是我們平常說的 Controller 控制器)后,開始由 HandlerAdapter 適配器處理。
  4. HandlerAdapter 會根據(jù) Handler來調(diào)用真正的處理器開處理請求,并處理相應(yīng)的業(yè)務(wù)邏輯。
  5. 處理器處理完業(yè)務(wù)后,會返回一個 ModelAndView 對象,Model 是返回的數(shù)據(jù)對象,View 是個邏輯上的 View
  6. ViewResolver 會根據(jù)邏輯 View 查找實際的 View。
  7. DispaterServlet 把返回的 Model 傳給 View(視圖渲染)。
  8. View 返回給請求者(瀏覽器)

14. Spring 框架中用到了哪些設(shè)計模式?

工廠設(shè)計模式 : Spring使用工廠模式通過 BeanFactoryApplicationContext 創(chuàng)建 bean 對象。

代理設(shè)計模式 : Spring AOP 功能的實現(xiàn)。

單例設(shè)計模式 : Spring 中的 Bean 默認(rèn)都是單例的。

模板方法模式 : Spring 中 jdbcTemplate、hibernateTemplate 等以 Template 結(jié)尾的對數(shù)據(jù)庫操作的類,它們就使用到了模板模式。

包裝器設(shè)計模式 : 我們的項目需要連接多個數(shù)據(jù)庫,而且不同的客戶在每次訪問中根據(jù)需要會去訪問不同的數(shù)據(jù)庫。這種模式讓我們可以根據(jù)客戶的需求能夠動態(tài)切換不同的數(shù)據(jù)源。

觀察者模式: Spring 事件驅(qū)動模型就是觀察者模式很經(jīng)典的一個應(yīng)用。

適配器模式 :Spring AOP 的增強(qiáng)或通知(Advice)使用到了適配器模式、spring MVC 中也是用到了適配器模式適配Controller。

15. Spring 事務(wù)實現(xiàn)方式有哪些?

  • 編程式事務(wù)管理:這意味著你可以通過編程的方式管理事務(wù),這種方式帶來了很大的靈活性,但很難維護(hù)。
  • 聲明式事務(wù)管理:這種方式意味著你可以將事務(wù)管理和業(yè)務(wù)代碼分離。你只需要通過注解或者XML配置管理事務(wù)。

16. spring事務(wù)定義的傳播規(guī)則

  • PROPAGATION_REQUIRED: 支持當(dāng)前事務(wù),如果當(dāng)前沒有事務(wù),就新建一個事務(wù)。這是最常見的選擇。
  • PROPAGATION_SUPPORTS: 支持當(dāng)前事務(wù),如果當(dāng)前沒有事務(wù),就以非事務(wù)方式執(zhí)行。
  • PROPAGATION_MANDATORY: 支持當(dāng)前事務(wù),如果當(dāng)前沒有事務(wù),就拋出異常。
  • PROPAGATION_REQUIRES_NEW: 新建事務(wù),如果當(dāng)前存在事務(wù),把當(dāng)前事務(wù)掛起。
  • PROPAGATION_NOT_SUPPORTED: 以非事務(wù)方式執(zhí)行操作,如果當(dāng)前存在事務(wù),就把當(dāng)前事務(wù)掛起。
  • PROPAGATION_NEVER: 以非事務(wù)方式執(zhí)行,如果當(dāng)前存在事務(wù),則拋出異常。
  • PROPAGATION_NESTED:如果當(dāng)前存在事務(wù),則在嵌套事務(wù)內(nèi)執(zhí)行。如果當(dāng)前沒有事務(wù),則進(jìn)行與PROPAGATION_REQUIRED類似的操作。

17 .Spring 中的單例 bean 的線程安全問題?

當(dāng)多個用戶同時請求一個服務(wù)時,容器會給每一個請求分配一個線程,這時多個線程會并發(fā)執(zhí)行該請求對應(yīng)的業(yè)務(wù)邏輯(成員方法),此時就要注意了,如果該處理邏輯中有對單例狀態(tài)的修改(體現(xiàn)為該單例的成員屬性),則必須考慮線程同步問題。
線程安全問題都是由全局變量及靜態(tài)變量引起的。
若每個線程中對全局變量、靜態(tài)變量只有讀操作,而無寫操作,一般來說,這個全局變量是線程安全的;若有多個線程同時執(zhí)行寫操作,一般都需要考慮線程同步,否則就可能影響線程安全.

無狀態(tài)bean和有狀態(tài)bean

  • 有狀態(tài)就是有數(shù)據(jù)存儲功能。有狀態(tài)對象(Stateful Bean),就是有實例變量的對象,可以保存數(shù)據(jù),是非線程安全的。在不同方法調(diào)用間不保留任何狀態(tài)。
  • 無狀態(tài)就是一次操作,不能保存數(shù)據(jù)。無狀態(tài)對象(Stateless Bean),就是沒有實例變量的對象 .不能保存數(shù)據(jù),是不變類,是線程安全的。

在spring中無狀態(tài)的Bean適合用不變模式,就是單例模式,這樣可以共享實例提高性能。有狀態(tài)的Bean在多線程環(huán)境下不安全,適合用Prototype原型模式。
Spring使用ThreadLocal解決線程安全問題。如果你的Bean有多種狀態(tài)的話(比如 View Model 對象),就需要自行保證線程安全 。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容