.NET 云原生架构师训练营(模板方法 && 建造者)--学习笔记
目录
- 模板方法
- 源码
- 建造者
模板方法
定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤
源码
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/ ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。
如有任何疑问,请与我联系 (MingsonZheng@outlook.com) 。
.NET 云原生架构师训练营(模板方法 && 建造者)--学习笔记的更多相关文章
- .NET 云原生架构师训练营(模块一 架构师与云原生)--学习笔记
目录 什么是软件架构 软件架构的基本思路 单体向分布式演进.云原生.技术中台 1.1 什么是软件架构 1.1.1 什么是架构? Software architecture = {Elements, F ...
- .NET 云原生架构师训练营(建立系统观)--学习笔记
目录 目标 ASP .NET Core 什么是系统 什么是系统思维 系统分解 什么是复杂系统 作业 目标 通过整体定义去认识系统 通过分解去简化对系统的认识 ASP .NET Core ASP .NE ...
- .NET 云原生架构师训练营(权限系统 RGCA 架构设计)--学习笔记
目录 项目核心内容 实战目标 RGCA 四步架构法 项目核心内容 无代码埋点实现对所有 API Action 访问控制管理 对 EF Core 实体新增.删除.字段级读写控制管理 与 Identity ...
- .NET 云原生架构师训练营(设计原则&&设计模式)--学习笔记
目录 设计原则 设计模式 设计原则 DRY (Don't repeat yourself 不要重复) KISS (Keep it stupid simple 简单到傻子都能看懂) YAGNI (You ...
- .NET 云原生架构师训练营(模块二 基础巩固 敏捷开发)--学习笔记
2.7.1 敏捷开发 敏捷介绍 敏捷的起源 敏捷软件开发宣言 敏捷开发十二原则 生命周期对比 敏捷开发的特点 敏捷的发展 敏捷的核心 敏捷的起源 2001年,17个老头子在一起一边滑雪,一边讨论工作, ...
- .NET 云原生架构师训练营(设计原则与模式)--学习笔记
在复杂系统的架构设计中引入设计原则与模式,能够极大降低复杂系统开发.和维护的成本 目录 几个问题 为什么要学习设计模式 优良架构设计的具体指标 理解复杂系统 面向对象思想(指导复杂系统的分析.设计.实 ...
- .NET 云原生架构师训练营(系统架构)--学习笔记
目录 对外展现的功能 内部功能 功能交互与价值通路 系统架构 目标 认识系统的价值通路 认识功能架构,通过把功能结构与形式结构结合来描述系统架构 受益原则 好的架构必须使人受益,要想把架构做好,就要专 ...
- .NET 云原生架构师训练营(对象过程建模)--学习笔记
目录 UML OPM OPM优化 UML 1997年发布UML标准 主要域 视图 图 主要概念 结构 静态视图 类图 类.关联.泛化.依赖关系.实现.接口 用例视图 用例图 用例.参与者.关联.扩展. ...
- .NET 云原生架构师训练营(ASP .NET Core 整体概念推演)--学习笔记
演化与完善整体概念 ASP .NET Core 整体概念推演 整体概念推演到具体的形式 ASP .NET Core 整体概念推演 ASP .NET Core 其实就是通过 web framework ...
随机推荐
- mybatis处理集合、数组参数使用in查询等语句的两种方法
对于mybatis的参数类型是集合数组的时候进行查询. 第一种:参数list使用mybatis的标签 SELECT * FROM TABLE_NAME AS a <where> <i ...
- js和jquery之间的转换
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...
- 35、搜索插入位置 | 算法(leetode,附思维导图 + 全部解法)300题
零 标题:算法(leetode,附思维导图 + 全部解法)300题之(35)搜索插入位置 一 题目描述 二 解法总览(思维导图) 三 全部解法 1 方案1 1)代码: // 方案1 "无视要 ...
- C#ADO.NET技术总结
[ADO.NET的主要组件-..NET framework数据提供程序和DataSet(数据集)] 一.DataSet数据集负责对数据库执行命令. 二.NET framework数据提供程序的四个核心 ...
- C++STL标准库学习笔记(四)multiset续
自定义排序规则的multiset用法 前言: 在这个笔记中,我把大多数代码都加了注释,我的一些想法和注解用蓝色字体标记了出来,重点和需要关注的地方用红色字体标记了出来,只不过这一次的笔记主要是我的补充 ...
- 什么是内容分发CDN
一.简介 CDN全称:Content Delivery Network或Content Ddistribute Network,即内容分发网络 基本思路: 尽可能避开互联网上有可能影响数据传输速度和稳 ...
- Nginx状态码和日志
目录 一.Nginx状态返回码 二.Nginx日志统计 一.Nginx状态返回码 http返回状态码(Status-Code), 以3位数字组成 200 成功 301 永久重定向(redirect) ...
- Django中的常用字段类型与参数
Django中的常用字段类型 1. 数值型 以下都是数值相关的,比如AutoField,它在MySQL中的类型为int(11),而BooleanField在MySQL中对应的类型是tinyint(1) ...
- JAVA日期Date格式转corn表达式
date转corn 定时任务获取corn /*** * 日期转corn表达式 * @param date 日期 * @return */ public static String getCron(Da ...
- SpringBoot 整合MinIO
引入依赖 <dependency> <groupId>io.minio</groupId> <artifactId>minio</artifact ...