Spring 框架重點解析
1. Spring 框架中的單例 bean 是線程安全的嗎?
不是線程安全的
- Spring 框架中有一個 @Scope 注解,默認的值是 singleton,即單例的;
- 因為一般在 Spring 的 bean 對象都是無狀態的(在生命周期中不被修改的,比如正經開發下, service 層的和 dao 層的),沒有線程安全問題;
- 如果在 bean 中定義了可修改的成員變量,是要考慮線程安全問題的,可以使用多例 prototype 或者加鎖來解決;
2. 什么是 AOP,你們項目中有沒有使用到 AOP 呢?
什么是 AOP ?
- 面向切面編程,用戶將與業務無直接相關,但卻對多個對象產生影響的公共行為和邏輯,將這些抽取成公共模塊進行復用,降低耦合度;
- 可以理解為 一組業務的效果上的"增強buff" ;
原理就是動態代理
- 即目標方法的執行時機和結果可以由代理決定和處理;
你們項目有沒有使用到 AOP ?
- 如 統一處理(攔截器、異常處理器、響應處理器),記錄接口訪問日志,緩存處理,Spring 實現的事務;
- 記錄接口訪問日志核心就是,使用 Spring AOP 中的環繞通知 + 切點表達式(找到記錄日志的方法),通過環繞通知的參數獲取請求方法的參數,獲得這些參數后,進行一些業務;
joinPoint.proceed();
代表,切入點執行,也就是目標方法的執行;
3. Spring 中的事務是如何實現的?
對于編程式事務,事務的維護還是有目標方法的業務進行,沒用到 AOP,對業務代碼入侵性大,項目中很少使用;
而對于聲明式事務,通過對方法加注解 @Transactional 聲明該方法是一個事務
- 其本質是通過 AOP 功能,對方法前后進行攔截,在執行方法之前開啟事務,在執行完目標方法之后進行提交或者捕獲到異常進行回滾事務;
4. Spring 中事務失效的場景有哪些?
- 異常捕獲處理,自己業務中處理了異常,沒有拋出,這就意味著這個業務被 Spring 認為是“沒有問題,無需回滾”
- 解決:根據實際業務決定是否要手動拋出、手動回滾;
- 拋出檢查異常(throws),Spring 默認認為事務拋出的檢查異常要被調用者處理,故沒有回滾;
- 解決:設置注解 @Transactional 的 rollbackFor 屬性為 Exception.class,則即使是檢查異常,也會回滾;
- 非 public 方法導致的事務失效,由于 Spring 聲明式事務的原理是 Spring AOP,所以創建代理、獲取參數、添加事務通知,都要求方法是 public;
- 解決:改為 public;
5. Spring 中 Bean 的生命周期(如何去管理和創建 Bean 實例的?)
推薦文章:【JavaEE】深入了解Spring中Bean的可見范圍(作用域)以及前世今生(生命周期)_bean的全局變量-CSDN博客
-
通過 BeanDefinition 獲取 Bean 的定義信息,調用構造方法創建 Bean(空);
-
Bean 對象中成員變量的依賴注入;
-
處理 Aware 接口(Bean Name Aware、Bean Factory Aware、ApplicationContextAware);
-
Bean 處理器 BeanPostProcessor - 前置;
-
初始化方法(InitializingBean、init-method);
-
Bean 處理器 BeanPostProcessor - 后置;
- 可以去加強 Bean 對象,例如使其成為代理對象,可以被動態代理;
-
銷毀 Bean;
6. Spring 中的循環引用(循環依賴)
6.1 循環依賴是什么?
6.2 Spring 的三種緩存
6.3 一級緩存 + 二級緩存解決普通 Bean 對象的循環依賴
顯然,循環依賴的主要原因就是兩個對象都是半成品,所以單憑一級緩存是無法解決循環依賴問題的;
6.4 三種緩存一起解決加強 Bean 對象的循環依賴
由于原始對象是沒有被特殊處理的,所以只憑借一二級緩存無法注入一些加強對象,如代理對象;
- 因為在 6.3 中,將半成品注入給 B,是因為地址后面不會變化,這樣即使注入了半成品,也不會影響結果;
- 而代理對象地址和性質跟原對象是不同的;
這個時候可以加上三級緩存來解決問題;
6.5 構造方法出現了循環依賴
由于 Bean 的生命周期中構造函數是第一個執行的,Spring 框架并不能解決構造器函數的依賴注入,而我們可以通過“延遲加載”來解決問題;
6.6 回答
- 循環依賴:循環依賴也就是循環引用,也就是一個或一個以上的 Bean 互相注入的現象,形成了閉環。如 A 依賴 B,B 依賴 A;
- 循環依賴在 Spring 中是允許存在的,Spring 框架依據三級緩存已經解決了大部分的循環依賴;
- 一級緩存:單例池,存放完整的 Bean 對象;
- 二級緩存:緩存 Bean 半成品對象;
- 三級緩存:緩存的是 ObjectFactory,表示對象工廠,用來創建某個 Bean(間接存儲 Bean 對象);
- 當然,Spring 無法解決構造方法出現了循環依賴的問題,但是我們可以通過 @Lazy 注解讓某個 Bean “延遲加載”來解決問題;
7. Spring MVC 的執行流程知道嗎?
7.1 JSP
7.2 前后端分離
AOP 也大概在 3、4、5 起代理作用,感興趣可以去研究,不作為重點;
7.3 回答
Spring MVC 的執行流程是這個框架最核心的內容了,有兩種:
- 比較老舊的 JSP;
- 比較主流的前后端分離的異步、接口開發;
對于 JSP :
- 用戶發送請求到 前端控制器 DispatcherServlet;
- 前端控制器 DispatcherServlet 收到請求調用 處理器映射器 HandlerMapping;
- 處理器映射器 HandlerMapping 將 url 映射到對應的處理器,生成 處理器執行鏈 HandlerExecutionChain 返回給 前端控制器 DispatcherServlet;
- 前端控制器 DispatcherServlet 調用 處理器適配器 HandlerAdapter;
- 處理器適配器 HandlerAdapter 經過適配調用具體的處理器(Handler/Controller),進行目標方法的參數處理以及返回值處理,并將結果轉化為邏輯視圖數據 ModelAndView 返回給 前端控制器 DispatcherServlet;
- 前端控制器 DispatcherServlet 將 ModelAndView 傳給 視圖解析器 ViewReslover;
- 視圖解析器 ViewReslover 將 ModelAndView 解析成真實的視圖數據 View 返回給 前端控制器 DispatcherServlet;
- 前端控制器 DispatcherServlet 根據 View 進行渲染視圖;
- 前端控制器 DispatcherServlet 將頁面響應給用戶;
對于前后端分離:
- 用戶發送請求到 前端控制器 DispatcherServlet;
- 前端控制器 DispatcherServlet 收到請求調用 處理器映射器 HandlerMapping;
- 處理器映射器 HandlerMapping 將 url 映射到對應的處理器,生成 處理器執行鏈 HandlerExecutionChain 返回給 前端控制器 DispatcherServlet;
- 前端控制器 DispatcherServlet 調用 處理器適配器 HandlerAdapter;
- 處理器適配器 HandlerAdapter 經過適配調用具體的處理器(Handler/Controller),進行目標方法的參數處理以及返回值處理,將數據直接響應給用戶;
- 方法上如果添加了 @ResponseBody,處理器適配器 HandlerAdapter 將目標方法的返回值轉化為 JSON 字符串再響應給用戶;
8. Spring Boot 自動配置原理
我們知道,Spring 中有不少的 Bean 是自帶的和程序員自己寫的,那么我們通過啟動類,是怎么讓這些 Bean 自動配置的呢?
就需要理解一下 Spring Boot 自動配置原理
8.1 @SpringBootApplication 注解
- @SpringBootConfiguration:聲明一個配置類;
- @ComponentScan:組件掃描,掃描的包路徑下的 Bean 存到 Spring 容器中;
- @EnableAutoConfiguration:Spring Boot 實現自動化配置的核心注解;
8.2 @EnableAutoConfiguration 注解
- @Import 注解導入了配置選擇器,讀取該項目的以及引用的 jar 包的包路徑下的 META-INF/spring.factories 文件中的所有配置類;
- 根據一些條件判斷的注解去判斷是否把配置類中的 Bean 放入 Spring 容器;
8.3 回答
- 在 Spring Boot 項目中的引導類有一個注解 @SpringBootApplication,這個注解其實是對三個注解進行了封裝,分別是:
- @SpringBootConfiguration:聲明一個配置類;
- @ComponentScan:組件掃描,掃描的包路徑下的 Bean 存到 Spring 容器中;
- @EnableAutoConfiguration:Spring Boot 實現自動化配置的核心注解;
- 其中,核心注解 @EnableAutoConfiguration,通過 @Import 注解導入了配置選擇器,讀取該項目的以及引用的 jar 包的包路徑下的 META-INF/spring.factories 文件中的所有配置類;
- 會有一些條件判斷的注解(@Conditional 開頭),例如:
- @ConditionalOnClass 判斷是否有對應的字節碼文件,如果有字節碼,就加載該類把這個配置類所有的 Bean 放入 Spring 容器中;
- @ConditionalOnMissingBean 加在 @Bean 標注的方法上,判斷是否有對應的 Bean,如果對應的 Bean 不存在,則將這個 Bean 放入 Spring 容器;