1.Spring?AOP?怎么實現的
AOP 即面向切面編程,是通過代理實現的,主要分為靜態代理和動態代理,靜態代理就是在程序運行前就已經指定并聲明了代理類和增強邏輯,運行時就已經被編譯為字節碼文件了,而動態代理則是在運行過程中,動態生成代理類增強目標類,主要分為 JDK 和 CGLIB 動態代理,一般在項目中多采用注解方式聲明切面,切點以及連接點,Spring 會自動掃描切面,通過切點尋找需要代理增強的類,pring 會根據目標 Bean 的類型來選擇代理方式:
- 如果目標類實現了接口,Spring 默認采用 JDK 動態代理,生成實現接口的代理類;
- 如果目標類未實現任何接口,或設置了
proxyTargetClass = true
,則使用 CGLIB 動態代理,生成目標類的子類進行方法增強; - CGLIB 是通過繼承并重寫目標類中非 final 的方法,在子類中織入增強邏輯并調用父類方法完成的。
2.Spring的兩大核心是什么?談一談你對IOC的理解? 談一談你對DI的理解?
Spring 的兩大核心是控制反轉(IOC)和面向切面編程(AOP),IOC 的目的是將 Bean 的生命周期管理從程序員手中交由 Spring 管理,傳統開發都需要程序員 new 對象并管理對象,IOC 則只需要聲明依賴,使程序員專注于業務邏輯的實現,DI 依賴注入則是 IOC 控制反轉的具體實現,Bean 交由 Spring 容器管理后,就需要在程序員調用的時候注入進來。
IOC 的一般實現方法有 XML 配置和注解配置,注解配置主要是通過 Component, Service,Bean,Controller,Confuguration 等注解;
DI 的一般實現方法有構造器注入,Setter 注入以及字段注入;
3.Spring 的生命周期?
Spring 的生命周期指的是 Spring 容器從創建、初始化 Bean、管理 Bean 到銷毀 Bean 的整個過程。這個過程貫穿了從啟動到關閉整個應用的生命周期。
階段 | 說明 |
1. 實例化 | 通過構造方法或反射創建 Bean 對象 |
2. 依賴注入(DI) | Spring 根據配置注入屬性值(@Autowired、@Value、@Resource) |
3. 執行 Aware 接口回調 | 如 BeanNameAware、ApplicationContextAware 獲取容器信息 |
4. BeanPostProcessor(前置) | 調用 方法 |
5. 初始化 | - 方法執行 - 實現 - 配置的 執行 |
6. BeanPostProcessor(后置) | 調用 方法 |
7. Bean 使用階段 | Bean 進入工作狀態,參與項目邏輯處理 |
8. 銷毀前處理 | - 方法執行 - 實現 - 配置的 執行 |
9. Bean 被銷毀 | Spring 容器關閉,釋放資源 |
4.Spring 支持 bean 的作用域有幾種嗎? 每種作用域是什么樣的?
Bean 的作用域有單例,原型,request,session,globalSession 五種;
單例是默認作用域,在整個程序生命周期內有且僅有一個實例;
原型作用域則是每次注入或調用 getBean() 都會返回一個新對象;
request 作用域則是在單個請求內有且僅有一個實例 Bean,不同請求中的實例不同;
session 作用域則是在同一個 session 內有且僅有一個實例 Bean,session 關閉后所有 session 作用域的 Bean 銷毀;
application 則是一個 ServletContext 對應一個 Bean,整個 Web 應用共享一個實例。
@Component
@Scope("prototype") // 也可以是 singleton, request, session 等
public class MyBean {...
}
5.Spring 事務的實現方式和實現原理
Spring 事務可以通過注解 Transactional 來實現,Spring 事務的實現主要依賴于底層數據庫的事務
Spring 事務有兩種實現方式:
- 編程式事務管理(使用
TransactionTemplate
) - 聲明式事務管理(使用
@Transactional
注解)——最常用
- 編程式事務管理(使用
Spring 聲明式事務的核心原理是:基于 AOP(面向切面編程)實現方法級別的事務增強。其底層實現包括以下幾個關鍵點:
- 代理機制
Spring 會為標注了@Transactional
的類或方法創建代理對象:
- 代理機制
- 如果類實現了接口,使用 JDK 動態代理;
- 如果沒有實現接口,使用 CGLIB 動態代理。
- 事務攔截器(TransactionInterceptor)
攔截器會攔截所有事務方法,在方法執行前后插入統一事務邏輯:
- 事務攔截器(TransactionInterceptor)
- 獲取事務屬性(事務傳播、隔離級別、是否只讀等);
- 調用事務管理器(
PlatformTransactionManager
)開啟、提交或回滾事務。
- 事務管理器(TransactionManager)
Spring 并不直接操作數據庫事務,而是通過事務管理器統一封裝底層操作。常用管理器包括:
- 事務管理器(TransactionManager)
DataSourceTransactionManager
(JDBC/MyBatis)JpaTransactionManager
(JPA/Hibernate)
- 數據庫事務控制
Spring 的事務最終會調用底層連接(Connection
)來控制事務,例如:
- 數據庫事務控制
java復制代碼
conn.setAutoCommit(false); // 開啟事務
conn.commit(); // 提交事務
conn.rollback(); // 回滾事務
Spring 的聲明式事務是基于 AOP 實現的,它通過為 @Transactional 方法創建代理對象,在方法調用前后統一管理事務。其核心是 TransactionInterceptor 攔截器,它依賴 PlatformTransactionManager 與底層數據庫事務交互,最終通過 JDBC 的事務操作實現提交與回滾。這樣程序員只需要添加注解即可實現完整的事務控制,極大地簡化了開發流程。
6.Spring 框架中都用到了哪些設計模式?
提到 Spring 框架中的設計模式,必不可少的就是其兩大核心性質,IOC 和 AOP,IOC 則是通過 Spring 容器管理 Bean,就是使用了工廠方法進行 Bean 的依賴注入,以及 Bean 默認都是單例的,所以采用了單例模式;而 AOP 就是采用了代理模式實現目標類的代理增強;
以及 JDBCTemplate,RedisTemplate 都采用了模板方法,Spring 項目啟動時一系列的監聽時間,采用了觀察者模式。
7.你知道的 Spring 的通知類型有哪些,分別在什么時候執行?
before: 方法執行之前調用(不影響方法執行)
after: 方法執行之后調用(無論是否拋異常都會執行)
around: 方法執行前后都能控制,可手動決定是否執行目標方法
afterReturning: 方法正常返回后調用(無異常時才會執行)
afterThrowing: 方法拋出異常后調用
8.Spring 的對象默認是單例的還是多例的? 單例 bean 存不存在線程安全問題呢?
Spring 的對象默認是單例的,單例 Bean 也存在線程安全問題,比如某個有狀態的 Bean,允許不同線程修改該狀態,在多線程環境下就會出現狀態異常的情況。解決方式包括加鎖、使用線程安全結構,或通過 @Scope("prototype")、ThreadLocal 等方式避免共享。
9.@Resource 和 @Autowired 依賴注入的區別是什么? @Qualifier 使用場景是什么?
Resource 和 Autowired 都是 Spring 自動依賴注入的注解,Resource 只能修飾屬性,而 Autowired 既可以修飾屬性又可以修飾方法,修飾屬性就是在容器中尋找對應實例并注入,修飾方法時,則是自動執行該方法。再使用 Resource 依賴注入屬性時,Spring 會先通過屬性名在容器中查找對應的類,找不到再使用類型匹配,Autowired 則相反;Autowired 也可以通過使用 Qualifier 指定類名稱來依賴注入,在容器中存在多個同類型 Bean 時,應使用 @Qualifier 明確指定注入的 Bean。
10.Spring 的常用注解
注解名 | 功能分類 | 用于位置 | 作用說明 |
| 組件定義 | 類上 | 通用組件,注冊到容器 |
| Web 控制層 | 類上 | MVC 控制器,返回視圖 |
| 業務邏輯層 | 類上 | 標識 Service 層組件 |
| 持久層組件 | 類上 | DAO 層組件,支持異常轉換 |
| Web 控制層 | 類上 | 等價于 ,返回 JSON |
| 配置類 | 類上 | 聲明 Java 配置類,等同 XML 配置 |
| 注冊 Bean | 方法上 | 注冊一個手動管理的 Bean 到容器 |
| Bean 掃描配置 | 類上 | 指定包路徑,自動掃描 等注解 |
| 依賴注入 | 屬性/構造器/方法 | 按類型注入 Bean |
| Bean 精確注入 | 屬性/參數 | 指定注入 Bean 的名稱,配合 使用 |
| 請求映射 | 類/方法上 | 映射請求路徑,支持所有 HTTP 方法 |
| 請求映射 | 方法上 | 映射 GET 請求, 簡寫 |
| 請求映射 | 方法上 | 映射 POST 請求 |
| 請求映射 | 方法上 | 映射 PUT 請求 |
| 請求映射 | 方法上 | 映射 DELETE 請求 |
| 參數綁定 | 方法參數 | 綁定 URL 路徑中的變量值 |
| 參數綁定 | 方法參數 | 綁定請求參數(?id=123) |
| 參數綁定 | 方法參數 | 接收 JSON 請求體,自動反序列化成對象 |
| 響應綁定 | 方法/類上 | 將方法返回值序列化為 JSON 響應體 |
| 事務控制 | 類/方法上 | 開啟事務支持,自動提交/回滾 |
| 啟動配置 | 主類上 | 組合注解,包含 、 、 |
11.Spring 的事務傳播行為
傳播行為 | 含義說明 | 常見使用場景 |
(默認) | 如果當前存在事務,則加入;否則新建事務 | 常用(最常用的默認值) |
| 掛起當前事務,新建一個事務 | 重要操作不影響主事務 |
| 有事務就加入,沒有就以非事務方式執行 | 讀操作(可選事務) |
| 以非事務方式執行,掛起當前事務 | 調用不希望有事務的邏輯 |
| 不能處于事務中,若存在事務就拋異常 | 明確禁止事務的代碼 |
| 必須在事務中執行,若沒有就拋異常 | 依賴事務的邏輯(如更新) |
| 當前有事務,則在嵌套事務中執行(使用保存點),否則新建事務 | 回滾子邏輯不影響主邏輯 |
12.Spring 中的事務隔離級別
Spring 中事務隔離級別通過 @Transactional(isolation = Isolation.XXX) 來設置,實質上是借助底層數據庫(如 MySQL、Oracle)提供的隔離機制。所以分為讀未提交,讀已提交,可重復讀,串行化。
Spring 的事務隔離級別通過 @Transactional
設置,最終由底層數據庫實現。Spring 提供 5 種隔離級別,分別對應數據庫中的標準隔離等級。默認使用數據庫配置(MySQL 是 REPEATABLE_READ)。合理設置隔離級別可以避免臟讀、不可重復讀、幻讀等并發問題。
13.攔截器,過濾器,統一異常處理器的區別
過濾器(Filter)是 Servlet 提供的,在 DispatcherServlet 之前執行,作用范圍是所有請求(包括靜態資源),一般用作登錄校驗、請求日志、跨域處理(CORS);
而攔截器(Interceptor)是 Spring MVC 提供的,攔截器在 DispatcherServlet 之后,Controller 之前執行,一般用作權限校驗、參數封裝、請求預處理/后處理;
統一異常處理器(ControlerAdvice)也是是 Spring MVC 提供的,ControlerAdvice 則是在 Controller 執行時拋出異常后,一般用作統一異常轉換成友好 JSON 響應;
當一個請求進入到程序中,需要先進入 Spring 容器,再通過過濾器到 Servlet,最后通過攔截器后才能到控制器,同樣返回結果就是根據相反方向流動,統一異常處理器則是當程序允許出現了異常,一路拋出直到被統一異常處理器捕獲,它根據異常類型采取不一樣的策略。
@ControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler(value = RuntimeException.class)public ResponseEntity<String> handleRuntimeException(RuntimeException ex) {return ResponseEntity.status(500).body("系統錯誤:" + ex.getMessage());}
}
14.有哪些不同類型的依賴注入實現方式?
DI 的一般實現方法有構造器注入,Setter 注入以及字段注入;
@Component
public class OrderService {private final UserService userService;@Autowiredpublic OrderService(UserService userService) {this.userService = userService;}
}@Component
public class OrderService {private UserService userService;@Autowiredpublic void setUserService(UserService userService) {this.userService = userService;}
}@Component
public class OrderService {@Autowiredprivate UserService userService;
}
構造器注入依賴最明確、安全性高、支持 final,是官方推薦方式;Setter 注入適合可選依賴;字段注入雖然最常見但不利于測試和維護,不推薦在生產中使用。
15.Spring 中事務失效的場景?
1. 自調用:同一個類內部調用自己的 @Transactional
方法 , Spring 的事務是通過代理對象調用生效的,自調用直接調用原始對象,不會走代理。在同一個類內部,方法之間直接調用不會通過 Spring AOP 的代理對象,因此 @Transactional 注解失效。如果外部沒有事務,內部也不會開啟事務;如果外部已有事務,內部方法雖然注解失效,但其邏輯會“加入”外部事務一起提交或回滾。所以,內部方法的事務傳播屬性不會生效,所有事務行為以外部為準。
@Service
public class UserService {@Transactionalpublic void outer() {inner(); // 事務不生效}@Transactionalpublic void inner() {// 不會被代理攔截}
}public void outer() {// 無 @Transactional,也沒事務開啟inner(); // inner 雖然有 @Transactional,但事務失效,也不會開啟事務
}
@Transactional
public void inner() {// 實際沒有事務!!!
}@Transactional // 外部有事務
public void outer() {inner(); // 調用 inner 時沒走代理,@Transactional 失效
}@Transactional(propagation = Propagation.REQUIRES_NEW)
public void inner() {// 事務傳播屬性不生效,只是加入了 outer 的事務
}
2.方法不是 public 修飾的,Spring 事務 AOP 默認只能攔截 public 方法。非 public 方法(如 private, protected)不會被代理增強。
3. 異常被捕獲后未拋出,默認只有 RuntimeException 或其子類 異常才會觸發事務回滾;如果捕獲了異常但沒有重新拋出,就無法觸發回滾。
@Transactional
public void save() {try {// 報錯了} catch (Exception e) {log.error("異常", e);}// 沒有回滾
}
4. 數據庫不支持事務或操作本身不在事務控制范圍內,比如 DDL 語句(如 CREATE, ALTER)在某些數據庫下不會被事務控制;非關系型數據庫(如 Redis、Mongo)不支持 Spring 的 JDBC 事務。
5. 未被 Spring 容器管理的類上加了 @Transactional
,如果不是 @Service、@Component、@Controller、@Repository 注解的類,Spring 不會創建代理。
6. 事務傳播行為設置不當,比如外層無事務,調用內部 REQUIRES_NEW 方法,會新建事務;但外層異常回滾不會影響內層已經提交的事務。
7. 多線程操作中事務不生效,每個線程是獨立的,線程中執行方法不會沿用主線程的事務上下文。在異步任務(如 @Async)中事務失效。
16.JDK 動態代理和 CGLIB 動態代理的區別
Spring AOP 是通過 動態代理 實現增強的。根據目標類是否實現接口,選擇不同的代理方式:
JDK 動態代理:要求目標類實現接口;Spring 會生成一個實現目標接口的代理類;通過 InvocationHandler#invoke() 攔截方法調用,執行增強邏輯。
CGLIB 動態代理:目標類沒有接口,或強制使用 proxyTargetClass=true;通過生成目標類的子類,并重寫非 final 方法實現增強;增強邏輯織入子類方法中,調用時先執行增強邏輯再調用原方法。
17.Spring 中的循環引用
Spring 中的循環依賴指多個 Bean 之間互相引用的現象。Spring 通過三級緩存(singletonObjects、earlySingletonObjects 和 singletonFactories)解決了單例作用域和非構造器注入的循環依賴。但對于構造器注入或原型作用域 Bean 的循環依賴,Spring 無法解決,會拋出異常。
@Component
public class A {@Autowiredprivate B b;
}@Component
public class B {@Autowiredprivate A a;
}
Spring 通過 三級緩存機制解決了大部分單例 Bean 的構造器注入(Setter 或字段注入)循環依賴問題。
緩存名稱 | 類型 | 描述 |
一級緩存 |
| 成熟 Bean(完全初始化) |
二級緩存 |
| 半成品 Bean(已實例化未初始化) |
三級緩存 |
| Bean 工廠,用于暴露代理或提前引用 |
- Spring 在創建 Bean A 時,發現它依賴 B;
- 開始創建 B,發現 B 又依賴 A;
- Spring 會從三級緩存提前暴露 A 的實例引用(還未注入屬性);
- B 獲取到 A 的“早期引用”,順利注入;
- A、B 最終都能完整實例化,注入完成。
18.Spring?有什么好處
1.解耦 + 降低復雜度(IOC/DI)
核心優勢:通過控制反轉(IoC)和依賴注入(DI)實現對象管理,解耦對象之間的依賴關系;
你只需要關注“用什么”,而不是“怎么創建”;
減少了硬編碼,提升了系統可測試性和擴展性。
2.AOP 支持
面向切面編程(AOP)讓你可以統一處理橫切關注點,如:事務、日志、安全、權限校驗等;
可以在不修改業務代碼的前提下,動態增強 Bean 的功能。
3.統一事務管理
Spring 提供了聲明式事務管理(@Transactional
),簡潔、清晰;
支持多種事務管理器(JDBC、JPA、Hibernate、MyBatis)。
4.豐富的模塊和生態支持
Spring 不是一個工具,而是一個完整生態體系。
19. 為什么構造器注入不能解決循環依賴?
Spring 的循環依賴解決機制依賴于 提前暴露“半成品” Bean 實例(early reference):
- 構造器注入是在 Bean 還沒創建出來之前就必須準備好所有依賴;
- Spring 此時無法提前放入三級緩存,沒有機會暴露半成品;
- 所以只要 A 和 B 都通過構造器互相依賴,Bean 根本創建不出來,就死鎖或拋異常。