.netcore入门13:aspnetcore源码之如何在程序启动时将Controller里的Action自动扫描封装成Endpoint

环境:

  • .netcore 3.1.1
  • vs2019 16.4.5

建议先阅读:.netcore入门16:aspnetcore之终结点路由工作原理

一、探索目的说明

我们知道在程序启动时,程序就会扫描所有相关程序集中Controller的Action,然后将他们封装成一个个的Endpoint交给“路由”模块去管理,这样在http请求到来时“路由”模块才能寻找到正确的Endpoint,进而找到Action并调用执行。这里我们正是要探索“程序在启动时如何扫描Action并封装成Endpoint”的。

二、关键对象介绍

services.AddControllers()```涉及到的关键对象:

  • ApplicationPart
    这个对象代表了程序的一部分资源,它内部包含一个Assembly对象,在这个Assembly对象(也就是dll动态库)中可以定义很多Controller。
    当程序启动时会将生成的所有dll动态库都封装成单独的ApplicationPart,待到endpoints.Mapcontrollers()执行时就会遍历这些dll里的Controller,进而将里面的Action方法封装成EndPoint
  • ApplicationPartManager
    这个对象负责管理所有的ApplicationPart,同时它本身被包含在MvcCoreBuilderMvcBuilder中。
  • MvcCoreBuilderMvcBuilder
    它们都是用来构建mvc服务的,不同的是MvcCoreBuilder一般出现在mvc框架源码中,而MvcBuilder一般出现在用户的代码中。
  • ControllerFeatureProvider
    这个provider可以从ApplicationPart中提取出所有的Controller。

endpoints.MapControllers()涉及到的关键对象:

  • EndpointDataSource
    这是个抽象类,表示一类EndPoint数据源,它的子类ActionEndpointDataSourceBaseControllerActionEndpointDataSource将数据源具化到了Controller里的Action
  • IEndpointRouteBuilder
    表示给路由构建Endpoint的对象,它的实现类ControllerActionEndpointConventionBuilder就是endpoints.MapControllers()返回的实例类
  • ActionDescriptor
    它表示一个Action的描述对象,框架先遍历找到Controller的Action生成ActionDescriptor,然后再进一步生成EndPoint
  • IActionDescriptorProvider
    表示ActionDescriptor的提供者

三、services.AddControllers()都做了什么?

3.1 概述:

这个方法是在程序启动时遍历所有的相关程序集,将它们封装成一个个的AssemblyPart(继承自ApplciationPart)

3.2 具体执行过程分析:

  1. 我们直接来看services.AddControllers();的源码:MvcServiceCollectionExtensions.cs
namespace Microsoft.Extensions.DependencyInjection
{
    public static class MvcServiceCollectionExtensions
    {
        ......
        public static IMvcBuilder AddControllers(this IServiceCollection services)
        {
            if (services == null)
            {
                throw new ArgumentNullException(nameof(services));
            }
			//从这里跳进去
            var builder = AddControllersCore(services);
            return new MvcBuilder(builder.Services, builder.PartManager);
        }
        private static IMvcCoreBuilder AddControllersCore(IServiceCollection services)
        {
            // This method excludes all of the view-related services by default.
            return services
            	//从这里跳进去
                .AddMvcCore()
                .AddApiExplorer()
                .AddAuthorization()
                .AddCors()
                .AddDataAnnotations()
                .AddFormatterMappings();
        }
        ......
    }
}

  1. 看到上面的代码,我们要知道AddMvcCore()是核心(这个方法返回的是IMvcCoreBuilder,其后的调用都是基于IMvcCoreBuilder的),其后的调用都是在引入关联到的一些服务,那么直接看AddMvcCore()的源码:
namespace Microsoft.Extensions.DependencyInjection
{
   public static class MvcCoreServiceCollectionExtensions
   {
       ......
       public static IMvcCoreBuilder AddMvcCore(this IServiceCollection services)
       {
           if (services == null)
           {
               throw new ArgumentNullException(nameof(services));
           }
   		   //从这里跳进去(获取已经包含所有关联到的程序集的ApplicationPartManager)
           var partManager = GetApplicationPartManager(services);
           services.TryAddSingleton(partManager);
   		   //给这个ApplicationPartManager添加ControllerFeature
           ConfigureDefaultFeatureProviders(partManager);
           //就是调用了services.AddRouting();
           ConfigureDefaultServices(services);
           //在这里添加mvc核心服务到容器中
           AddMvcCoreServices(services);

           var builder = new MvcCoreBuilder(services, partManager);

           return builder;
       }

       private static void ConfigureDefaultFeatureProviders(ApplicationPartManager manager)
       {
           if (!manager.FeatureProviders.OfType<ControllerFeatureProvider>().Any())
           {
               manager.FeatureProviders.Add(new ControllerFeatureProvider());
           }
       }

       private static ApplicationPartManager GetApplicationPartManager(IServiceCollection services)
       {
           var manager = GetServiceFromCollection<ApplicationPartManager>(services);
           if (manager == null)
           {
               manager = new ApplicationPartManager();

               var environment = GetServiceFromCollection<IWebHostEnvironment>(services);
               var entryAssemblyName = environment?.ApplicationName;
               if (string.IsNullOrEmpty(entryAssemblyName))
               {
                   return manager;
               }
               //从这里跳进去 
               manager.PopulateDefaultParts(entryAssemblyName);
           }
           return manager;
       }

       private static T GetServiceFromCollection<T>(IServiceCollection services)
       {
           return (T)services
               .LastOrDefault(d => d.ServiceType == typeof(T))
               ?.ImplementationInstance;
       }
       ......
   }
}

上面代码中我们看到AddMvcCore方法中先获取到ApplicationPartManager,并且立刻将当前程序集封装成了ApplicationPart放进partManager中(看代码:manager.PopulateDefaultParts(entryAssemblyName);),注意:这里的entryAssemblyName就是获取到的当前程序集的名称,那么继续看代码:ApplicationPartManager.PopulateDefaultParts(entryAssemblyName)

namespace Microsoft.AspNetCore.Mvc.ApplicationParts
{
    public class ApplicationPartManager
    {
        public IList<IApplicationFeatureProvider> FeatureProviders { get; } =
            new List<IApplicationFeatureProvider>();
        public IList<ApplicationPart> ApplicationParts { get; } = new List<ApplicationPart>();
        public void PopulateFeature<TFeature>(TFeature feature)
        {
            if (feature == null)
            {
                throw new ArgumentNullException(nameof(feature));
            }

            foreach (var provider in FeatureProviders.OfType<IApplicationFeatureProvider<TFeature>>())
            {
                provider.PopulateFeature(ApplicationParts, feature);
            }
        }

        internal void PopulateDefaultParts(string entryAssemblyName)
        {
            //获取所有关联到的程序集
            var assemblies = GetApplicationPartAssemblies(entryAssemblyName);
            var seenAssemblies = new HashSet<Assembly>();
            foreach (var assembly in assemblies)
            {
                if (!seenAssemblies.Add(assembly)) continue;
                var partFactory = ApplicationPartFactory.GetApplicationPartFactory(assembly);
                foreach (var applicationPart in partFactory.GetApplicationParts(assembly))
                {
                    ApplicationParts.Add(applicationPart);
                }
            }
        }

        private static IEnumerable<Assembly> GetApplicationPartAssemblies(string entryAssemblyName)
        {
            var entryAssembly = Assembly.Load(new AssemblyName(entryAssemblyName));            
            var assembliesFromAttributes = entryAssembly.GetCustomAttributes<ApplicationPartAttribute>()
                .Select(name => Assembly.Load(name.AssemblyName))
                .OrderBy(assembly => assembly.FullName, StringComparer.Ordinal)
                .SelectMany(GetAsemblyClosure);
            return GetAsemblyClosure(entryAssembly)
                .Concat(assembliesFromAttributes);
        }

        private static IEnumerable<Assembly> GetAsemblyClosure(Assembly assembly)
        {
            yield return assembly;
            var relatedAssemblies = RelatedAssemblyAttribute.GetRelatedAssemblies(assembly, throwOnError: false)
                .OrderBy(assembly => assembly.FullName, StringComparer.Ordinal);

            foreach (var relatedAssembly in relatedAssemblies)
            {
                yield return relatedAssembly;
            }
        }
    }
}

从上面的代码中看出,PopulateDefaultParts()方法从当前程序集找到所有引用到了的程序集(包括[assembly:ApplicationPart(“demo”)]中标记的)把他们封装成ApplciationPart(其实也就是AssemblyPart,具体可以看DefaultApplicationPartFactory.cs),然后把他们放在了ApplciationPartManager的ApplicationParts属性中。
那么分析到这,可以返回AddMvcCore()方法了,在这个方法向下看有一个ConfigureDefaultFeatureProviders(partManager);的调用,这行代码是创建了一个新的ControllerFeatureProvider实例放进了partManager的FeatureProviders属性中,注意这个ControllerFeatureProvider对象在后面遍历ApplicationPart的时候负责找出里面的Controller。
接着看AddMvcCore()方法的后面几行,他们的意图很明显:引入Routing服务;引入Mvc核心服务;构建一个MvcCoreBuilder并返回;
再接着,代码就返回到了services.AddControllers()中,在AddControllers()中利用返回的MvcCoreBuilder对象调用MvcCoreBuilder.AddApiExplorer();MvcCoreBuilder.AddAuthorization()等一系列方法逐渐引入mvc中需要的其他服务。
至此,services.AddControllers()就执行完了,可以看到它的主要作用就是扫描所有的有关程序集封装成ApplicationPart!

3.3 附:ApplicationPartFactory、AssemblyPart 和 DefaultApplicationPartFactory源码**

AssemblyPart:

namespace Microsoft.AspNetCore.Mvc.ApplicationParts
{
    public class AssemblyPart : ApplicationPart, IApplicationPartTypeProvider
    {
        public AssemblyPart(Assembly assembly)
        {
            Assembly = assembly ?? throw new ArgumentNullException(nameof(assembly));
        }
        public Assembly Assembly { get; }
        public override string Name => Assembly.GetName().Name;
        public IEnumerable<TypeInfo> Types => Assembly.DefinedTypes
    }
}

DefaultApplicationPartFactory:

namespace Microsoft.AspNetCore.Mvc.ApplicationParts
{
    public class DefaultApplicationPartFactory : ApplicationPartFactory
    {
        public static DefaultApplicationPartFactory Instance { get; } = new DefaultApplicationPartFactory();
        public static IEnumerable<ApplicationPart> GetDefaultApplicationParts(Assembly assembly)
        {
            if (assembly == null)
            {
                throw new ArgumentNullException(nameof(assembly));
            }

            yield return new AssemblyPart(assembly);
        }
        public override IEnumerable<ApplicationPart> GetApplicationParts(Assembly assembly)
        {
            return GetDefaultApplicationParts(assembly);
        }
    }
}

ApplicationPartFactory:

namespace Microsoft.AspNetCore.Mvc.ApplicationParts
{
    public abstract class ApplicationPartFactory
    {
        public abstract IEnumerable<ApplicationPart> GetApplicationParts(Assembly assembly);
        public static ApplicationPartFactory GetApplicationPartFactory(Assembly assembly)
        {
            if (assembly == null)
            {
                throw new ArgumentNullException(nameof(assembly));
            }

            var provideAttribute = assembly.GetCustomAttribute<ProvideApplicationPartFactoryAttribute>();
            if (provideAttribute == null)
            {
                return DefaultApplicationPartFactory.Instance;
            }

            var type = provideAttribute.GetFactoryType();
            if (!typeof(ApplicationPartFactory).IsAssignableFrom(type))
            {
                throw new InvalidOperationException(Resources.FormatApplicationPartFactory_InvalidFactoryType(
                    type,
                    nameof(ProvideApplicationPartFactoryAttribute),
                    typeof(ApplicationPartFactory)));
            }

            return (ApplicationPartFactory)Activator.CreateInstance(type);
        }
    }
}

四、endpoints.MapControllers()都做了什么

4.1 概述

这个方法当然是遍历已经收集到的ApplicationPart(也就是AssemblyPart),找出其中的所有Action封装成EndPoint对象然后交给Routing模块了!

4.2 具体执行过程分析

我们直接看endpoints.MapControllers()的源码:

namespace Microsoft.AspNetCore.Builder
{
    public static class ControllerEndpointRouteBuilderExtensions
    {
        ......
        public static ControllerActionEndpointConventionBuilder MapControllers(this IEndpointRouteBuilder endpoints)
        {
            if (endpoints == null)
            {
                throw new ArgumentNullException(nameof(endpoints));
            }

            EnsureControllerServices(endpoints);
            //跳进去查看:1
            return GetOrCreateDataSource(endpoints).DefaultBuilder;
        }

        private static void EnsureControllerServices(IEndpointRouteBuilder endpoints)
        {
            var marker = endpoints.ServiceProvider.GetService<MvcMarkerService>();
            if (marker == null)
            {
                throw new InvalidOperationException(Resources.FormatUnableToFindServices(
                    nameof(IServiceCollection),
                    "AddControllers",
                    "ConfigureServices(...)"));
            }
        }

        private static ControllerActionEndpointDataSource GetOrCreateDataSource(IEndpointRouteBuilder endpoints)
        {
            var dataSource = endpoints.DataSources.OfType<ControllerActionEndpointDataSource>().FirstOrDefault();
            if (dataSource == null)
            {
                //跳进去查看:2
                dataSource = endpoints.ServiceProvider.GetRequiredService<ControllerActionEndpointDataSource>();
                endpoints.DataSources.Add(dataSource);
            }

            return dataSource;
        }
        ......
    }
}

从上面的代码中我们观察到return GetOrCreateDataSource(endpoints).DefaultBuilder;这一行代码,我们要从这一行代码跳进去查看。当我们跳进去后发现有这么一行代码.GetRequiredService<ControllerActionEndpointDataSource>(),这行代码执行完后就获取到了datesource,而这个datasource里面就是所有的action。 s所以我们需要知道ControllerActionEndpointDataSource是怎么被创建的?我们回到services.AddMvcCore()方法体中可以看到如下:

namespace Microsoft.Extensions.DependencyInjection
{
    public static class MvcCoreServiceCollectionExtensions
    {
        public static IMvcCoreBuilder AddMvcCore(this IServiceCollection services)
        {
            ......
            AddMvcCoreServices(services);
            ......
        }
        internal static void AddMvcCoreServices(IServiceCollection services)
        {
            //
            // Endpoint Routing / Endpoints
            //
            services.TryAddSingleton<ControllerActionEndpointDataSource>();
            ......
        }
    }
}

所以我们直接查看ControllerActionEndpointDataSource的构造函数即可:

namespace Microsoft.AspNetCore.Mvc.Routing
{
    internal class ControllerActionEndpointDataSource : ActionEndpointDataSourceBase
    {
        private readonly ActionEndpointFactory _endpointFactory;
        private readonly List<ConventionalRouteEntry> _routes;
        private int _order;
        public ControllerActionEndpointDataSource(
            IActionDescriptorCollectionProvider actions,
            ActionEndpointFactory endpointFactory)
            : base(actions)
        {
            _endpointFactory = endpointFactory;
            _routes = new List<ConventionalRouteEntry>();
            _order = 1;
            DefaultBuilder = new ControllerActionEndpointConventionBuilder(Lock, Conventions);
            //跳进去查看(在父类里面定义)
            Subscribe();
        }
        protected override List<Endpoint> CreateEndpoints(IReadOnlyList<ActionDescriptor> actions, IReadOnlyList<Action<EndpointBuilder>> conventions)
        {
            var endpoints = new List<Endpoint>();
            var keys = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
            
            var routeNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
            
            for (var i = 0; i < actions.Count; i++)
            {
                if (actions[i] is ControllerActionDescriptor action)
                {
                    _endpointFactory.AddEndpoints(endpoints, routeNames, action, _routes, conventions, CreateInertEndpoints);

                    if (_routes.Count > 0)
                    {
                    
                        foreach (var kvp in action.RouteValues)
                        {
                            keys.Add(kvp.Key);
                        }
                    }
                }
            }
            
            for (var i = 0; i < _routes.Count; i++)
            {
                var route = _routes[i];
                _endpointFactory.AddConventionalLinkGenerationRoute(endpoints, routeNames, keys, route, conventions);
            }

            return endpoints;
        }
    }
}

从上面的构造函数中我们发现最后有一行Subscribe(),那么扫描的代码应该在这里,不过这个方法是父类ActionEndpointDataSourceBase的,我们找到父类的代码如下:

namespace Microsoft.AspNetCore.Mvc.Routing
{
    internal abstract class ActionEndpointDataSourceBase : EndpointDataSource, IDisposable
    {
        ......
        private readonly IActionDescriptorCollectionProvider _actions;
        protected readonly object Lock = new object();
        protected readonly List<Action<EndpointBuilder>> Conventions;
        private List<Endpoint> _endpoints;
        private CancellationTokenSource _cancellationTokenSource;
        private IChangeToken _changeToken;
        private IDisposable _disposable;
        public ActionEndpointDataSourceBase(IActionDescriptorCollectionProvider actions)
        {
            _actions = actions;
            Conventions = new List<Action<EndpointBuilder>>();
        }        

        protected void Subscribe()
        {
            if (_actions is ActionDescriptorCollectionProvider collectionProviderWithChangeToken)
            {
                _disposable = ChangeToken.OnChange(
                    () => collectionProviderWithChangeToken.GetChangeToken(),
                    UpdateEndpoints);
            }
        }
        ......
    }
}

这里的_actions是注入进来的,这个服务是我们在services.AddMvcCore()中注入进来的:services.TryAddSingleton<IActionDescriptorCollectionProvider, DefaultActionDescriptorCollectionProvider>();
那么观察DefaultActionDescriptorCollectionProvider

namespace Microsoft.AspNetCore.Mvc.Infrastructure
{
    internal class DefaultActionDescriptorCollectionProvider : ActionDescriptorCollectionProvider
 }

从这里可以看到DefaultActionDescriptorCollectionProvider是继承自ActionDescriptorCollectionProvider的,所以在Subscribe()中的 if 判断为true继续向下执行,下一步该执行ChangeToken.OnChange()了,关于ChangeToken这里就不介绍了,只说一下它的功能和执行逻辑:功能就是“检测变化并回调函数 ”,执行逻辑是“先调用() => collectionProviderWithChangeToken.GetChangeToken()返回一个token,当这个token发生变化时就执行UpdateEndpoints方法”(可以参考:.netcore入门17:IChangeToken和ChangeToken用法简介),由此可见下一步去分析collectionProviderWithChangeToken.GetChangeToken(),但是在分析之前我们先说一下关系:现有一个actiondescriptorprivder,然后根据这个provider生成actiondescriptor,再之后根据descriptor生成endpoint,那么我们去看一下collectionProviderWithChangeToken.GetChangeToken()源码:

namespace Microsoft.AspNetCore.Mvc.Infrastructure
{
    internal class DefaultActionDescriptorCollectionProvider : ActionDescriptorCollectionProvider
    {
        private readonly IActionDescriptorProvider[] _actionDescriptorProviders;
        private readonly IActionDescriptorChangeProvider[] _actionDescriptorChangeProviders;
        private readonly object _lock;
        private ActionDescriptorCollection _collection;
        private IChangeToken _changeToken;
        private CancellationTokenSource _cancellationTokenSource;
        private int _version = 0;
        public DefaultActionDescriptorCollectionProvider(
            IEnumerable<IActionDescriptorProvider> actionDescriptorProviders,
            IEnumerable<IActionDescriptorChangeProvider> actionDescriptorChangeProviders)
        {
            _actionDescriptorProviders = actionDescriptorProviders
                .OrderBy(p => p.Order)
                .ToArray();

            _actionDescriptorChangeProviders = actionDescriptorChangeProviders.ToArray();

            _lock = new object();

            // IMPORTANT: this needs to be the last thing we do in the constructor. Change notifications can happen immediately!
            ChangeToken.OnChange(
                GetCompositeChangeToken,
                UpdateCollection);
        }        
        public override ActionDescriptorCollection ActionDescriptors
        {
            get
            {
                Initialize();
                Debug.Assert(_collection != null);
                Debug.Assert(_changeToken != null);

                return _collection;
            }
        }        
        public override IChangeToken GetChangeToken()
        {
            //从这里入手
            Initialize();
            Debug.Assert(_collection != null);
            Debug.Assert(_changeToken != null);

            return _changeToken;
        }
        private IChangeToken GetCompositeChangeToken()
        {
            if (_actionDescriptorChangeProviders.Length == 1)
            {
                return _actionDescriptorChangeProviders[0].GetChangeToken();
            }

            var changeTokens = new IChangeToken[_actionDescriptorChangeProviders.Length];
            for (var i = 0; i < _actionDescriptorChangeProviders.Length; i++)
            {
                changeTokens[i] = _actionDescriptorChangeProviders[i].GetChangeToken();
            }

            return new CompositeChangeToken(changeTokens);
        }
        private void Initialize()
        {
            if (_collection == null)
            {
                lock (_lock)
                {
                    if (_collection == null)
                    {
                        //从这里跳进去
                        UpdateCollection();
                    }
                }
            }
        }
        private void UpdateCollection()
        {
            lock (_lock)
            {
                var context = new ActionDescriptorProviderContext();

                for (var i = 0; i < _actionDescriptorProviders.Length; i++)
                {
                    //_actionDescriptorProviders是从services.AddMvcCore()中注册的services.TryAddEnumerable(ServiceDescriptor.Transient<IActionDescriptorProvider, ControllerActionDescriptorProvider>());
                    //跳进去
                    _actionDescriptorProviders[i].OnProvidersExecuting(context);
                }

                for (var i = _actionDescriptorProviders.Length - 1; i >= 0; i--)
                {
                    //同上,跳进去
                    _actionDescriptorProviders[i].OnProvidersExecuted(context);
                }
                var oldCancellationTokenSource = _cancellationTokenSource;
                _collection = new ActionDescriptorCollection(
                    new ReadOnlyCollection<ActionDescriptor>(context.Results),
                    _version++);
                _cancellationTokenSource = new CancellationTokenSource();
                _changeToken = new CancellationChangeToken(_cancellationTokenSource.Token);
                oldCancellationTokenSource?.Cancel();
            }
        }
    }
}

阅读上面的代码时,从GetChangeToken()方法入手,然后跟着注释走即可。当走到UpdateCollection()方法时,发现有个_actionDescriptorProviders。这个服务是我们在services.AddMvcCore()方法中注入的(services.TryAddEnumerable( ServiceDescriptor.Transient<IActionDescriptorProvider, ControllerActionDescriptorProvider>());)。所以在后面执行的OnProvidersExecuting(context);OnProvidersExecuted(context);的代码为:

namespace Microsoft.AspNetCore.Mvc.ApplicationModels
{
    internal class ControllerActionDescriptorProvider : IActionDescriptorProvider
    {
        private readonly ApplicationPartManager _partManager;
        private readonly ApplicationModelFactory _applicationModelFactory;
        public ControllerActionDescriptorProvider(
            ApplicationPartManager partManager,
            ApplicationModelFactory applicationModelFactory)
        {
            if (partManager == null)
            {
                throw new ArgumentNullException(nameof(partManager));
            }

            if (applicationModelFactory == null)
            {
                throw new ArgumentNullException(nameof(applicationModelFactory));
            }

            _partManager = partManager;
            _applicationModelFactory = applicationModelFactory;
        }
        public int Order => -1000;
        public void OnProvidersExecuting(ActionDescriptorProviderContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }
			
			//从这里入手,查看GetDescriptors()
            foreach (var descriptor in GetDescriptors())
            {
                context.Results.Add(descriptor);
            }
        }
        public void OnProvidersExecuted(ActionDescriptorProviderContext context)
        {
            var keys = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
            for (var i = 0; i < context.Results.Count; i++)
            {
                var action = context.Results[i];
                foreach (var key in action.RouteValues.Keys)
                {
                    keys.Add(key);
                }
            }

            for (var i = 0; i < context.Results.Count; i++)
            {
                var action = context.Results[i];
                foreach (var key in keys)
                {
                    if (!action.RouteValues.ContainsKey(key))
                    {
                        action.RouteValues.Add(key, null);
                    }
                }
            }
        }

        internal IEnumerable<ControllerActionDescriptor> GetDescriptors()
        {
			//这个是用来从partManager中获取所有ApplicationPart中包含的Controller类型的,可以跳进去查看
            var controllerTypes = GetControllerTypes();
			//这个跳进去查看,就是从这里获取的遍历的controller和action
            var application = _applicationModelFactory.CreateApplicationModel(controllerTypes);
            return ControllerActionDescriptorBuilder.Build(application);
        }

        private IEnumerable<TypeInfo> GetControllerTypes()
        {
            var feature = new ControllerFeature();
			//将所有的Controller类型放进feature中
            _partManager.PopulateFeature(feature);

            return feature.Controllers;
        }
    }
}

上面的源码中可以看到,OnProvidersExecuting执行的时候有个GetDescriptors()方法被调用了,它调用后就生成了Descriptor了,那么进到这个方法体里面观察,可以观察到:

  • 第一步:GetControllerTypes()获取所有的Controller类型
  • 第二步:_applicationModelFactory.CreateApplicationModel(controllerTypes);遍历所有的Controller和Action生成Descriptor

4.3 遍历所有的程序集(AssemblyPart),寻找Controller

先分析第一步:GetControllerTypes,这个方法里关键的代码段是_partManager.PopulateFeature(feature);那么看一下它的代码

namespace Microsoft.AspNetCore.Mvc.ApplicationParts
{
    public class ApplicationPartManager
    {
        public IList<IApplicationFeatureProvider> FeatureProviders { get; } =
            new List<IApplicationFeatureProvider>();
        public IList<ApplicationPart> ApplicationParts { get; } = new List<ApplicationPart>();
        public void PopulateFeature<TFeature>(TFeature feature)
        {
            if (feature == null)
            {
                throw new ArgumentNullException(nameof(feature));
            }
            //这里的FeatureProviders是从AddMvcCore()方法的末尾ConfigureDefaultFeatureProviders()中将ControllerFeatureProvider注册到了这个集合中
            foreach (var provider in FeatureProviders.OfType<IApplicationFeatureProvider<TFeature>>())
            {
                provider.PopulateFeature(ApplicationParts, feature);
            }
        }
        ......
    }
}

注意里面的FeatureProviders.OfType<IApplicationFeatureProvider<TFeature>>(),还记得最初分析的时候MvcCoreServiceCollectionExtensions.AddMvcCore()方法体里有个ConfigureDefaultFeatureProviders(partManager);吗?在这里面就将ControllerFeatureProvider放进了这个集合中,所以provider.PopulateFeature(ApplicationParts, feature)的源码如下:

namespace Microsoft.AspNetCore.Mvc.Controllers
{
    public class ControllerFeatureProvider : IApplicationFeatureProvider<ControllerFeature>
    {
        private const string ControllerTypeNameSuffix = "Controller";
        public void PopulateFeature(
            IEnumerable<ApplicationPart> parts,
            ControllerFeature feature)
        {
            foreach (var part in parts.OfType<IApplicationPartTypeProvider>())
            {
                foreach (var type in part.Types)
                {
                    if (IsController(type) && !feature.Controllers.Contains(type))
                    {
                        feature.Controllers.Add(type);
                    }
                }
            }
        }
        protected virtual bool IsController(TypeInfo typeInfo)
        {
            if (!typeInfo.IsClass)
            {
                return false;
            }
            if (typeInfo.IsAbstract)
            {
                return false;
            }
            if (!typeInfo.IsPublic)
            {
                return false;
            }
            if (typeInfo.ContainsGenericParameters)
            {
                return false;
            }
            if (typeInfo.IsDefined(typeof(NonControllerAttribute)))
            {
                return false;
            }
            if (!typeInfo.Name.EndsWith(ControllerTypeNameSuffix, StringComparison.OrdinalIgnoreCase) &&
                !typeInfo.IsDefined(typeof(ControllerAttribute)))
            {
                return false;
            }
            return true;
        }
    }
}

上面的代码很简单,逻辑如下:
已知一批ApplicationPart和ControllerFeature,把Part中的所有Controller放进Feature中!
这里说一下继承关系:

  • abstract class ApplicationPart
  • interface IApplicationPartTypeProvider
  • class AssemblyPart : ApplicationPart, IApplicationPartTypeProvider

services.AddControllers()的分析中我们知道把所有的程序集都封装成了AssemblyPart,那么var part in parts.OfType<IApplicationPartTypeProvider>()中的part也必然是AssemblyPart。接着往下,可以看到遍历part下的所有类,判别是否是Controller,然后添加到Feature中。根据代码,我把判别是否Controller的逻辑整理如下:

  • 1.必须是类
  • 2.不能是抽象的
  • 3.必须是public的
  • 4.不能包含泛型参数
  • 5.不能具有[NonController]特性
  • 6.名字以“Controller”结尾或者具有[Controller]特性( 注意 :ControllerBase上有[Controller]特性)

现在“获取所有的Controller类型”已经分析完了,那么接着我们就要分析“遍历Controller里所有的Action生成Descriptor”

4.4 遍历Controller中的所有Action

还是从ControllerActionDescriptorProvider里的var application = _applicationModelFactory.CreateApplicationModel(controllerTypes);说起,直接看这个方法的源码:

namespace Microsoft.AspNetCore.Mvc.ApplicationModels
{
    internal class ApplicationModelFactory
    {
        private readonly IApplicationModelProvider[] _applicationModelProviders;
        private readonly IList<IApplicationModelConvention> _conventions;
        public ApplicationModelFactory(
            IEnumerable<IApplicationModelProvider> applicationModelProviders,
            IOptions<MvcOptions> options)
        {
            if (applicationModelProviders == null)
            {
                throw new ArgumentNullException(nameof(applicationModelProviders));
            }
            if (options == null)
            {
                throw new ArgumentNullException(nameof(options));
            }
            _applicationModelProviders = applicationModelProviders.OrderBy(p => p.Order).ToArray();
            _conventions = options.Value.Conventions;
        }
        public ApplicationModel CreateApplicationModel(IEnumerable<TypeInfo> controllerTypes)
        {
            if (controllerTypes == null)
            {
                throw new ArgumentNullException(nameof(controllerTypes));
            }
            var context = new ApplicationModelProviderContext(controllerTypes);
            //这里的_applicationModelProviders是在MvcCoreServiceCollectionExtensions.AddMvcCore()中注入的services.TryAddEnumerable(ServiceDescriptor.Transient<IApplicationModelProvider, DefaultApplicationModelProvider>());和services.TryAddEnumerable(                    ServiceDescriptor.Transient<IApplicationModelProvider, ApiBehaviorApplicationModelProvider>());
            for (var i = 0; i < _applicationModelProviders.Length; i++)
            {
                //跳进去查看,这里只查看DefaultApplicationModelProvider就行
                _applicationModelProviders[i].OnProvidersExecuting(context);
            }

            for (var i = _applicationModelProviders.Length - 1; i >= 0; i--)
            {
                _applicationModelProviders[i].OnProvidersExecuted(context);
            }
            ApplicationModelConventions.ApplyConventions(context.Result, _conventions);
            return context.Result;
        }
        ......
    }
}

上面源码里注入的_applicationModelProviders是在MvcCoreServiceCollectionExtensions.AddMvcCore()中注册的两个服务:

services.TryAddEnumerable(ServiceDescriptor.Transient<IApplicationModelProvider, DefaultApplicationModelProvider>());
services.TryAddEnumerable(ServiceDescriptor.Transient<IApplicationModelProvider, ApiBehaviorApplicationModelProvider>());

这里只关注一个就行:DefaultApplicationModelProvider,源码如下:

namespace Microsoft.AspNetCore.Mvc.ApplicationModels
{
    internal class DefaultApplicationModelProvider : IApplicationModelProvider
    {
        private readonly MvcOptions _mvcOptions;
        private readonly IModelMetadataProvider _modelMetadataProvider;
        private readonly Func<ActionContext, bool> _supportsAllRequests;
        private readonly Func<ActionContext, bool> _supportsNonGetRequests;
        public DefaultApplicationModelProvider(
            IOptions<MvcOptions> mvcOptionsAccessor,
            IModelMetadataProvider modelMetadataProvider)
        {
            _mvcOptions = mvcOptionsAccessor.Value;
            _modelMetadataProvider = modelMetadataProvider;

            _supportsAllRequests = _ => true;
            _supportsNonGetRequests = context => !string.Equals(context.HttpContext.Request.Method, "GET", StringComparison.OrdinalIgnoreCase);
        }        
        public int Order => -1000;        
        public void OnProvidersExecuting(ApplicationModelProviderContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }
            foreach (var filter in _mvcOptions.Filters)
            {
                context.Result.Filters.Add(filter);
            }
            foreach (var controllerType in context.ControllerTypes)
            {
                var controllerModel = CreateControllerModel(controllerType);
                if (controllerModel == null)
                {
                    continue;
                }
                context.Result.Controllers.Add(controllerModel);
                controllerModel.Application = context.Result;
                foreach (var propertyHelper in PropertyHelper.GetProperties(controllerType.AsType()))
                {
                    var propertyInfo = propertyHelper.Property;
                    var propertyModel = CreatePropertyModel(propertyInfo);
                    if (propertyModel != null)
                    {
                        propertyModel.Controller = controllerModel;
                        controllerModel.ControllerProperties.Add(propertyModel);
                    }
                }
                //开始获取所有的方法
                foreach (var methodInfo in controllerType.AsType().GetMethods())
                {
                	//重要方法,过滤方法创建action模型
                    var actionModel = CreateActionModel(controllerType, methodInfo);
                    if (actionModel == null)
                    {
                        continue;
                    }
                    actionModel.Controller = controllerModel;
                    controllerModel.Actions.Add(actionModel);
                    foreach (var parameterInfo in actionModel.ActionMethod.GetParameters())
                    {
                        var parameterModel = CreateParameterModel(parameterInfo);
                        if (parameterModel != null)
                        {
                            parameterModel.Action = actionModel;
                            actionModel.Parameters.Add(parameterModel);
                        }
                    }
                }
            }
        }        
        public void OnProvidersExecuted(ApplicationModelProviderContext context)
        {
            // Intentionally empty.
        }        
        internal ActionModel CreateActionModel(
            TypeInfo typeInfo,
            MethodInfo methodInfo)
        {
            if (typeInfo == null)
            {
                throw new ArgumentNullException(nameof(typeInfo));
            }
            if (methodInfo == null)
            {
                throw new ArgumentNullException(nameof(methodInfo));
            }
            if (!IsAction(typeInfo, methodInfo))
            {
                return null;
            }
            var attributes = methodInfo.GetCustomAttributes(inherit: true);
            var actionModel = new ActionModel(methodInfo, attributes);
            AddRange(actionModel.Filters, attributes.OfType<IFilterMetadata>());
            var actionName = attributes.OfType<ActionNameAttribute>().FirstOrDefault();
            if (actionName?.Name != null)
            {
                actionModel.ActionName = actionName.Name;
            }
            else
            {
                actionModel.ActionName = CanonicalizeActionName(methodInfo.Name);
            }
            var apiVisibility = attributes.OfType<IApiDescriptionVisibilityProvider>().FirstOrDefault();
            if (apiVisibility != null)
            {
                actionModel.ApiExplorer.IsVisible = !apiVisibility.IgnoreApi;
            }
            var apiGroupName = attributes.OfType<IApiDescriptionGroupNameProvider>().FirstOrDefault();
            if (apiGroupName != null)
            {
                actionModel.ApiExplorer.GroupName = apiGroupName.GroupName;
            }
            foreach (var routeValueProvider in attributes.OfType<IRouteValueProvider>())
            {
                actionModel.RouteValues.Add(routeValueProvider.RouteKey, routeValueProvider.RouteValue);
            }
            var currentMethodInfo = methodInfo;
            IRouteTemplateProvider[] routeAttributes;
            while (true)
            {
                routeAttributes = currentMethodInfo
                    .GetCustomAttributes(inherit: false)
                    .OfType<IRouteTemplateProvider>()
                    .ToArray();
                if (routeAttributes.Length > 0)
                {
                    break;
                }
                var nextMethodInfo = currentMethodInfo.GetBaseDefinition();
                if (currentMethodInfo == nextMethodInfo)
                {
                    break;
                }
                currentMethodInfo = nextMethodInfo;
            }
            var applicableAttributes = new List<object>();
            foreach (var attribute in attributes)
            {
                if (attribute is IRouteTemplateProvider)
                {                
                }
                else
                {
                    applicableAttributes.Add(attribute);
                }
            }
            applicableAttributes.AddRange(routeAttributes);
            AddRange(actionModel.Selectors, CreateSelectors(applicableAttributes));
            return actionModel;
        }        
        internal bool IsAction(TypeInfo typeInfo, MethodInfo methodInfo)
        {
            if (typeInfo == null)
            {
                throw new ArgumentNullException(nameof(typeInfo));
            }
            if (methodInfo == null)
            {
                throw new ArgumentNullException(nameof(methodInfo));
            }
            if (methodInfo.IsSpecialName)
            {
                return false;
            }
            if (methodInfo.IsDefined(typeof(NonActionAttribute)))
            {
                return false;
            }
            if (methodInfo.GetBaseDefinition().DeclaringType == typeof(object))
            {
                return false;
            }
            if (IsIDisposableMethod(methodInfo))
            {
                return false;
            }
            if (methodInfo.IsStatic)
            {
                return false;
            }
            if (methodInfo.IsAbstract)
            {
                return false;
            }
            if (methodInfo.IsConstructor)
            {
                return false;
            }
            if (methodInfo.IsGenericMethod)
            {
                return false;
            }
            return methodInfo.IsPublic;
        }
        ......
    }
}

上面的代码中我们可以看到遍历ControllerTpe的所有方法并过滤生成ActionModel,那么过滤的规则是:

  • 首先不能是特殊的方法,比如:运算符重载,属性的set/get方法等
  • 方法上不能是[NonAction]
  • 不能是object继承下来的
  • 不能是dispose方法
  • 不能是静态方法
  • 不能是抽象方法
  • 不能是构造函数
  • 不能是泛型方法
  • 必须是公共的方法

这样过滤完了剩下的方法就是Action。
上面已经分析到将action扫描成ActionDescriptor了,那么还要再分析怎么将ActionDescriptor生成EndPoint的…

4.5 将遍历生成的ActionDescriptor生成EndPoint

这就要回到ActionEndpointDataSourceBase类的Subscribe()方法了:

protected void Subscribe()
{
    if (_actions is ActionDescriptorCollectionProvider collectionProviderWithChangeToken)
    {
        _disposable = ChangeToken.OnChange(
            () => collectionProviderWithChangeToken.GetChangeToken(),
            UpdateEndpoints);
    }
}

从上面代码中字面中可以看到,它要表达的意思是:当actiondescriptorprovider发生改变时就执行UpdateEndpoints方法,这里我们先不讨论actiondescriptorprovider什么时候发生改变(我也不清楚),重点在于UpdateEndpoints方法。下面我们看下UpdateEndpoints的方法体(注意这个方法还是在类ActionEndpointDataSourceBase中):

private void UpdateEndpoints()
{
    lock (Lock)
    {
        var endpoints = CreateEndpoints(_actions.ActionDescriptors.Items, Conventions);
        var oldCancellationTokenSource = _cancellationTokenSource;
        _endpoints = endpoints;
        _cancellationTokenSource = new CancellationTokenSource();
        _changeToken = new CancellationChangeToken(_cancellationTokenSource.Token);
        oldCancellationTokenSource?.Cancel();
    }
}

从上面的代码中我们看到了由“ActionDescriptors”到Endpoint转变的方法CreateEndpoints,这是个关键方法,我们找到它的方法体(方法体在它的子类里面,注意:我们一开始就是由子类ControllerActionEndpointDataSource找到ActionEndpointDataSourceBase的,所以在调用抽象方法CreateEndpoints的时候自然要再回到子类ControllerActionEndpointDataSource中去),这里我们先看看父类ActionEndpointDataSourceBase的源码:

namespace Microsoft.AspNetCore.Mvc.Routing
{
    internal abstract class ActionEndpointDataSourceBase : EndpointDataSource, IDisposable
    {
        private readonly IActionDescriptorCollectionProvider _actions;
        protected readonly object Lock = new object();
        // Protected for READS and WRITES.
        protected readonly List<Action<EndpointBuilder>> Conventions;
        private List<Endpoint> _endpoints;
        private CancellationTokenSource _cancellationTokenSource;
        private IChangeToken _changeToken;
        private IDisposable _disposable;
        public ActionEndpointDataSourceBase(IActionDescriptorCollectionProvider actions)
        {
            _actions = actions;

            Conventions = new List<Action<EndpointBuilder>>();
        }
        public override IReadOnlyList<Endpoint> Endpoints
        {
            get
            {
                Initialize();
                Debug.Assert(_changeToken != null);
                Debug.Assert(_endpoints != null);
                return _endpoints;
            }
        }
        protected abstract List<Endpoint> CreateEndpoints(IReadOnlyList<ActionDescriptor> actions, IReadOnlyList<Action<EndpointBuilder>> conventions);
        protected void Subscribe()
        {
            if (_actions is ActionDescriptorCollectionProvider collectionProviderWithChangeToken)
            {
                _disposable = ChangeToken.OnChange(
                    () => collectionProviderWithChangeToken.GetChangeToken(),
                    UpdateEndpoints);
            }
        }
        public override IChangeToken GetChangeToken()
        {
            Initialize();
            Debug.Assert(_changeToken != null);
            Debug.Assert(_endpoints != null);
            return _changeToken;
        }
        private void Initialize()
        {
            if (_endpoints == null)
            {
                lock (Lock)
                {
                    if (_endpoints == null)
                    {
                        UpdateEndpoints();
                    }
                }
            }
        }
        private void UpdateEndpoints()
        {
            lock (Lock)
            {
                var endpoints = CreateEndpoints(_actions.ActionDescriptors.Items, Conventions);  
                var oldCancellationTokenSource = _cancellationTokenSource;
                _endpoints = endpoints;
                _cancellationTokenSource = new CancellationTokenSource();
                _changeToken = new CancellationChangeToken(_cancellationTokenSource.Token);
                oldCancellationTokenSource?.Cancel();
            }
        }
    }
}

再看子类ControllerActionEndpointDataSource的源码:

namespace Microsoft.AspNetCore.Mvc.Routing
{
    internal class ControllerActionEndpointDataSource : ActionEndpointDataSourceBase
    {
        private readonly ActionEndpointFactory _endpointFactory;
        private readonly List<ConventionalRouteEntry> _routes;
        private int _order;
        public ControllerActionEndpointDataSource(
            IActionDescriptorCollectionProvider actions,
            ActionEndpointFactory endpointFactory)
            : base(actions)
        {
            _endpointFactory = endpointFactory;
            _routes = new List<ConventionalRouteEntry>();
            _order = 1;
            DefaultBuilder = new ControllerActionEndpointConventionBuilder(Lock, Conventions);
            Subscribe();
        }
        public ControllerActionEndpointConventionBuilder DefaultBuilder { get; }
        public bool CreateInertEndpoints { get; set; }
        public ControllerActionEndpointConventionBuilder AddRoute(
            string routeName,
            string pattern,
            RouteValueDictionary defaults,
            IDictionary<string, object> constraints,
            RouteValueDictionary dataTokens)
        {
            lock (Lock)
            {
                var conventions = new List<Action<EndpointBuilder>>();
                _routes.Add(new ConventionalRouteEntry(routeName, pattern, defaults, constraints, dataTokens, _order++, conventions));
                return new ControllerActionEndpointConventionBuilder(Lock, conventions);
            }
        }
        protected override List<Endpoint> CreateEndpoints(IReadOnlyList<ActionDescriptor> actions, IReadOnlyList<Action<EndpointBuilder>> conventions)
        {
            var endpoints = new List<Endpoint>();
            var keys = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
            var routeNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
            for (var i = 0; i < actions.Count; i++)
            {
                if (actions[i] is ControllerActionDescriptor action)
                {
                    _endpointFactory.AddEndpoints(endpoints, routeNames, action, _routes, conventions, CreateInertEndpoints);

                    if (_routes.Count > 0)
                    {
                        foreach (var kvp in action.RouteValues)
                        {
                            keys.Add(kvp.Key);
                        }
                    }
                }
            }
            for (var i = 0; i < _routes.Count; i++)
            {
                var route = _routes[i];
                _endpointFactory.AddConventionalLinkGenerationRoute(endpoints, routeNames, keys, route, conventions);
            }
            return endpoints;
        }
    }
}

从上面的代码中,我们可以看到将ActionDescriptor转换成了EndPoint放进了一个集合中。那么EndPoint创建完毕后,我们就返回ControllerEndpointRouteBuilderExtensions类的GetOrCreateDataSource()方法了,如下:

private static ControllerActionEndpointDataSource GetOrCreateDataSource(IEndpointRouteBuilder endpoints)
{
    var dataSource = endpoints.DataSources.OfType<ControllerActionEndpointDataSource>().FirstOrDefault();
    if (dataSource == null)
    {
        dataSource = endpoints.ServiceProvider.GetRequiredService<ControllerActionEndpointDataSource>();
        endpoints.DataSources.Add(dataSource);
    }

    return dataSource;
}

那么再返回就到了MapControllers()了,如下:

public static ControllerActionEndpointConventionBuilder MapControllers(this IEndpointRouteBuilder endpoints)
{
    if (endpoints == null)
    {
        throw new ArgumentNullException(nameof(endpoints));
    }

    EnsureControllerServices(endpoints);

    return GetOrCreateDataSource(endpoints).DefaultBuilder;
}

所以最终endpoints=>endpoints.MapControllers()最终返回了一个ControllerActionEndpointConventionBuilder回去。

五、总结

本篇文章讲解了,程序启动时如何扫描当前程序关联的程序集以生成Endpoint的原理。其实这个过程分两个步骤:

  • 第一个步骤:services.AddControllers(),这里扫描到了所有程序集并且将他们以ApplicationPart(AssemblyPart)管理起来(用ApplicationPartManager管理)。附图如下:
    在这里插入图片描述
  • 第二个步骤:endpoints.MapControllers(),这里将上一步扫描到的程序集里的Action先转换成ActionDescriptor然后再转换成EndPoint,附图如下:

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jackletter

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值