一、Spring 基礎概念
- 什么是 Spring 框架?
- Spring 是一個開源的 Java 應用程序框架,它提供了一種輕量級的、非侵入式的方式來構建企業級應用。Spring 的核心功能包括依賴注入(Dependency Injection,DI)、面向切面編程(Aspect - Oriented Programming,AOP)、事務管理、數據訪問等,旨在簡化企業級 Java 開發,提高開發效率和代碼的可維護性。
- Spring 框架的核心模塊有哪些?
- 核心容器(Core Container)
- spring - core:提供了框架的基本核心功能,如 IOC(控制反轉)和依賴注入(DI)的基礎實現。
- spring - beans:提供了 BeanFactory,這是 Spring 框架的核心工廠接口,用于管理和配置應用程序中的對象(beans)。
- spring - context:建立在核心容器之上,提供了一種更高級的應用程序上下文(ApplicationContext),它是 BeanFactory 的子接口,除了具有 BeanFactory 的功能外,還提供了諸如國際化、事件傳播、資源加載等附加功能。
- spring - context - support:提供了對第三方庫(如緩存、郵件、調度等)的集成支持。
- AOP(Aspect - Oriented Programming)
- spring - aop:提供了面向切面編程的基礎架構,允許在運行時將橫切關注點(如日志記錄、安全檢查等)織入到應用程序的核心業務邏輯中。
- spring - aspectj:對 AspectJ 框架的集成支持,AspectJ 是一種功能強大的 AOP 實現,Spring - aspectj 使得在 Spring 應用中可以使用 AspectJ 的注解和語法進行更靈活的 AOP 編程。
- 數據訪問與集成(Data Access/Integration)
- spring - jdbc:提供了對 JDBC(Java Database Connectivity)的簡化操作,包括數據庫連接的獲取、SQL 語句的執行、結果集的處理等,減少了編寫 JDBC 代碼的繁瑣。
- spring - tx:提供了事務管理的抽象層,支持編程式事務和聲明式事務,可與多種底層事務管理器(如 JDBC 事務、JTA 事務等)集成。
- spring - orm:對多種對象 - 關系映射(ORM)框架(如 Hibernate、MyBatis 等)提供集成支持,方便在 Spring 應用中使用 ORM 框架進行數據庫操作。
- Web(Spring Web)
- spring - web:提供了基礎的 Web 開發功能,如 Web 應用上下文、Servlet 集成、文件上傳等。
- spring - webmvc:Spring 的 MVC(Model - View - Controller)框架,用于構建 Web 應用程序,實現了請求處理、視圖渲染、模型綁定等功能。
- spring - webflux:提供了基于反應式編程(Reactive Programming)的 Web 框架,適用于構建高性能、非阻塞的 Web 應用,如處理大量并發請求的微服務。
- 核心容器(Core Container)
- 解釋一下控制反轉(IOC)和依賴注入(DI)的概念。
- 控制反轉(Inversion of Control,IOC)
- 在傳統的編程模式中,對象的創建和對象間的依賴關系是由程序代碼直接控制的。而在 IOC 模式下,這種控制權被轉移到了容器(如 Spring 容器)中。程序代碼不再負責對象的創建和依賴關系的維護,而是由容器來負責創建對象,并將它們裝配在一起。這就好比在傳統模式下,你自己做飯(自己創建和管理對象),而在 IOC 模式下,你去餐館吃飯(由容器來提供和管理對象)。
- 依賴注入(Dependency Injection,DI)
- 依賴注入是實現控制反轉的一種具體方式。它是指當一個對象(依賴方)需要依賴于另一個對象(被依賴方)時,由容器在創建依賴方對象時,將被依賴方對象注入到依賴方對象中。依賴注入有三種常見的方式:
- 構造函數注入(Constructor Injection):通過構造函數將依賴對象傳入。例如:
public class MyService {private MyRepository myRepository;public MyService(MyRepository myRepository) {this.myRepository = myRepository;} }
** setter 注入(Setter Injection)**:通過 setter 方法將依賴對象傳入。例如:
public class MyService {private MyRepository myRepository;public void setMyRepository(MyRepository myRepository) {this.myRepository = myRepository;} }
字段注入(Field Injection):直接在類的字段上使用注解注入依賴對象,但這種方式會使類與 Spring 框架耦合較緊密,例如:
public class MyService {@Autowiredprivate MyRepository myRepository; }
- 控制反轉(Inversion of Control,IOC)
二、Spring Bean
- Spring Bean 的生命周期是怎樣的?
- 實例化(Instantiation):
- Spring 首先根據配置(如 XML 配置、注解等)確定要創建的 Bean 類型,然后使用 Java 反射機制創建 Bean 的實例。例如,對于一個簡單的使用 @Component 注解的類:
@Component public class MyBean {public MyBean() {// 這是構造函數,在實例化時被調用} }
屬性賦值(Populate Properties):
- 在實例化后,Spring 會對 Bean 的屬性進行賦值。如果使用了依賴注入,會將依賴的其他 Bean 注入到當前 Bean 中。例如:
@Component public class MyBean {@Autowiredprivate AnotherBean anotherBean; }
-
- 這里 Spring 會找到 AnotherBean 的實例并注入到 MyBean 的 anotherBean 屬性中。
- 初始化(Initialization):
- 執行 Bean 的初始化方法。有兩種常見的初始化方式:
- 實現 InitializingBean 接口
@Component public class MyBean implements InitializingBean {@Overridepublic void afterPropertiesSet() throws Exception {// 初始化邏輯} }
使用 @PostConstruct 注解:
@Component public class MyBean {@PostConstructpublic void init() {// 初始化邏輯} }
- 實現 InitializingBean 接口
- 執行 Bean 的初始化方法。有兩種常見的初始化方式:
- Spring 首先根據配置(如 XML 配置、注解等)確定要創建的 Bean 類型,然后使用 Java 反射機制創建 Bean 的實例。例如,對于一個簡單的使用 @Component 注解的類:
- 實例化(Instantiation):
?????????????????????????
- 使用(In - use):
- 初始化完成后,Bean 就可以被應用程序使用了,例如被其他組件調用方法等。
- 銷毀(Destruction):
- 當容器關閉時,會銷毀 Bean。如果 Bean 實現了 DisposableBean 接口,會調用其 destroy 方法:
@Component
public class MyBean implements DisposableBean {@Overridepublic void destroy() throws Exception {// 銷毀邏輯}
}
或者使用 @PreDestroy 注解來定義銷毀方法:
@Component
public class MyBean {@PreDestroypublic void cleanUp() {// 銷毀邏輯}
}
- 如何配置 Spring Bean?有哪些方式?
- 基于 XML 配置:
- 在 XML 文件中定義 Bean,例如:
xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema - instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring - beans.xsd"><bean id="myBean" class="com.example.MyBean"><property name="propertyName" value="propertyValue"/></bean> </beans>
這里定義了一個 id 為 myBean 的 Bean,其類是 com.example.MyBean,并設置了一個屬性。
- 在 XML 文件中定義 Bean,例如:
- 基于注解配置:
- 使用 Spring 的注解來標識 Bean 及其配置。常見的注解有:
- @Component:通用的組件注解,用于標識一個普通的 Spring 組件。例如:
java
@Component public class MyComponent {// 組件邏輯 }
- @Service:用于標識業務邏輯層的組件,通常在服務類上使用。例如:
java
@Service public class MyService {// 服務邏輯 }
- @Repository:用于標識數據訪問層(如數據庫操作)的組件,例如:
java
@Repository public class MyRepository {// 數據訪問邏輯 }
- @Controller:用于標識 Web 應用中的控制器組件,例如在 Spring MVC 中:
java
@Controller public class MyController {// 控制器邏輯 }
- @Configuration 和 @Bean:用于 Java 配置類來定義 Bean。例如:
java
@Configuration public class AppConfig {@Beanpublic MyBean myBean() {return new MyBean();} }
- @Component:通用的組件注解,用于標識一個普通的 Spring 組件。例如:
- 使用 Spring 的注解來標識 Bean 及其配置。常見的注解有:
- 基于 Java 配置(Java - based configuration):
- 除了 @Configuration 和 @Bean 的方式外,還可以通過繼承特定的抽象類來配置 Spring。例如,繼承 WebMvcConfigurerAdapter(在 Spring 5 中已被 WebMvcConfigurer 取代)來配置 Spring MVC:
java
@Configuration public class WebMvcConfig extends WebMvcConfigurer {// 配置視圖解析器等@Overridepublic void configureViewResolvers(ViewResolverRegistry registry) {// 配置邏輯} }
- 除了 @Configuration 和 @Bean 的方式外,還可以通過繼承特定的抽象類來配置 Spring。例如,繼承 WebMvcConfigurerAdapter(在 Spring 5 中已被 WebMvcConfigurer 取代)來配置 Spring MVC:
- 基于 XML 配置:
三、Spring AOP
- 什么是面向切面編程(AOP)?Spring AOP 的原理是什么?
- 面向切面編程(AOP)
- AOP 是一種編程范式,它允許將橫切關注點(如日志記錄、安全檢查、事務管理等)從應用程序的核心業務邏輯中分離出來,以獨立的模塊(切面)進行處理。這樣可以提高代碼的模塊化程度和可維護性。例如,在一個沒有 AOP 的應用中,如果每個業務方法都需要添加日志記錄代碼,會導致日志記錄代碼分散在各個業務方法中,而使用 AOP,可以將日志記錄邏輯定義在一個切面中,然后自動織入到需要的業務方法中。
- Spring AOP 原理
- Spring AOP 主要基于動態代理(Dynamic Proxy)技術。當一個目標對象(被代理對象)需要被織入切面邏輯時,Spring 會根據目標對象是否實現接口來選擇使用 JDK 動態代理或 CGLIB 動態代理。
- JDK 動態代理:
- 如果目標對象實現了接口,Spring 會使用 JDK 動態代理。JDK 動態代理是通過實現目標對象的接口來創建代理對象的。例如,有一個接口和實現類:
java
public interface MyInterface {void doSomething(); } public class MyClass implements MyInterface {@Overridepublic void doSomething() {// 業務邏輯} }
Spring 會創建一個實現 MyInterface 的代理對象,在代理對象的 doSomething 方法中,會在調用目標對象的 doSomething 方法前后添加切面邏輯。
- 如果目標對象實現了接口,Spring 會使用 JDK 動態代理。JDK 動態代理是通過實現目標對象的接口來創建代理對象的。例如,有一個接口和實現類:
- CGLIB 動態代理:
- 如果目標對象沒有實現接口,Spring 會使用 CGLIB(Code Generation Library)動態代理。CGLIB 是通過字節碼生成技術來創建目標對象的子類作為代理對象。例如,有一個沒有實現接口的類:
java
public class MyClass {public void doSomething() {// 業務邏輯} }
Spring 會生成一個 MyClass 的子類作為代理對象,在子類的 doSomething 方法中添加切面邏輯。
- 如果目標對象沒有實現接口,Spring 會使用 CGLIB(Code Generation Library)動態代理。CGLIB 是通過字節碼生成技術來創建目標對象的子類作為代理對象。例如,有一個沒有實現接口的類:
- JDK 動態代理:
- Spring AOP 主要基于動態代理(Dynamic Proxy)技術。當一個目標對象(被代理對象)需要被織入切面邏輯時,Spring 會根據目標對象是否實現接口來選擇使用 JDK 動態代理或 CGLIB 動態代理。
- 面向切面編程(AOP)
- Spring AOP 中的通知(Advice)有哪些類型?
- 前置通知(Before Advice):
- 在目標方法執行之前執行的通知。例如,在一個方法調用前記錄日志:
java
import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before;@Aspect public class LoggingAspect {@Before("execution(* com.example.service.MyService.*(..))")public void beforeAdvice(JoinPoint joinPoint) {System.out.println("Before method: " + joinPoint.getSignature().getName());} }
- 在目標方法執行之前執行的通知。例如,在一個方法調用前記錄日志:
- 后置通知(After Advice):
- 在目標方法執行之后執行的通知。有兩種類型:
- 正常返回后執行的后置通知(After Returning Advice):
- 當目標方法正常返回時執行。例如:
java
import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.Aspect;@Aspect public class LoggingAspect {@AfterReturning(pointcut = "execution(* com.example.service.MyService.*(..))", returning = "result")public void afterReturningAdvice(JoinPoint joinPoint, Object result) {System.out.println("After returning method: " + joinPoint.getSignature().getName() + ", result: " + result);} }
- 當目標方法正常返回時執行。例如:
- 無論是否拋出異常都執行的后置通知(After Finally Advice):
- 這種通知類似于 try - finally 塊中的 finally 語句塊,無論目標方法是否拋出異常都會執行。例如:
java
import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect;@Aspect public class LoggingAspect {@After("execution(* com.example.service.MyService.*(..))")public void afterFinallyAdvice(JoinPoint joinPoint) {System.out.println("After finally method: " + joinPoint.getSignature().getName());} }
- 這種通知類似于 try - finally 塊中的 finally 語句塊,無論目標方法是否拋出異常都會執行。例如:
- 正常返回后執行的后置通知(After Returning Advice):
- 在目標方法執行之后執行的通知。有兩種類型:
- 環繞通知(Around Advice):
- 環繞通知可以在目標方法執行前后都添加邏輯,它可以完全控制目標方法的執行。例如:
java
import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect;@Aspect public class LoggingAspect {@Around("execution(* com.example.service.MyService.*(..))")public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {System.out.println("Before method in around advice: " + pjp.getSignature().getName());Object result = pjp.proceed();System.out.println("After method in around advice: " + pjp.getSignature().getName());return result;} }
- 環繞通知可以在目標方法執行前后都添加邏輯,它可以完全控制目標方法的執行。例如:
- 異常通知(After Throwing Advice):
- 當目標方法拋出異常時執行的通知。例如:
import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Aspect;@Aspect public class LoggingAspect {@AfterThrowing(pointcut = "execution(* com.example.service.MyService.*(..))", throwing = "ex")public void afterThrowingAdvice(JoinPoint joinPoint, Exception ex) {System.out.println("Method: " + joinPoint.getSignature().getName() + " threw exception: " + ex.getMessage());} }
- 當目標方法拋出異常時執行的通知。例如:
- 前置通知(Before Advice):