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主要做的工作了。

 

posted @ 2016-03-20 14:46  幻暝玄冰  阅读(992)  评论(0编辑  收藏  举报