• OwinBuilder源码阅读


    源码参见Microsoft.Owin.Host.SystemWeb.OwinBuilder

    通过前文知道,Build方法将被调用,其做的第一件事儿就是寻找Startup方法

        internal static OwinAppContext Build()
            {
                Action<IAppBuilder> startup = GetAppStartup();
                return Build(startup);
            }

    GetAppStartup方法主要完成从当前Assembly中寻找AppStartup方法,这也是为什么申明Startup,使其被调用有两种方法:

    [assembly: OwinStartup(typeof(XX.Startup))]  //利用OwinStartupAtrribute来指导Startup

    <appSettings>  
      <add key="owin:appStartup" value="StartupDemo.ProductionStartup" />
     </appSettings>
    
    //在webconfig中定义owin:appStartup键值对,以下将对负责搜索Startup方法的DefaultLoader的源码进行分析,来了解如何定位Startup方法的
        internal static Action<IAppBuilder> GetAppStartup()
            {
                string appStartup = ConfigurationManager.AppSettings[Constants.OwinAppStartup];
                var loader = new DefaultLoader(new ReferencedAssembliesWrapper());
                IList<string> errors = new List<string>();
                Action<IAppBuilder> startup = loader.Load(appStartup ?? string.Empty, errors);
    if (startup == null) { throw new EntryPointNotFoundException(Resources.Exception_AppLoderFailure + Environment.NewLine + " - " + string.Join(Environment.NewLine + " - ", errors) + (IsAutomaticAppStartupEnabled ? Environment.NewLine + Resources.Exception_HowToDisableAutoAppStartup : string.Empty) + Environment.NewLine + Resources.Exception_HowToSpecifyAppStartup); } return startup; }

    上面源码展示了调用DefaultLoaderLoad方法来搜索Startup,而Startup是一个Action<IAppBuilder>方法,即接受一个实现了IAppBuilder接口的实例作为参数,返回值为voidAction

        public Action<IAppBuilder> Load(string startupName, IList<string> errorDetails)
            {
                return LoadImplementation(startupName, errorDetails) ?? _next(startupName, errorDetails);
            }

    Load方法实际上是对LoadImplementation的一个封装,如果寻找失败则使用_next进行寻找(实际上这会返回null,这不是重点)

     1 private Action<IAppBuilder> LoadImplementation(string startupName, IList<string> errorDetails)
     2         {
     3             Tuple<Type, string> typeAndMethod = null;
     4             startupName = startupName ?? string.Empty;
     5             // Auto-discovery or Friendly name?
     6             if (!startupName.Contains(','))
     7             {
     8                 typeAndMethod = GetDefaultConfiguration(startupName, errorDetails);    //通常会进入这一流程,如果startupName中包含逗号,则对应另一种申明方式
     9             }
    10 
    11             if (typeAndMethod == null && !string.IsNullOrWhiteSpace(startupName))    //这种申明方式为StartupName = “startupName,assemblyName”
    12             {
    13                 typeAndMethod = GetTypeAndMethodNameForConfigurationString(startupName, errorDetails);    //对startupName和assemblyName进行分离,并找到对应的assembly加载
    14                                                                 //其中的startupName
    15             }
    16 
    17             if (typeAndMethod == null)
    18             {
    19                 return null;
    20             }
    21 
    22             Type type = typeAndMethod.Item1;
    23             // default to the "Configuration" method if only the type name was provided    //如果只提供了startup的type,则默认调用其中的Configuration方法
    24             string methodName = !string.IsNullOrWhiteSpace(typeAndMethod.Item2) ? typeAndMethod.Item2 : Constants.Configuration;
    25 
    26             Action<IAppBuilder> startup = MakeDelegate(type, methodName, errorDetails);    //直接调用startup方法或者做为一个middleware压入List中,后文会讲到具体实现
    27             if (startup == null)
    28             {
    29                 return null;
    30             }
    31 
    32             return builder =>    //再对startup进行一次delegate封装,传入参数为builder,供上层调用
    33             {
    34                 if (builder == null)
    35                 {
    36                     throw new ArgumentNullException("builder");
    37                 }
    38 
    39                 object value;
    40                 if (!builder.Properties.TryGetValue(Constants.HostAppName, out value) ||
    41                     String.IsNullOrWhiteSpace(Convert.ToString(value, CultureInfo.InvariantCulture)))
    42                 {
    43                     builder.Properties[Constants.HostAppName] = type.FullName;    //获取并记录HostAppName
    44                 }
    45                 startup(builder);    //开始构造
    46             };
    47         }

    由于参数startupName为最初定义的常量,其值为Constants.OwinAppStartup = "owin:AppStartup";所以很明显会调用GetDefaultConfiguration(startupName, errorDetails)方法进一步处理。

        private Tuple<Type, string> GetDefaultConfiguration(string friendlyName, IList<string> errors)
            {
                friendlyName = friendlyName ?? string.Empty;
                bool conflict = false;
                Tuple<Type, string> result = SearchForStartupAttribute(friendlyName, errors, ref conflict);
    
                if (result == null && !conflict && string.IsNullOrEmpty(friendlyName))
                {
                    result = SearchForStartupConvention(errors);
                }
    
                return result;
            }

    这个方法又是对SearchForStartupAttribute的一个封装

    先了解一下OwinStartupAttribute

    看上文使用到的构造函数

        public OwinStartupAttribute(Type startupType)
                : this(string.Empty, startupType, string.Empty)
            {
            }
        public OwinStartupAttribute(string friendlyName, Type startupType, string methodName)
            {
                if (friendlyName == null)
                {
                    throw new ArgumentNullException("friendlyName");
                }
                if (startupType == null)
                {
                    throw new ArgumentNullException("startupType");
                }
                if (methodName == null)
                {
                    throw new ArgumentNullException("methodName");
                }
    
                FriendlyName = friendlyName;
                StartupType = startupType;
                MethodName = methodName;
            }

    这里默认将FriendlyNameMethodName设置为空,即只记录了Startup类的Type,下面的SearchForStartupAttribute主要也是通过寻找OwinStartupAttribute中的StartupType 来获取Startup的。

     1 private Tuple<Type, string> SearchForStartupAttribute(string friendlyName, IList<string> errors, ref bool conflict)
     2         {
     3             friendlyName = friendlyName ?? string.Empty;
     4             bool foundAnyInstances = false;
     5             Tuple<Type, string> fullMatch = null;
     6             Assembly matchedAssembly = null;
     7             foreach (var assembly in _referencedAssemblies)    // 遍历程序集
     8             {
     9                 object[] attributes;
    10                 try
    11                 {
    12                     attributes = assembly.GetCustomAttributes(inherit: false);    // 获取程序集的所有自定义Attribute
    13                 }
    14                 catch (CustomAttributeFormatException)
    15                 {
    16                     continue;
    17                 }
    18 
    19                 foreach (var owinStartupAttribute in attributes.Where(attribute => attribute.GetType().Name.Equals(Constants.OwinStartupAttribute, StringComparison.Ordinal)))    // 对获取到的Attribute进行过滤,只遍历OwinStartupAttribute,即是优先会 //对上文所说的第一种 Startup申明进行调用
    20                 {
    21                     Type attributeType = owinStartupAttribute.GetType();    //采用反射机制,先获取Type
    22                     foundAnyInstances = true;
    23 
    24                     // Find the StartupType property.
    25                     PropertyInfo startupTypeProperty = attributeType.GetProperty(Constants.StartupType, typeof(Type));    //寻找属性名是StartupType,属性类型是Type的属性
    26                     if (startupTypeProperty == null)    //寻找失败,记录错误
    27                     {
    28                         errors.Add(string.Format(CultureInfo.CurrentCulture, LoaderResources.StartupTypePropertyMissing,
    29                             attributeType.AssemblyQualifiedName, assembly.FullName));
    30                         continue;
    31                     }
    32 
    33                     var startupType = startupTypeProperty.GetValue(owinStartupAttribute, null) as Type;    //获取StartupType属性的值,并转换为Type,为反射做准备
    34                     if (startupType == null)    //获取或者转换失败,记录错误
    35                     {
    36                         errors.Add(string.Format(CultureInfo.CurrentCulture, LoaderResources.StartupTypePropertyEmpty, assembly.FullName));
    37                         continue;
    38                     }
    39 
    40                     // FriendlyName is an optional property.
    41                     string friendlyNameValue = string.Empty;    //FriendlyName是可选项,作为对Startup类的别称,不是重点
    42                     PropertyInfo friendlyNameProperty = attributeType.GetProperty(Constants.FriendlyName, typeof(string));
    43                     if (friendlyNameProperty != null)
    44                     {
    45                         friendlyNameValue = friendlyNameProperty.GetValue(owinStartupAttribute, null) as string ?? string.Empty;
    46                     }
    47 
    48                     if (!string.Equals(friendlyName, friendlyNameValue, StringComparison.OrdinalIgnoreCase))    //如果未定义FriendlyName则默认是Empty,否则记录错误
    49                     {
    50                         errors.Add(string.Format(CultureInfo.CurrentCulture, LoaderResources.FriendlyNameMismatch,
    51                             friendlyNameValue, friendlyName, assembly.FullName));
    52                         continue;
    53                     }
    54 
    55                     // MethodName is an optional property.
    56                     string methodName = string.Empty;    同理MethodName也是可选项,如果为定义默认是Empty
    57                     PropertyInfo methodNameProperty = attributeType.GetProperty(Constants.MethodName, typeof(string));
    58                     if (methodNameProperty != null)
    59                     {
    60                         methodName = methodNameProperty.GetValue(owinStartupAttribute, null) as string ?? string.Empty;
    61                     }
    62 
    63                     if (fullMatch != null)    //表明已经寻找到一个Startup类,则冲突了,说明有重复申明Startup类
    64                     {
    65                         conflict = true;
    66                         errors.Add(string.Format(CultureInfo.CurrentCulture,
    67                             LoaderResources.Exception_AttributeNameConflict,
    68                             matchedAssembly.GetName().Name, fullMatch.Item1, assembly.GetName().Name, startupType, friendlyName));
    69                     }
    70                     else    //尚未寻找到Startup类,将StartupType和MethodName存为二元组,记录程序集
    71                     {
    72                         fullMatch = new Tuple<Type, string>(startupType, methodName);
    73                         matchedAssembly = assembly;
    74                     }
    75                 }
    76             }
    77 
    78             if (!foundAnyInstances)    //未寻找到申明Startup的程序集,记录错误
    79             {
    80                 errors.Add(LoaderResources.NoOwinStartupAttribute);
    81             }
    82             if (conflict)    //如果有冲突,返回null
    83             {
    84                 return null;
    85             }
    86             return fullMatch;    //返回结果
    87         }

    前文讲到MakeDelegate(Type type, string methodName, IList<string> errors)主要作用是将寻找到的startup方法作为一个middleware压入List中,看其源码

     1 private Action<IAppBuilder> MakeDelegate(Type type, string methodName, IList<string> errors)
     2         {
     3             MethodInfo partialMatch = null;
     4             foreach (var methodInfo in type.GetMethods())
     5             {
     6                 if (!methodInfo.Name.Equals(methodName))
     7                 {
     8                     continue;
     9                 }
    10 
    11                 // void Configuration(IAppBuilder app)    //检测Startup类中的Configuration方法的参数和返回值,这种为默认的方法,也是新建MVC时默认的方法
    12                 if (Matches(methodInfo, false, typeof(IAppBuilder)))    //方法无返回值(void),参数为(IAppBuilder)
    13                 {
    14                     object instance = methodInfo.IsStatic ? null : _activator(type);    //如果为静态方法,则不需要实例,否则实例化一个Startup对象
    15                     return builder => methodInfo.Invoke(instance, new[] { builder });    //返回一个Lambda形式的delegate,实际上就是调用Startup的Configuration(IAppBuilder)方法
    16                 }
    17 
    18                 // object Configuration(IDictionary<string, object> appProperties)    //另一种Configuration方法,参数为Environment,返回object
    19                 if (Matches(methodInfo, true, typeof(IDictionary<string, object>)))
    20                 {
    21                     object instance = methodInfo.IsStatic ? null : _activator(type);    //由于传入参数为Dictionary,所以将这个Configuration方法压入middleware的List中
    22                     return builder => builder.Use(new Func<object, object>(_ => methodInfo.Invoke(instance, new object[] { builder.Properties })));
    23                 }    //builder.Use传入参数是一个Func<object,object>的delegate,实际上就是一个middleware,不过因为在初始化阶段,所以不需要进入下一个stage
    24 
    25                 // object Configuration()    //无参数,返回object
    26                 if (Matches(methodInfo, true))
    27                 {
    28                     object instance = methodInfo.IsStatic ? null : _activator(type);
    29                     return builder => builder.Use(new Func<object, object>(_ => methodInfo.Invoke(instance, new object[0])));
    30                 }
    31 
    32                 partialMatch = partialMatch ?? methodInfo;    //记录找到但不符合三种定义的Configuration方法
    33             }
    34 
    35             if (partialMatch == null)    //未找到的Configuration,记录错误
    36             {
    37                 errors.Add(string.Format(CultureInfo.CurrentCulture,
    38                     LoaderResources.MethodNotFoundInClass, methodName, type.AssemblyQualifiedName));
    39             }
    40             else    找到Configuration,但不符合三种定义,记录错误
    41             {
    42                 errors.Add(string.Format(CultureInfo.CurrentCulture, LoaderResources.UnexpectedMethodSignature,
    43                     methodName, type.AssemblyQualifiedName));
    44             }
    45             return null;
    46         }

    总结:OwinBuilder主要完成对Startup类的寻找,并调用其中的Configuration方法,Configuration有三种签名(传入参数与返回结果),将其封装成一个方法返回给上层,供上层调用。接下来就是最重要的工作,调用Startup中的Configuration具体做了什么,每个middleware是如何注入到pipeline中的,这就是AppBuilder主要做的工作了。

  • 相关阅读:
    Android入门:监听ContentProvider数据改变
    Android入门:ListView(SimpleCursorAdapter实现)
    Android入门:ContentProvider
    Android入门:ListView(继承BaseAdapter实现)
    随机生成字符串实现
    很惊讶douban.com是用python语言的一个框架写的
    简单的总是好的,在这个复杂的世界: java simple log
    Quixtoe比PHP更简单吗?
    python 与 ruby (ruby学习资源大全)
    Web的未来:语义网
  • 原文地址:https://www.cnblogs.com/hmxb/p/5297772.html
Copyright © 2020-2023  润新知