Spring
單例bean是否線程安全
spring中的bean由@Scope註解定義是否為單例
- @Scope(“singleton”) 單例,默認
- @Scope(“prototype”) 多例
因此spring中bean默認單例
但單例bean也可能會導致線程不安全
這需要看bean有無狀態
- 有狀態:類中有可被修改的成員變量(通常是我們自己手動添加了成員變量)
- 無狀態:類中無可被修改的成員變量(例如通常情況下自動注入的service、mapper等bean,它們不可被修改)
若bean有狀態,多線程訪問時都對這個成員變量進行修改,就可能導致線程安全
此時我們可以通過
- 多例(資源不共享)
- 加鎖(資源共享,但互斥訪問)
來解決線程安全問題
AOP
作用:減少重複代碼、抽取公共模塊降低耦合
常用場景:
- 記錄日誌
- 緩存處理
- 事務處理
使用:
- 定義切面類(bean管理、@Aspect)
- 定義切入點(可通過類路徑、註解指定切入位置)
- 定義通知(常用環繞通知)
spring事務也基於AOP實現
事務
spring事務也基於AOP實現
作用:讓代碼同成功同失敗
使用:方法上添加@Transactional
失效場景:
- 異常被手動捕獲(沒拋出,spring事務框架就不知道要回滾)
- 拋出檢查異常(spring默認只回滾非檢查異常,即只回滾RunTimeException)
通過@Transactional(rollbackFor = Exception.class)即可解決
- 非public修飾的方法(spring只給public方法創建代理、添加事務通知)
bean生命週期
無論是通過在xml中定義bean信息,還是通過註解定義bean信息
在創建bean前,spring都會先將bean信息封裝成BeanDefinition對象,作為後續創建bean的依據
流程:
- 實例化(依據BeanDefinition調用構造函數,創建初始bean)
- 初始化
- 依賴注入(注入成員變量,包括存儲值的普通成員變量、依賴的其他bean)
- Aware接口(實現以下三個接口,並實現對應方法,方法中的代碼會依次執行)
- BeanNameAware
- BeanFactoryAware
- ApplicationContextAware
- BeanPostProcessor#before(和後面那個after一樣,定義在另一個實現了BeanPostProcessor類(記得註冊為bean)中,實現before、after方法,方法中的代碼會依次執行)
- 初始化方法
- InitializingBean(實現對應接口和方法,操作和Aware一樣)
- 自定義init方法(@PostConstruct修飾的方法)
- BeanPostProcessor#after(常用作AOP的位置)
- 銷燬(銷燬前執行@PreDestroy修飾的方法)
BeanPostProcessor相對於Aware接口
- 前者相當於全局處理器,定義在外部,可作用於所有符合條件的bean
- 後者乃至大部分初始化步驟,只針對實現了對應接口、或者使用了對應註解的bean自身
bean循環依賴
循環依賴:兩個或多個Bean相互依賴(如A依賴B,B依賴A)
主要作用在bean生命週期的屬性注入階段
通過spring三級緩存解決
- 一級:已經初始化完成的bean對象
- 二級:提前暴露的 “半成品” 單例 Bean 實例(存由三級工廠創建的對象)
- 三級:用於創建 “提前暴露” 的 Bean 實例的 Bean 工廠
簡單流程如下:
- 實例化:通過構造器創建 Bean 的原始對象(此時對象已存在,但屬性未注入,初始化未完成)
- 提前暴露:將剛實例化的原始對象放入三級緩存,供其他依賴它的 Bean 提前引用
- 依賴注入:
- Spring 嘗試為當前 Bean 注入依賴(如,A 實例化後,注入依賴 B)
- 若 B 尚未創建,則觸發 B 的實例化流程
- 當 B 實例化後,注入依賴 A 時,會從三級緩存中獲取 A 的提前暴露實例,避免 A 重複創建,從而解決循環依賴
但若依賴注入的方式是構造函數注入,因為實例化第一步就調用構造函數,因此spring無法通過三級緩存解決循環依賴問題,此時就需要添加@Lazy註解,讓bean對象懶加載
SpringMVC
執行流程
- 用户發出請求到DispatcherServlet(前端控制器)
- DispatcherServlet調用HandlerMapping(處理器映射器)
- HandlerMapping找到具體的處理器並將處理器對象返回給DispatcherServlet
- DispatcherServlet調用HandlerAdapter(處理器適配器)
- HandlerAdapter調用具體的處理器
- 處理器添加了@ResponseBody
- 通過HttpMessageConverter將返回結果轉為JSON並響應
SpringBoot
自動配置原理
啓動類有註解@SpringBootApplication
該註解包含:
- @SpringBootConfiguration
- @EnableAutoConfiguration
- @ComponentScan
@EnableAutoConfiguration是實現自動配置的核心
其內部包含@Import會導入META-INF/spring.factories文件中所配置的類的全類名
被導入的配置類會自帶@Conditional等註解,符合條件則將配置類中的bean注入到spring容器中
常用註解
|
分類
|
註解名稱
|
作用説明
|
|
啓動類註解 |
|
Spring Boot 應用入口註解,組合了 |
|
配置類註解 |
|
標識當前類為配置類,替代傳統 XML 配置文件,可通過 |
|
|
用於 |
|
|
|
加載指定的外部屬性文件(如 |
|
|
|
將配置文件中的屬性批量綁定到 Java 類(需配合 |
|
|
組件掃描 |
|
指定組件掃描路徑,默認掃描當前類所在包及子包,可通過 |
|
|
通用組件註解,標識類為 Spring 管理的 Bean,通常用於非三層架構的普通組件
|
|
|
|
標識 MVC 中的控制器(Controller),處理 HTTP 請求,返回視圖或數據
|
|
|
|
組合 |
|
|
|
標識業務邏輯層(Service)組件
|
|
|
|
標識數據訪問層(DAO)組件,自動轉換數據庫操作異常為 Spring 統一異常
|
|
|
依賴注入 |
|
自動注入依賴的 Bean,默認按類型匹配,可配合 |
|
|
與 |
|
|
|
JDK 自帶註解,默認按名稱注入,無名稱時按類型匹配(與 |
|
|
|
注入配置文件中的單個屬性值(如 |
|
|
MVC 映射 |
|
映射 HTTP 請求(URL、方法、參數等)到控制器方法,可用於類或方法上(類上定義公共路徑)
|
|
|
簡化的 |
|
|
|
簡化的 |
|
|
|
處理 PUT 請求(通常用於更新資源)
|
|
|
|
處理 DELETE 請求(通常用於刪除資源)
|
|
|
|
標識控制器方法返回結果直接寫入響應體(而非視圖),通常用於返回 JSON 數據
|
|
|
|
將 HTTP 請求體(如 JSON)轉換為方法參數對象(適用於 POST/PUT 等請求)
|
|
|
|
綁定 URL 路徑中的參數(如 |
|
|
|
綁定請求參數(如 |
|
|
|
綁定請求頭信息到方法參數(如 |
|
|
條件註解 |
|
基於條件判斷是否創建 Bean 或導入配置類,需自定義 |
|
|
當類路徑中存在指定類時,才生效(如 |
|
|
|
當類路徑中不存在指定類時生效
|
|
|
|
當容器中存在指定 Bean 時生效
|
|
|
|
當容器中不存在指定 Bean 時生效(常用於自動配置的 “默認 Bean”)
|
|
|
|
當配置文件中存在指定屬性且值匹配時生效(如 |
|
|
AOP 相關 |
|
標識當前類為 AOP 切面類
|
|
|
定義切入點(如 |
|
|
|
前置通知,在目標方法執行前執行
|
|
|
|
後置通知,在目標方法執行後(無論是否異常)執行
|
|
|
|
返回通知,在目標方法正常返回後執行
|
|
|
|
異常通知,在目標方法拋出異常後執行
|
|
|
|
環繞通知,包裹目標方法,可控制方法執行時機(需顯式調用 |
|
|
事務管理 |
|
標識方法或類需要事務管理,可配置隔離級別、傳播行為、超時時間等
|
|
測試相關 |
|
用於 Spring Boot 測試類,自動加載應用上下文,支持模擬環境
|
|
|
在測試中替換容器中的 Bean 為 Mock 實例(通常配合 Mockito 使用)
|
Mybatis
執行流程
- 讀取mybatis配置文件,加載運行環境和xml(sql映射文件)
- 根據配置文件構建SqlSessionFactory(會話工廠)
- 每次操作數據庫前,SqlSessionFactory創建SqlSession對象
- 根據SqlSession創建mapper代理對象
- 調用mapper代理方法時調用Executor(執行器)生成Statement對象(包含sql、結果集等)
- 若成功則SqlSession提交事務,再關閉SqlSession
延遲加載
即需要用到數據時才加載
立即加載是,當我查詢一個表後,這個表關聯的其他表的內容也都查詢出來
例如:
user表和user_like表是一對多關係
實體類中user類中有屬性 List userLikes
如果是立即加載,則會將useLiskes的值也填充好,默認是立即加載
一二級緩存
作用:減少數據庫查詢次數,提升性能
本質:把已查過的數據暫存在內存中,後續再查相同數據時直接從緩存取,不用再訪問數據庫
一級緩存默認開啓,二級默認不開啓
一級針對SqlSession(避免同一會話重複查詢),二級針對SqlSessionFactory(即多個SqlSession共享緩存,多個請求走同一個緩存)