讀書筆記-《Spring技術內幕》(三)MVC與Web環境

前面我們學習了 Spring 最核心的 IoC 與 AOP 模塊(讀書筆記-《Spring技術內幕》(一)IoC容器的實現、讀書筆記-《Spring技術內幕》(二)AOP的實現),接下來繼續學習 MVC,其同樣也是經典。

我們依舊按照從淺到深的方式來學習,先從程序員的視角看看其簡化了哪些工作,幫我們做了什么,再到具體的設計與實現。


01

MVC 概述

在之前 IoC 的筆記中,有這樣一張圖:

這里依然可以復用,從程序員的視角來看,Spring MVC 帶來的最直觀的好處是,我們不需要再去寫繁瑣冗余的 Servlet,而是改寫 Controller。

拉長時間線來看,JavaWeb 的技術發展歷程大致如下:

總結一下就是,初期使用的技術在業務的發展中逐漸暴露出局限性,于是有了分層思想,按照數據維度分層的 MVC 是最經典的分層模式,Spring MVC 就是 MVC 的實現之一。

除了 MVC,還有按照業務維度分層的 DDD,不過后者用得比較少,其比較適合復雜系統,并且需要所有人員(產品、研發、測試)都有較高水準的業務理解。


02

Spring MVC 概述

了解了 MVC 后,我們可以很快明白 Spring MVC 的重點工作。Model 層的 Bean 初始化,Controller 層的請求處理以及 View 層的視圖呈現。具體步驟就是下面三步:

  • 初始化:通過 Bean 定義,在 IoC 容器初始化時,建立起 Controller 與 HTTP 請求的映射關系。

  • 處理請求:MVC 框架接收到 HTTP 請求,DispatcherServlet?根據 URL 查詢到具體的 Controller,Controller 完成請求并生成 ModelAndView 對象。

  • 呈現視圖:視圖對象通過 render 方法完成視圖的呈現。

接下來我們就可以展開步驟,來詳細看看其實現了。


03

Spring MVC 設計與實現

1.初始化

我們以 Tomcat 的 web.xml 文件為例

<servlet><servlet-name>sample</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><load-on-startup>2</load-on-startup>
</servlet><servlet-mapping><servlet-name>sample</servlet-name><url-pattern>/*</url-pattern>
</servlet-mapping><context-param><param-name>contextConfigLocation</param-name><param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param><listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

可以看到,其定義了一個叫 sample 的 servlet,全限定類名正是 DispatcherServlet,且其將處理所有請求。而后,這里還有一個 Bean 定義的配置文件是 WEB-IN 目錄下的 applicationContext.xml。最后,有個監聽器 ContextLoaderListener,其將負責完成 IoC 容器在 Web 環境中的啟動。

從代碼上來看,Web 容器中啟動 Spring?應用程序的過程如下:

  • ContextLoaderListener.contextInitialized() 初始化根上下文,其中調用父類?ContextLoader?的方法;

  • ContextLoader.initWebApplicationContext() 初始化 Web 應用上下文,其中有挺多異常校驗和日志打印;

  • ContextLoader.loadParentContext() 加載雙親上下文;

  • XmlWebApplicationContext.createWebApplicationContext() 創建 Web 應用上下文;

  • XmlWebApplication.refresh() 刷新。這里前面講 IoC 的時候也講到了,refresh 方法可以視作整個容器的初始化方法。

在根上下文初始化好后,就可以關注?DispatcherServlet?了。從前面的概述也看得出來,其是 Spring MVC 的核心。DispatcherServlet?的初始化和處理過程大致如下:

上面時序圖中,從右到左依次是繼承關系,我們來詳細描述下上半邊:

  • HttpServletBean.init() 基類的初始化,首先會獲取 Servlet 的初始化參數,對 Bean 屬性進行配置,也就是上面我們舉例的配置文件里的 Bean,然后調用子類的方法;

  • FrameworkServlet.initServletBean()?初始化 Servlet Bean;

  • FrameworkServlet.initWebApplicationContext()?從 ServletContext 中獲取根上下文,并設置為當前 MVC 上下文的雙親上下文,再把當前上下文設置到 ServletContext 中去(根上下文也就是上面提到的 ContextLoader 設置到 ServletContext 中去的);

  • 上面的 FrameworkServlet.initWebApplicationContext() 中會調用自己的 createWebApplicationContext() ,里面就包含了 refresh();

  • DispatcherServlet.onRefresh();

  • DispatcherServlet.initStrategies() 啟動框架的初始化,代碼很簡潔,依次初始化 multipartResolver、localeResolver、themeResolver、handlerMappings、handlerAdapters、handlerExceptionResolvers、requestToViewNameTranslator、viewResolvers;

  • 以 initHandlerMappings() 為例,將設置所有的 handlerMapping Bean,這些 Bean 可能在當前 DispatcherServlet 的 IoC 容器中,也可能在雙親上下文中。如果都沒有找到,則去 DispatcherServlet.properties 中找默認值。

2.處理請求

前面初始化已完成,接下來就關注上面那張圖的下半邊,也就是 DispatcherServlet 的 doDispatch() 了。

注意 HandlerMapping 有很多實現,比如通過 Bean 名稱的、通過類名稱的,我們以SimpleUrlHandlerMapping 為例,先看下關鍵的數據結構:

// 1.基類定義了方法 getHandler,返回一個 Chain
public interface HandlerMapping {HandlerExecutionChain getHandler(HttpServletRequest req) throws Exception;……
}// 2.Chain 里持有了 handler,也就是我們編寫的 Controller
// 還有個攔截器鏈,對 handler 進行功能增強
public class HandlerExecutionChain {private final Object handler;private HandlerInterceptor[] interceptors;private List<HandlerInterceptor> interceptorList;……
}// 3.關鍵的成員變量,key 是 url,value 是對應的處理 handler
// 它的賦值是在 SimpleUrlHandlerMapping.initApplicationContext() -> AbstractUrlHandlerMapping.registerHandler() 里
// 它的使用是在 AbstractHandlerMapping.getHandler() -> AbstractUrlHandlerMapping.getHandlerInternal() 里
public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping implements MatchableHandlerMapping {private final Map<String, Object> handlerMap = new LinkedHashMap<String, Object>();……
}

再看看?doDispatch() 的詳細時序圖:

可以看到,doDispatch() 完成了模型、控制器、視圖的耦合處理,從根據請求得到對應的 handler,到調用 handler 的攔截器,到調用適配器的 handle(),最后到 ModelAndView 的呈現。

3.呈現視圖

對于最后的視圖呈現,除了當時常見的 JSP 視圖,還有 Excel 視圖、PDF 視圖等等。不過現在都不涉及了,主流的應用都通過前后端分離的方式,將數據的展示交給前端開發處理。

關于視圖呈現,我們以 JSP 視圖為例,過程如下:

  • DispatcherServlet.render()?里面有兩種情況,一種是在 ModelAndView?中設置了 View 的名稱,需要調用 resolveViewName 方法獲取 View,還有一種情況是 ModelAndView 里已經有 View 了,則直接使用(注意這里說的 render 方法是 DispatcherServlet 的,上面時序圖里說的 render 方法是 View 的);

  • DispatcherServlet.resolveViewName()?調用 ViewResolver.resolveViewName(),后者會到上下文中通過名稱把 View 的 Bean 對象獲取到;

  • AbstractView.render()?這里是基類的方法,把所有 Model 進行整合,放在一個 HashMap 里,然后繼續往下調用;

  • InternalResourceView.renderMergedOutputModel() 這里接著會往?AbstractView?調用。不過最終就是 InternalResourceView 完成了數據到頁面的輸出,以及資源的重定向處理;

  • AbstractView.exposeModelAsRequestAttributes()?把 ModelAndView 中的模型數據和請求數據都放到 HttpServletRequest 的屬性中去,這樣程序員就可以愉快的使用了。


???????

原文鏈接:讀書筆記-《Spring技術內幕》(三)MVC與Web環境

原創不易,點個關注不迷路喲,謝謝!

文章推薦:

  • 如何提高核心競爭力
  • 讀書筆記-《當下的力量》
  • 讀書筆記-《寫給大家看的設計書》
  • 賽博朋克2077玩后感
  • 程序員工作中常見問題,你遇到過幾個?
  • 如何設計離線跑批系統
  • 讀書筆記-《人人都是產品經理》

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/37144.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/37144.shtml
英文地址,請注明出處:http://en.pswp.cn/web/37144.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Spring底層原理之bean的加載方式八 BeanDefinitionRegistryPostProcessor注解

BeanDefinitionRegistryPostProcessor注解 這種方式和第七種比較像 要實現兩個方法 第一個方法是實現工廠 第二個方法叫后處理bean注冊 package com.bigdata1421.bean;import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.…

解決idea中git無法管理項目中所有需要管理的文件

點擊文件->設置 選擇版本控制—>目錄映射 點擊加號 設置整個項目被Git管理

【python入門】自定義函數

文章目錄 定義自定義函數的基本語法參數類型示例代碼函數作用域匿名函數&#xff08;Lambda&#xff09;閉包裝飾器 Python中的自定義函數允許你編寫一段可重用的代碼塊&#xff0c;這段代碼可以帶參數&#xff08;輸入&#xff09;&#xff0c;并可能返回一個值&#xff08;輸…

MySQL高級-事務-并發事務演示及隔離級別

文章目錄 0、四種隔離級別1、創建表 account2、修改當前會話隔離級別為 read uncommitted2.1、會出現臟讀 3、修改當前會話隔離級別為 read committed3.1、可以解決臟讀3.2、會出現不可重復讀 4、修改當前會話隔離級別為 repeatable read&#xff08;默認&#xff09;4.1、解決…

解決docker鏡像pull失敗的有效

機器環境 本實踐將在 Ubuntu 22.04.3LTS 系統上進行測試 docker 版本Docker Engine - Community 24.0.6 &#xff0c;原則上docker版本無影響 本實踐進僅學習研究使用&#xff0c;無作他用途。 背景 曾幾何時&#xff0c;docker鏡像的拉去會失敗&#xff0c;網速會慢&#xff0…

代碼隨想錄算法訓練營第五十三天| 739. 每日溫度、 496.下一個更大元素 I、503.下一個更大元素II

LeetCode 739. 每日溫度 題目鏈接&#xff1a;https://leetcode.cn/problems/daily-temperatures/description/ 文章鏈接&#xff1a;https://programmercarl.com/0739.%E6%AF%8F%E6%97%A5%E6%B8%A9%E5%BA%A6.html 思路 * 單調棧的本質是空間換時間&#xff0c;因為在遍歷的過…

【論文閱讀】transformer及其變體

寫在前面&#xff1a; transformer模型已經是老生常談的一個東西&#xff0c;以transformer為基礎出現了很多變體和文章&#xff0c;Informer、autoformer、itransformer等等都是頂刊頂會。一提到transformer自然就是注意力機制&#xff0c;變體更是數不勝數&#xff0c;一提到…

【目標檢測】DN-DETR

一、引言 論文&#xff1a; DN-DETR: Accelerate DETR Training by Introducing Query DeNoising 作者&#xff1a; IDEA 代碼&#xff1a; DN-DETR 注意&#xff1a; 該算法是在DAB-DETR基礎上的改進&#xff0c;在學習該算法前&#xff0c;建議掌握DETR、DAB-DETR等相關知識…

TCP和UDP的區別以及應用場景

TCP&#xff08;傳輸控制協議&#xff09;和UDP&#xff08;用戶數據報協議&#xff09;是兩種不同的傳輸層協議 區別 TCP是面向連接的&#xff0c;UDP是無連接的&#xff1b; TCP是可靠的&#xff0c;UDP是不可靠的&#xff1b; TCP是面向字節流的&#xff0c;UDP是面向數據…

如何高效配置與使用Pip換源

目錄 1. Pip源的基本概念 1.1 常見的國內鏡像源 2. 臨時換源 2.1 使用命令行參數指定鏡像源 2.2 安裝多個包時指定鏡像源 3. 永久換源 3.1 修改用戶級配置文件 3.1.1 創建和編輯配置文件 3.2 修改全局配置文件 3.2.1 創建和編輯全局配置文件 4. 驗證換源配置 5. 切…

VMamba: Visual State Space Model論文筆記

文章目錄 VMamba: Visual State Space Model摘要引言相關工作Preliminaries方法網絡結構2D-Selective-Scan for Vision Data(SS2D) VMamba: Visual State Space Model 論文地址: https://arxiv.org/abs/2401.10166 代碼地址: https://github.com/MzeroMiko/VMamba 摘要 卷積神…

防火墻共性檢測技術

防火墻共性檢測技術 防火墻共性檢測技術是指防火墻在監控和控制網絡流量時&#xff0c;共同采用的一些檢測和過濾方法。無論是哪種類型的防火墻&#xff0c;這些技術都可以用于識別和阻止惡意流量&#xff0c;確保網絡安全。以下是防火墻共性檢測技術的詳細介紹&#xff0c;包…

axios的基本使用和vue腳手架自帶的跨域問題解決

axios的基本使用和vue腳手架自帶的跨域問題解決 1. axios 1.1 導入axios npm i axios1.2 創建serve1.js serve1.js const express require(express) const app express()app.use((request,response,next)>{console.log(有人請求服務器1了);console.log(請求來自于,re…

go Channel 原理 (一)

Channel 設計原理 不要通過共享內存的方式進行通信&#xff0c;而是應該通過通信的方式共享內存。 在主流編程語言中&#xff0c;多個線程傳遞數據的方式一般都是共享內存。 Go 可以使用共享內存加互斥鎖進行通信&#xff0c;同時也提供了一種不同的并發模型&#xff0c;即通…

npm ci vs npm i

npm ci vs npm i 幾個關鍵區別&#xff1a;該選擇哪個&#xff1f; 通過 npm ci 和 npm i 兩個命令&#xff0c;都可安裝項目的依賴。那么這兩個命令有什么區別呢&#xff1f; 幾個關鍵區別&#xff1a; 目的和用途&#xff1a; npm ci &#xff1a;根據項目中的 package-lock…

AI奏響未來樂章:音樂界的革命性變革

AI在創造還是毀掉音樂 引言 隨著科技的飛速發展&#xff0c;人工智能&#xff08;AI&#xff09;正在逐漸滲透到我們生活的每一個角落&#xff0c;音樂領域也不例外。AI技術的引入&#xff0c;不僅為音樂創作、教育、體驗帶來了革命性的變革&#xff0c;更為整個音樂產業注入了…

順序表應用——通訊錄

在本篇之前的順序表專題我們已經學習的順序表的實現&#xff0c;了解了如何實現順序表的插入和刪除等功能&#xff0c;那么在本篇當中就要學習基于順序表來實現通訊錄&#xff0c;在通訊錄當中能實現聯系人的增、刪、查改等功能&#xff0c;接下來就讓我們一起來實現通訊錄吧&a…

grpc學習golang版( 五、多proto文件示例 )

系列文章目錄 第一章 grpc基本概念與安裝 第二章 grpc入門示例 第三章 proto文件數據類型 第四章 多服務示例 第五章 多proto文件示例 第六章 服務器流式傳輸 第七章 客戶端流式傳輸 第八章 雙向流示例 文章目錄 一、前言二、定義proto文件2.1 公共proto文件2.2 語音喚醒proto文…

解決Vue3項目中跨域問題的步驟

決Vue3項目中跨域問題的步驟可以按照以下方式清晰地分點表示和歸納&#xff1a; 1. 使用代理服務器&#xff08;Proxy&#xff09; 步驟&#xff1a; 在Vue項目的根目錄下找到或創建vue.config.js文件。在vue.config.js中配置devServer的proxy選項。設定需要代理的接口前綴&a…

基于局域網下的服務器連接、文件傳輸以及內網穿透教程 | 服務器連接ssh | 服務器文件傳輸scp | 內網穿透frp | 研究生入學必備 | 深度學習必備

&#x1f64b;大家好&#xff01;我是毛毛張! &#x1f308;個人首頁&#xff1a; 神馬都會億點點的毛毛張 &#x1f4cc;本篇博客分享的是基于局域網下的服務器連接&#x1f517;、文件傳輸以及內網穿透教程&#xff0c;內容非常完備?&#xff0c;涵蓋了在服務器上做深度學…