• .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) 。

  • 相关阅读:
    adobe acrobat 无效批注对象
    分享下今天研究的流量上限DDos攻击分析和解决方式
    【二】【HTML列表、表格与框架】
    大话计算机中的流水作业
    texinfo
    texindex
    texi2dvi
    tex, virtex, initex
    testprns printername [printcapname]
    testparm
  • 原文地址:https://www.cnblogs.com/MingsonZheng/p/15777078.html
Copyright © 2020-2023  润新知