目錄

  • 模板方法
  • 源碼
  • 建造者

模板方法

定義一個操作中的算法的骨架,而將一些步驟延遲到子類中,使得子類可以不改變一個算法的結構即可重定義該算法的某些特定步驟

源碼

https://github.com/dotnet/aspnetcore/

在目錄 aspnetcore\src\Mvc\Mvc.Core\src\Infrastructure 下有一個 ControllerActionInvoker,它繼承自 ResourceInvoker

internal class ControllerActionInvoker : ResourceInvoker, IActionInvoker

在 ResourceInvoker 中定義了一些算法的骨架,在 InvokeAsync 方法中對一些方法進行了組裝

public virtual Task InvokeAsync()
{
...
task = InvokeFilterPipelineAsync();
...
return ReleaseResourcesCore(scope).AsTask();
...
}

還有一些抽象方法需要在子類 ControllerActionInvoker 中實現

/// <summary>
/// In derived types, releases resources such as controller, model, or page instances created as
/// part of invoking the inner pipeline.
/// </summary>
protected abstract ValueTask ReleaseResources(); protected abstract Task InvokeInnerFilterAsync();

這裏就是模板方法的一個應用,通過抽象類和一個子類來實現

子類沒有 InvokeAsync 方法,它在頂層完成了封裝,對多個方法進行調用,同時提供一些中間聯合的方法

從 MapControllers 方法的角度看,調用了 ControllerEndpointRouteBuilderExtensions

app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});

ControllerEndpointRouteBuilderExtensions 這個類會告訴你整個注册的過程發生了什麼

首先,它接收了一個 IEndpointRouteBuilder

public static ControllerActionEndpointConventionBuilder MapControllers(this IEndpointRouteBuilder endpoints)
{
... EnsureControllerServices(endpoints); return GetOrCreateDataSource(endpoints).DefaultBuilder;
}

在 EnsureControllerServices 中把所有的服務獲取進來

var marker = endpoints.ServiceProvider.GetService<MvcMarkerService>();

MvcMarkerService 需要先注册,獲取 DataSources,然後注册

private static ControllerActionEndpointDataSource GetOrCreateDataSource(IEndpointRouteBuilder endpoints)
{
var dataSource = endpoints.DataSources.OfType<ControllerActionEndpointDataSource>().FirstOrDefault();
if (dataSource == null)
{
var orderProvider = endpoints.ServiceProvider.GetRequiredService<OrderedEndpointsSequenceProviderCache>();
var factory = endpoints.ServiceProvider.GetRequiredService<ControllerActionEndpointDataSourceFactory>();
dataSource = factory.Create(orderProvider.GetOrCreateOrderedEndpointsSequenceProvider(endpoints));
endpoints.DataSources.Add(dataSource);
} return dataSource;
}

在 ControllerActionEndpointDataSource 中遍曆 actions

for (var i = 0; i < actions.Count; i++)
{
if (actions[i] is ControllerActionDescriptor action)
{
_endpointFactory.AddEndpoints(endpoints, routeNames, action, _routes, conventions, CreateInertEndpoints);

這些 actions 來自於基類 ActionEndpointDataSourceBase

public ActionEndpointDataSourceBase(IActionDescriptorCollectionProvider actions)
{
_actions = actions; Conventions = new List<Action<EndpointBuilder>>();
}

actions 通過 CreateEndpoints 綁定到 RequestDelegate

protected override List<Endpoint> CreateEndpoints(IReadOnlyList<ActionDescriptor> actions, IReadOnlyList<Action<EndpointBuilder>> conventions)

CreateEndpoints 中有一個 AddEndpoints 方法

_endpointFactory.AddEndpoints(endpoints, routeNames, action, _routes, conventions, CreateInertEndpoints);

在 AddEndpoints 方法中將一個 action 轉換為一個 endpoint

var builder = new InertEndpointBuilder()
{
DisplayName = action.DisplayName,
RequestDelegate = _requestDelegate,
};
AddActionDataToBuilder(
builder,
routeNames,
action,
routeName: null,
dataTokens: null,
suppressLinkGeneration: false,
suppressPathMatching: false,
conventions,
Array.Empty<Action<EndpointBuilder>>());
endpoints.Add(builder.Build());

接著看一下 _requestDelegate

_requestDelegate = CreateRequestDelegate();

這裏才是真正執行每個 web api 請求的入口

private static RequestDelegate CreateRequestDelegate()
{
// We don't want to close over the Invoker Factory in ActionEndpointFactory as
// that creates cycles in DI. Since we're creating this delegate at startup time
// we don't want to create all of the things we use at runtime until the action
// actually matches.
//
// The request delegate is already a closure here because we close over
// the action descriptor.
IActionInvokerFactory? invokerFactory = null; return (context) =>
{
var endpoint = context.GetEndpoint()!;
var dataTokens = endpoint.Metadata.GetMetadata<IDataTokensMetadata>(); var routeData = new RouteData();
routeData.PushState(router: null, context.Request.RouteValues, new RouteValueDictionary(dataTokens?.DataTokens)); // Don't close over the ActionDescriptor, that's not valid for pages.
var action = endpoint.Metadata.GetMetadata<ActionDescriptor>()!;
var actionContext = new ActionContext(context, routeData, action); if (invokerFactory == null)
{
invokerFactory = context.RequestServices.GetRequiredService<IActionInvokerFactory>();
} var invoker = invokerFactory.CreateInvoker(actionContext);
return invoker!.InvokeAsync();
};
}

首先從 context 獲取 endpoint,接著從 endpoint 得到 ActionDescriptor,再將它封裝成一個 ActionContext

通過 invokerFactory 創建一個 invoker,最後調用 InvokeAsync,所以整個執行過程是一個委托,在執行 MapControllers 的時候已經將委托掛到整個執行的 endpoint

每個路由的 endpoint 最後都是指向同一個地方,全部指向同一個 Delegate,只不過這個 Delegate 從 endpoint 的 Metadata 中拿到的 action 的定義,包括 controller, method, parameter

最後通過 invoker 的形式調用,所以才用到了 ResourceInvoker, PageActionInvoker, 和 ControllerActionInvoker 三種方式,發揮了模板方法作用

建造者

它是將一個複雜的對象分解為多個簡單的對象,然後一步一步構建而成

它將變與不變相分離,即產品的組成部分是不變的,但每一部分是可以靈活選擇的

建造者和模板方法有點類似,一個屬於行為型的設計模式,一個屬於創建型的設計模式

模板方法强調的是行為上面的分解,建造者更加關注創建對象的分解

兩者都是基於一個抽象的類提供抽象方法交給具體的類實現,代碼類似,意義不同

課程鏈接

https://appsqsyiqlk5791.h5.xiaoeknow.com/v1/course/video/v_5f39bdb8e4b01187873136cf?type=2

本作品采用知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議進行許可。

歡迎轉載、使用、重新發布,但務必保留文章署名 鄭子銘 (包含鏈接: http://www.cnblogs.com/MingsonZheng/ ),不得用於商業目的,基於本文修改後的作品務必以相同的許可發布。

如有任何疑問,請與我聯系 ([email protected]) 。

.NET 雲原生架構師訓練營(模板方法 && 建造者)--學習筆記的更多相關文章

  1. .NET 雲原生架構師訓練營(模塊一 架構師與雲原生)--學習筆記

    目錄 什麼是軟件架構 軟件架構的基本思路 單體向分布式演進.雲原生.技術中臺 1.1 什麼是軟件架構 1.1.1 什麼是架構? Software architecture = {Elements, F ...

  2. .NET 雲原生架構師訓練營(建立系統觀)--學習筆記

    目錄 目標 ASP .NET Core 什麼是系統 什麼是系統思維 系統分解 什麼是複雜系統 作業 目標 通過整體定義去認識系統 通過分解去簡化對系統的認識 ASP .NET Core ASP .NE ...

  3. .NET 雲原生架構師訓練營(設計原則&amp;&amp;設計模式)--學習筆記

    目錄 設計原則 設計模式 設計原則 DRY (Don't repeat yourself 不要重複) KISS (Keep it stupid simple 簡單到傻子都能看懂) YAGNI (You ...

  4. .NET 雲原生架構師訓練營(模塊二 基礎鞏固 敏捷開發)--學習筆記

    2.7.1 敏捷開發 敏捷介紹 敏捷的起源 敏捷軟件開發宣言 敏捷開發十二原則 生命周期對比 敏捷開發的特點 敏捷的發展 敏捷的核心 敏捷的起源 2001年,17個老頭子在一起一邊滑雪,一邊討論工作, ...

  5. .NET 雲原生架構師訓練營(設計原則與模式)--學習筆記

    在複雜系統的架構設計中引入設計原則與模式,能够極大降低複雜系統開發.和維護的成本 目錄 幾個問題 為什麼要學習設計模式 優良架構設計的具體指標 理解複雜系統 面向對象思想(指導複雜系統的分析.設計.實 ...

  6. .NET 雲原生架構師訓練營(系統架構)--學習筆記

    目錄 對外展現的功能 內部功能 功能交互與價值通路 系統架構 目標 認識系統的價值通路 認識功能架構,通過把功能結構與形式結構結合來描述系統架構 受益原則 好的架構必須使人受益,要想把架構做好,就要專 ...

  7. .NET 雲原生架構師訓練營(對象過程建模)--學習筆記

    目錄 UML OPM OPM優化 UML 1997年發布UML標准 主要域 視圖 圖 主要概念 結構 靜態視圖 類圖 類.關聯.泛化.依賴關系.實現.接口 用例視圖 用例圖 用例.參與者.關聯.擴展. ...

  8. .NET 雲原生架構師訓練營(ASP .NET Core 整體概念推演)--學習筆記

    演化與完善整體概念 ASP .NET Core 整體概念推演 整體概念推演到具體的形式 ASP .NET Core 整體概念推演 ASP .NET Core 其實就是通過 web framework ...

  9. .NET 雲原生架構師訓練營(模塊二 基礎鞏固 EF Core 更新和遷移)--學習筆記

    2.4.6 EF Core -- 更新 狀態 自動變更檢測 不查詢删除和更新 並發 狀態 Entity State Property State Entity State Added 添加 Uncha ...

  10. .NET 雲原生架構師訓練營(模塊二 基礎鞏固 MongoDB 介紹和基礎)--學習筆記

    2.5.1 MongoDB -- 介紹 mysql vs mongo 快速開始 mysql vs mongo 對比 mysql mongo 數據存儲 table 二維錶結構,需要預先定義結構 json ...

隨機推薦

  1. sdut 487-3279【哈希查找,sscanf ,map】

    487-3279 Time Limit: 2000ms   Memory limit: 65536K  有疑問?點這裏^_^ 題目描述 題目鏈接: sdut:   http://acm.sdut.ed ...

  2. dom事件不求甚解,色解事件捕獲和冒泡

    以前對事件只會用jq的bind綁定一下,腦海裏留著書中的事件循環,一直認為事件就是這兒循環的,最近看園子裏的文章,對事件的了解更模糊了 所以我做了個小實驗,總結一下看的這些零零碎碎的文章,如果總結錯了 ...

  3. Eclipse-將svn上的項目轉化成相應的項目

    這裏假設svn上的項目為maven項目 首先從svn檢出項目 其中項目名稱code可自己定義更改新的名稱 從svn檢出的項目結構 然後將項目轉化成相關的項目 轉換加載中 加載/下載 maven相關內容 ...

  4. 如何將maven項目導入myeclipse中

    在mvn的項目中 pom.xml 文件所在目錄, 運行 mvn eclipse:clean eclipse:eclipse ,會自動將mvn工程轉成eclipse工程, 然後在eclipse中 &qu ...

  5. js 如何獲取文本框中光標索引比特置

    function getTxt1CursorPosition(){ var oTxt1 = document.getElementById("txt1"); var cursurP ...

  6. IOS 應用程序啟動加載過程(從點擊圖標到界面顯示)

    今天幫同事解决問題的時候發現,程序BUG是由加載過程引起的.所以當局部代碼沒有問題,但是程序一運行卻總不是我們想要結果的時候,我們應該想想是不是因為我們忽略了試圖加載過程的原因.下面我們用一個例子來簡 ...

  7. Bzoj 2789: [Poi2012]Letters 樹狀數組,逆序對

    2789: [Poi2012]Letters Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 278  Solved: 185[Submit][Stat ...

  8. 1.2 linear SVM 推導

    1.將公式中的distance具體化 將$w_0$單獨抽出作為$b$,$w=(w_1,...,w_n),x=(x_1,...,x_n)$ 則分割平面為:$w^Tx+b=0$ A.證明w為法向量     ...

  9. 修改User-Agent來偽裝瀏覽器訪問手機站點

    有時候為了測試需要,可能需要使用測試手機wap這樣的站點,如果用真正的手機去測試也可以實現,但是比較麻煩,我們可以通過設置chrome的user agent來偽裝瀏覽器,達到我們的測試目的. 代碼如下 ...

  10. spring boot / cloud (十六) 分布式ID生成服務

    spring boot / cloud (十六) 分布式ID生成服務 在幾乎所有的分布式系統或者采用了分庫/分錶設計的系統中,幾乎都會需要生成數據的唯一標識ID的需求, 常規做法,是使用數據庫中的自動 ...