zoukankan      html  css  js  c++  java
  • AppBuilder(三)BuildInternal

    源码参见Microsoft.Owin.Builder.AppBuilder

    推荐三篇文章,对理解本文内容有帮助。

    Delegate.CreateDelegate Method (Type, Object, MethodInfo) 官方文档

    https://msdn.microsoft.com/en-us/library/74x8f551(v=vs.110).aspx

    c#委托(delegate)揭秘

    http://www.cnblogs.com/50614090/archive/2011/11/14/2248408.html

    C#中delegate的机制原理

    http://blog.csdn.net/argpunk/article/details/42121099

    前文讲到的AppBuilder.Build方法开始pipeline的重建,其实际上是对AppBuilder.BuildInternal的封装,传入参数为typeof(Func<IDictionary<string, object>, Task>),Func的参数为IDictionary<string, object>,返回一个Task,这就是middleware能串起来所要遵循的规范之一,微软工程师将其称为middleware的签名

    先看看NotFound的初始化

     1 private static readonly AppFunc NotFound = new NotFound().Invoke;    //将NotFound.Invoke绑定到AppBuilder.NotFound上
     2 internal class NotFound
     3     {
     4         private static readonly Task Completed = CreateCompletedTask();
     5 
     6         private static Task CreateCompletedTask()
     7         {
     8             var tcs = new TaskCompletionSource<object>();
     9             tcs.SetResult(null);
    10             return tcs.Task;
    11         }
    12 
    13         public Task Invoke(IDictionary<string, object> env)    //这是一个满足AppBuilder中对于AppFunc定义的一个方法,之前在这里老是被绕晕了
    14         {
    15             env["owin.ResponseStatusCode"] = 404;    //设置StatusCode
    16             return Completed;    //返回一个Task
    17         }
    18     }

    上面的代码展示了AppBuilder.NotFound是如何初始化为一个AppFunc的,这是对中间件的签名,对于后面的Convert方法来说至关重要。

     1 private object BuildInternal(Type signature)
     2         {
     3             object app;
     4             if (!_properties.TryGetValue(Constants.BuilderDefaultApp, out app))    //尝试寻找默认的最后一步处理方法,如果寻找失败则将app指向NotFound
     5             {
     6                 app = NotFound;
     7             }
     8 
     9             foreach (var middleware in _middleware.Reverse())    //对List进行反向遍历,反向遍历很重要,这样上一节所说的UseStageMarker对stage.Name的处理方式才能理解
    10             {
    11                 Type neededSignature = middleware.Item1;    //解耦三元组
    12                 Delegate middlewareDelegate = middleware.Item2;
    13                 object[] middlewareArgs = middleware.Item3;
    14 
    15                 app = Convert(neededSignature, app);    //尝试将app的Invoke方法创建为一个委托,委托为needSignature所表示的Type,听起来有点绕,没关系,慢慢来
    16     //这将涉及到pipeline中AppFunc与Middleware的转换,这是OWIN的精华所在
    17                 object[] invokeParameters = new[] { app }.Concat(middlewareArgs).ToArray();    //将app作为第一个参数与args合并
    18                 app = middlewareDelegate.DynamicInvoke(invokeParameters);
    19                 app = Convert(neededSignature, app);    //这一步我也没大懂,到后面懂了再说
    20             }
    21 
    22             return Convert(signature, app);    //同理这一步我也没大懂
    23         }

    从实际例子出发容易理解上面的流程一些,上一章讲到UseCookieAuthentication方法中先调用app.Use(typeof(CookieAuthenticationMiddleware), app, options),再调用app.UseStageMarker(stage),这实际上会调用app.Use(decoupler)方法,而decoulper是一个Func<AppFunc,AppFunc>委托,所以当前进行_middleware.Reverse遍历的时候,最先取到的就是app.Use(decoupler)压进去的委托。

    而参考上上一章对AppBuilder.Use方法的总结,实际上会调用第一种Use处理流程,所以上面源代码中middleware中的三元组对应的类型如下

    Item1

    GetParameterType(an instance of (Func<AppFunc,AppFunc>)),结果为typeof(AppFunc) =  typeof(Func<Idictionary<string, object>, Task>) = a special Delegate,是一个委托

    Item2

    Func<AppFunc,AppFunc> 委托的一个实例,对应decoupler

    Item3

    New object[0] 为空

    所以Convert(neededSignature, app)可以替换成Convert(a special Delegate, an instance of Func<Idictionary<string, object>, Task>)

    来看看Convert做了什么。

     1 private object Convert(Type signature, object app)
     2         {
     3             if (app == null)
     4             {
     5                 return null;
     6             }
     7 
     8             object oneHop = ConvertOneHop(signature, app);
     9             if (oneHop != null)
    10             {
    11                 return oneHop;
    12             }
    13 
    14             object multiHop = ConvertMultiHop(signature, app);
    15             if (multiHop != null)
    16             {
    17                 return multiHop;
    18             }
    19             throw new ArgumentException(
    20                 string.Format(CultureInfo.CurrentCulture, Resources.Exception_NoConversionExists, app.GetType(), signature),
    21                 "signature");
    22 
    23         }

    Covert实际上是对ConvertOneHopConvertMultiHop的封装。

    先看看ConvertOneHop方法。

     1 private object ConvertOneHop(Type signature, object app)
     2         {
     3             if (signature.IsInstanceOfType(app))    //针对上面的例子,app确实是signature的一个实例,都对应Func<Idictionary<string, object>, Task>
     4             {
     5                 return app;    //所以第一次调用会直接返回
     6             }
     7             if (typeof(Delegate).IsAssignableFrom(signature))    //如果signature是对Delegate的继承
     8             {
     9                 Delegate memberDelegate = ToMemberDelegate(signature, app);    //尝试将app的Invoke方法创建为一个signature所表示的Type类型的委托
    10                 if (memberDelegate != null)
    11                 {
    12                     return memberDelegate;
    13                 }
    14             }
    15             foreach (var conversion in _conversions)    //如果app的Invoke方法与signature的Invoke方法冲突,需要进行转换
    16     //这是Middleware与AppFunc之间的重要转换,也是pipeline的重点,留到后文详述
    17             {
    18                 Type returnType = conversion.Key.Item1;
    19                 Type parameterType = conversion.Key.Item2;
    20                 if (parameterType.IsInstanceOfType(app) &&
    21                     signature.IsAssignableFrom(returnType))
    22                 {
    23                     return conversion.Value.DynamicInvoke(app);
    24                 }
    25             }
    26             return null;
    27         }

    再回头看看_middleware.Rerverse遍历的第一次中,Convert(needSignature,app)会很快返回,值就是app,也就是Func<Idictionary<string, object>, Task>的一个实例,再运行app = middlewareDelegate.DynamicInvoke(invokeParameters)的时候,因为app已经合并进invokeParameters中所以,等同于执行

     1     app =>
     2     {
     3                     if (string.Equals(name, stage.Name, StringComparison.OrdinalIgnoreCase))    //name = "Authenticate", stage.Name = "PreHandlerExecute",返回false
     4                     {
     5                         // no decoupling needed when pipeline is already split at this name
     6                         return app ;
     7                     }
     8                     if (!IntegratedPipelineContext.VerifyStageOrder(name, stage.Name))    //name = "Authenticate", stage.Name = "PreHandlerExecute",name < stage.Name,返回false,注意前面有个'!'
     9                     {
    10                         // Stage markers added out of order will be ignored.
    11                         // Out of order stages/middleware may be run earlier than expected.
    12                         // TODO: LOG
    13                         return app ;
    14                     }
    15                     stage.EntryPoint = app ;    //设置PreHandlerExecute这一Stage的EntryPoint为app,此时的app就是NotFound.Invoke方法
    16                     stage = new IntegratedPipelineBlueprintStage    //为Authenticate新建一个IntegratedPipelineBlueprintStage,NextStage绑定到PreHandlerExcute这一Stage上
    17     //所以两个PipelineStage就链接起来了
    18                     {
    19                         Name = name,
    20                         NextStage = stage,
    21                     };
    22                     onStageCreated(stage);    //更新firstStage,使其指向Autenticate这一Stage
    23                     return (AppFunc)IntegratedPipelineContext.ExitPointInvoked;    //返回ExitPointInvoked方法
    24                 };

    上面的代码演示了PreHandlerExcuteAuthenticate两个PipelineStage是如何串接在一起的,再来看看IntegratedPipelineContext.ExitPointInvoked到底干了什么。

     1 public static Task ExitPointInvoked(IDictionary<string, object> env)
     2         {
     3             object value;
     4             if (env.TryGetValue(Constants.IntegratedPipelineContext, out value))    //尝试从environment中获取IntegratedPipelineContext实例,
     5             {
     6                 var self = (IntegratedPipelineContext)value;
     7                 return self._state.ExecutingStage.ExitPointInvoked(env);    //改变当前管道状态,使其可以流入下一管道
     8             }
     9             throw new InvalidOperationException();
    10         }
    11  public Task ExitPointInvoked(IDictionary<string, object> env)
    12         {
    13             _context.PreventNextStage = false;    //改变当前管道状态
    14             return Epilog(env);    //进行最后的收尾工作
    15         }
    16 
    17         private Task Epilog(IDictionary<string, object> env)
    18         {
    19             var tcs = new TaskCompletionSource<object>();
    20             _responseShouldEnd = false;    //开启response,因为即将进行Stage的切换,与Stage刚开始执行的时候关闭response相对应
    21             _context.PushLastObjects(env, tcs);    //验证当前pipeline所在Stage中的environment为null,TaskCompletionSource<object>为null,因为即将离开Stage,而Stage是公用的
    22     //这与IntegratedPipelineContextStage.BeginEvent中的TakeLastEnvironment,TakeLastCompletionSource相对应,都是原子操作
    23             StageAsyncResult result = Interlocked.Exchange(ref _result, null);
    24             if (result != null)
    25             {
    26                 result.TryComplete();
    27             }
    28             return tcs.Task;
    29         }

    扯了好远,在没有进行调试的情况下推断这些运行流程还真是很累的一件事儿。这对于前面没有搞懂的地方有很大帮助,看代码。

    app = middlewareDelegate.DynamicInvoke(invokeParameters)执行之后,app = (AppFunc)IntegratedPipelineContext.ExitPointInvoked了,这就是PreHandlerExecute的收尾工作。

    之后再次执行了app = Convert(neededSignature, app),此时的参数app仍然是一个AppFunc,所以还是会很快返回,进入下一循环。

    这次_middleware.Rerverse遍历获取到的应该是app.Use(typeof(CookieAuthenticationMiddleware), app, options)压进去的CookieAuthenticationMiddleware

    参考AppBuilder(一)那一节所分析的结果,因为传入的参数是一个Typeargs长度为2,所以会采用第四种方法来处理,如下

    private static Tuple<Type, Delegate, object[]> ToConstructorMiddlewareFactory(object middlewareObject, object[] args, ref Delegate middlewareDelegate)

    这个方法尝试寻找middlewareObject类中的参数个数为args长度+1,即是3个的构造函数。以下是对应的构造函数

    public CookieAuthenticationMiddleware(OwinMiddleware next, IAppBuilder app, CookieAuthenticationOptions options) : base(next, options)

    所以可以推断出此时取到的middleware三元组为

    Item1

    OwinMiddlewareType

    Item2

    CookieAuthenticationMiddleware(OwinMiddleware next, IAppBuilder app, CookieAuthenticationOptions options)  构造函数

    Item3

    [IAppBuilder app,  CookieAuthenticationOptions options] 长度为2object[]

    再次执行

    app = Convert(needSignature, app)…>object oneHop = ConvertOneHop(signature, app)

    此时的参数app是一个AppFunc,而signature是一个OwinMiddleware,会用到_conversions,这将是OwinMiddlewareAppFunc之间互相转换的实现,需要用到AppBuilder时候对_conversions初始化的知识,留待下一章再说。

    总结AppBuilder.BuildeInternalmiddlewareList遍历是反向的,虽然现在还不明白为什么如此设计,而且如何在一个PipelineStage中执行多个middleware也还不明朗,曾经以为是使用类似Invoke += Middleware.Invoke实现的,但既然是反向的,这不就顺序反了吗?

    目前能确定下来的时候每个PipelineStageEntryPoint已经显式指定了,刚刚大概又想了一下,为了保证PipelineStage的规范性,那么每个PipelineStage应该都是一个Func<AppFunc, AppFunc>形式的才对,而Middleware应该是被封装在这两个AppFunc之间的,这么说,应该是_conversions来完成了同一个PipelineStage中的Middleware的串联工作了,理应如此。下一节再验证这个问题。

  • 相关阅读:
    编写BinIoDemo.java的Java应用程序,程序完成的功能是:完成1.doc文件的复制,复制以后的文件的名称为自己的学号姓名.doc。
    编写IoDemo.java的Java应用程序,程序完成的功能是:首先读取text.txt文件内容,再通过键盘输入文件的名称为iodemo.txt,把text.txt的内容存入iodemo.txt
    编写TextRw.java的Java应用程序,程序完成的功能是:首先向TextRw.txt中写入自己的学号和姓名,读取TextRw.txt中信息并将其显示在屏幕上。
    事物 视图 与索引
    大家跟我一起涨知识(高级查询与分页)
    涨知识Style
    数据库打印图形
    SQL 编程
    新闻发布系统
    数据库分页储存
  • 原文地址:https://www.cnblogs.com/hmxb/p/5302866.html
Copyright © 2011-2022 走看看