備案號 不放在網(wǎng)站上網(wǎng)絡(luò)營銷做得好的公司
前文我們分享了IoC、Web、數(shù)據(jù)訪問、AOP、上下文等模塊設(shè)計,聰明的碼友已經(jīng)想到了,該協(xié)同作業(yè)了。
至此協(xié)同作業(yè)我們從以下主流程進行展開:接收請求–>處理請求–>結(jié)果處理–>返回結(jié)果
一、Web 請求入口
場景:用戶發(fā)起 HTTP 請求 → Spring Web 模塊接收請求 → 調(diào)用 Controller 處理業(yè)務(wù)邏輯。
1. 核心流程
2. 核心組件與關(guān)鍵問題
組件 | 主要功能 |
---|---|
DispatcherServlet | 前端控制器,統(tǒng)一接收所有 HTTP 請求,協(xié)調(diào)其他組件完成請求處理流程(分發(fā)請求、調(diào)用處理器、渲染視圖)。 |
HandlerMapping | 根據(jù)請求 URL 映射到對應(yīng)的 Controller 方法(如通過 @GetMapping 注解),返回 HandlerExecutionChain (包含目標處理器和攔截器鏈)。 |
HandlerAdapter | 適配不同類型的處理器(如基于注解的 Controller 或傳統(tǒng) Servlet),執(zhí)行方法并返回 ModelAndView 。 |
ViewResolver | 將邏輯視圖名(如 userView )解析為實際的物理視圖(如 JSP、Thymeleaf 模板)。 |
Interceptor | 實現(xiàn)橫切關(guān)注點(如權(quán)限校驗、日志記錄),在請求處理前后插入自定義邏輯。 |
- 問題 1:如何動態(tài)映射 URL 到 Controller?
- Spring 解決方案:
@GetMapping
/@PostMapping
注解:通過注解定義 URL 路徑和 HTTP 方法。- HandlerMapping:
RequestMappingHandlerMapping
解析注解并建立映射表。
- 問題 2:如何處理異常?
- Spring 解決方案:
@ControllerAdvice
:全局異常處理器,統(tǒng)一處理 Controller 層拋出的異常。HandlerExceptionResolver
:接口實現(xiàn)異常轉(zhuǎn)換邏輯(如返回 JSON 錯誤響應(yīng))。
3. 示例代碼
@RestController
public class UserController {@Autowiredprivate UserService userService;@GetMapping("/user/{id}")public User getUser(@PathVariable String id) {return userService.findUserById(id);}
}@ControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler(Exception.class)public ResponseEntity<String> handleException(Exception ex) {return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(ex.getMessage());}
}
二、業(yè)務(wù)邏輯與 IoC 容器
問題:Controller 需要調(diào)用 Service 和 Repository,如何管理這些對象的生命周期?
解決方案:引入 IoC(控制反轉(zhuǎn)),通過容器管理 Bean 的創(chuàng)建和依賴注入。
1. 核心流程
2. 核心組件與關(guān)鍵問題
組件 | 主要功能 |
---|---|
BeanFactory | Spring IoC 容器的核心接口,負責(zé)管理 Bean 的創(chuàng)建、依賴注入和生命周期。 |
ApplicationContext | 擴展 BeanFactory ,提供更豐富的功能(如事件發(fā)布、資源加載、國際化支持)。 |
BeanDefinition | 定義 Bean 的元數(shù)據(jù)(如類名、作用域、依賴關(guān)系),由容器解析后實例化 Bean。 |
BeanPostProcessor | 自定義 Bean 初始化/銷毀邏輯(如 AOP 代理生成、屬性賦值)。 |
- 問題 1:循環(huán)依賴
- 場景:
A
依賴B
,B
依賴A
,導(dǎo)致初始化死循環(huán)。 - Spring 解決方案:
- 三級緩存機制:
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#singletonObjects
:已完全初始化的 Bean。org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#earlySingletonObjects
:提前暴露的半成品 Bean。org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#singletonFactories
:通過ObjectFactory
創(chuàng)建代理對象。
- 代理對象介入:通過
AOP
生成代理對象,繞過循環(huán)依賴。
- 三級緩存機制:
- 問題 2:Bean 生命周期管理
- Spring 提供:
@PostConstruct
:初始化后調(diào)用。@PreDestroy
:銷毀前調(diào)用。BeanPostProcessor
:自定義初始化/銷毀邏輯。
3. 示例代碼
@Service
public class UserService {@Autowiredprivate UserRepository userRepository;
}@Repository
public class UserRepository {@Autowiredprivate JdbcTemplate jdbcTemplate;
}
三、數(shù)據(jù)訪問模塊
問題:Service 需要查詢數(shù)據(jù)庫,如何封裝 SQL 操作并管理事務(wù)?
解決方案:引入 Spring Data Access 模塊,通過模板模式和聲明式事務(wù)實現(xiàn)。
1. 核心流程
2. 核心組件與關(guān)鍵問題
組件 | 主要功能 |
---|---|
JdbcTemplate | 封裝 JDBC 操作(查詢、更新等),減少模板代碼冗余。 |
@Transactional | 聲明式事務(wù)管理,通過 AOP 控制事務(wù)邊界(如 REQUIRED 、REQUIRES_NEW 傳播模式)。 |
DataSource | 管理數(shù)據(jù)庫連接池(如 HikariCP),提供高性能的數(shù)據(jù)庫連接。 |
RowMapper | 自定義結(jié)果集映射邏輯,將數(shù)據(jù)庫記錄轉(zhuǎn)換為 Java 對象。 |
- 問題 1:SQL 模板代碼冗余
- Spring 解決方案:
JdbcTemplate
:封裝 JDBC 操作(查詢、更新等),減少重復(fù)代碼。- RowMapper:自定義結(jié)果集映射邏輯。
- 問題 2:事務(wù)管理復(fù)雜
- Spring 解決方案:
@Transactional
:通過 AOP 實現(xiàn)事務(wù)邊界控制。- 事務(wù)傳播行為:支持
REQUIRED
、REQUIRES_NEW
等傳播模式。
3. 示例代碼
@Repository
public class UserRepository {@Autowiredprivate JdbcTemplate jdbcTemplate;public User findUserById(String id) {return jdbcTemplate.queryForObject("SELECT * FROM users WHERE id = ?",new Object[]{id},(rs, rowNum) -> new User(rs.getString("name")));}
}@Service
public class UserService {@Transactionalpublic void updateUser(User user) {userRepository.update(user);}
}
四、上下文模塊
問題:如何統(tǒng)一管理配置、事件、資源加載?
解決方案:引入ApplicationContext
,作為 Spring 容器的核心接口。
1. 核心組件與關(guān)鍵問題
組件 | 主要功能 |
---|---|
ApplicationContext | 管理應(yīng)用配置、資源加載、事件發(fā)布和國際化支持。 |
PropertySourcesPlaceholderConfigurer | 解析 application.properties 中的占位符(如 ${spring.datasource.url} )。 |
ApplicationEventPublisher | 發(fā)布事件(如 ContextRefreshedEvent ),支持事件監(jiān)聽器(ApplicationListener )異步處理。 |
MessageSource | 提供多語言資源管理,支持國際化(i18n)。 |
- 問題 1:配置文件加載失敗
Spring 解決方案:PropertySourcesPlaceholderConfigurer
:解析application.properties
中的占位符。@PropertySource
:自定義配置文件路徑。
- 問題 2:事件監(jiān)聽效率低
Spring 解決方案:ApplicationListener
:異步監(jiān)聽事件(通過@Async
實現(xiàn))。
2. 示例代碼
@Configuration
public class AppConfig {@Beanpublic DataSource dataSource() {return DataSourceBuilder.create().build();}
}public class MyCustomListener implements ApplicationListener<ContextRefreshedEvent> {@Overridepublic void onApplicationEvent(ContextRefreshedEvent event) {System.out.println("應(yīng)用啟動完成");}
}
五、AOP 模塊
問題:如何將日志、事務(wù)等公共邏輯與業(yè)務(wù)代碼解耦?
解決方案:引入 AOP(面向切面編程),通過攔截器鏈實現(xiàn)橫切關(guān)注點管理。
1. 核心流程
2. 核心組件與關(guān)鍵問題
組件 | 主要功能 |
---|---|
@Aspect | 定義切面類,封裝橫切關(guān)注點(如日志、事務(wù))。 |
@Pointcut | 定義切入點表達式(如 execution(* com.albert.service.*.*(..)) ),指定切面邏輯的應(yīng)用范圍。 |
MethodInterceptor | 通過責(zé)任鏈模式執(zhí)行通知邏輯(如 @Before 、@After )。 |
ProxyFactory | 動態(tài)生成代理對象(JDK 動態(tài)代理或 CGLIB 代理)。 |
- 問題 1:切點表達式匹配錯誤
- Spring 解決方案:
- AspectJ 表達式語法:精確控制切點范圍(如
execution(* com.albert.service.*.*(..))
)。
- AspectJ 表達式語法:精確控制切點范圍(如
- 問題 2:代理性能問題
- Spring 解決方案:
- JDK 動態(tài)代理:適用于接口類。
- CGLIB 代理:適用于無接口類(需注意
final
方法限制)。
3. 示例代碼
@Aspect
@Component
public class MyLogAspect {@Before("execution(* com.albert.service.*.*(..))")public void logBefore(JoinPoint joinPoint) {System.out.println("方法調(diào)用前: " + joinPoint.getSignature().getName());}
}
六、整體執(zhí)行鏈路總結(jié)
1. Spring 容器啟動階段(資源準備)
1.1 Spring 容器初始化
ApplicationContext
加載配置- 讀取
application.properties
或 XML 配置文件,解析 Bean 定義(BeanDefinition
)。 - 初始化
BeanFactory
,注冊所有 Bean 的定義信息。
- 讀取
- 資源預(yù)加載
- 數(shù)據(jù)庫連接池初始化(如 Druid):
- 根據(jù)配置創(chuàng)建數(shù)據(jù)庫連接池,建立初始連接,確保后續(xù)請求可直接使用。
- AOP 代理對象生成:
- 通過
InstantiationAwareBeanPostProcessor
在 Bean 實例化前生成代理對象(如 AOP 代理)。 - 示例:
postProcessBeforeInstantiation
返回代理對象,跳過默認實例化。
- 通過
- 數(shù)據(jù)庫連接池初始化(如 Druid):
- Bean 實例化與依賴注入
- Bean 實例化:
- 調(diào)用構(gòu)造函數(shù)創(chuàng)建 Bean 實例(或通過工廠方法)。
- 通過
BeanPostProcessor
在實例化前后進行攔截(如屬性校驗、動態(tài)代理)。
- 依賴注入(DI):
- 填充屬性(
@Autowired
注解字段)。 - 通過
postProcessProperties
修改屬性值(如解密敏感數(shù)據(jù))。
- 填充屬性(
- Bean 實例化:
- Bean 初始化
- 調(diào)用
@PostConstruct
方法或init-method
。 - 應(yīng)用 AOP 通知(如
@Around
、@After
)。
- 調(diào)用
1.2 Web 模塊初始化
DispatcherServlet
注冊:- 將
DispatcherServlet
注冊為處理 HTTP 請求的前端控制器。
- 將
HandlerMapping
配置:- 將 URL 映射到 Controller 方法(如
@GetMapping
注解)。
- 將 URL 映射到 Controller 方法(如
ViewResolver
配置:- 配置邏輯視圖名到物理視圖的映射規(guī)則(如 Thymeleaf 模板路徑)。
1.3 事務(wù)管理器初始化
- 配置
PlatformTransactionManager
(如DataSourceTransactionManager
)。 - 注冊事務(wù)注解驅(qū)動(
@EnableTransactionManagement
)。
1.4 事件監(jiān)聽器注冊
- 注冊
ApplicationListener
監(jiān)聽應(yīng)用事件(如ContextRefreshedEvent
)。
2. 請求處理階段(運行時流程)
2.1 客戶端發(fā)送 HTTP 請求
- 請求由
DispatcherServlet
接收,進入 Spring MVC 流程。
2.2 請求映射與處理器調(diào)用
HandlerMapping
查找處理器:- 根據(jù) URL 匹配對應(yīng)的 Controller 方法(
@GetMapping
/@PostMapping
)。
- 根據(jù) URL 匹配對應(yīng)的 Controller 方法(
HandlerAdapter
調(diào)用處理器:- 執(zhí)行 Controller 方法,獲取
ModelAndView
。
- 執(zhí)行 Controller 方法,獲取
2.3 業(yè)務(wù)邏輯執(zhí)行
- Service 層調(diào)用 Repository:
- 通過依賴注入的 DAO 或 JPA Repository 訪問數(shù)據(jù)庫。
- 事務(wù)管理:
@Transactional
注解觸發(fā)事務(wù)邊界(如開啟事務(wù)、提交/回滾)。
2.4 AOP 攔截器鏈執(zhí)行
- 通知邏輯執(zhí)行:
@Before
:前置增強(如權(quán)限校驗)。@After
:后置增強(如日志記錄)。@Around
:環(huán)繞增強(如性能監(jiān)控)。
- 異常處理:
@AfterThrowing
捕獲異常并執(zhí)行自定義邏輯。
2.5 視圖解析與響應(yīng)返回
ViewResolver
解析視圖:- 將邏輯視圖名(如
userView
)轉(zhuǎn)換為物理視圖(如 Thymeleaf 模板路徑)。
- 將邏輯視圖名(如
- 渲染視圖并返回響應(yīng):
- 將
Model
數(shù)據(jù)傳遞給視圖模板,生成 HTML 響應(yīng)。
- 將
附錄:各模塊設(shè)計引入后的新問題與解決
模塊 | 引入后的新問題 | Spring 解決方案 |
---|---|---|
Web | 請求映射沖突、異常處理不統(tǒng)一 | @RequestMapping 注解、@ControllerAdvice 全局異常處理器 |
IoC | 循環(huán)依賴、Bean 初始化失敗 | 三級緩存機制、@Lazy 延遲加載、@PostConstruct 自定義初始化 |
數(shù)據(jù)訪問 | SQL 模板代碼冗余、事務(wù)管理復(fù)雜 | JdbcTemplate 、@Transactional 聲明式事務(wù) |
AOP | 切點匹配錯誤、代理性能問題 | AspectJ 表達式語法、JDK/CGLIB 代理選擇 |
上下文 | 配置文件加載失敗、事件監(jiān)聽效率低 | PropertySourcesPlaceholderConfigurer 、@Async 異步事件監(jiān)聽 |