环境:
- .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,同时它本身被包含在MvcCoreBuilder
或MvcBuilder
中。MvcCoreBuilder
或MvcBuilder
:
它们都是用来构建mvc服务的,不同的是MvcCoreBuilder
一般出现在mvc框架
源码中,而MvcBuilder
一般出现在用户的代码中。ControllerFeatureProvider
:
这个provider可以从ApplicationPart中提取出所有的Controller。
endpoints.MapControllers()涉及到的关键对象:
EndpointDataSource
:
这是个抽象类,表示一类EndPoint数据源,它的子类ActionEndpointDataSourceBase
和ControllerActionEndpointDataSource
将数据源具化到了Controller里的ActionIEndpointRouteBuilder
:
表示给路由构建Endpoint的对象,它的实现类ControllerActionEndpointConventionBuilder
就是endpoints.MapControllers()返回的实例类ActionDescriptor
它表示一个Action的描述对象,框架先遍历找到Controller的Action生成ActionDescriptor,然后再进一步生成EndPointIActionDescriptorProvider
表示ActionDescriptor的提供者
三、services.AddControllers()
都做了什么?
3.1 概述:
这个方法是在程序启动时遍历所有的相关程序集,将它们封装成一个个的AssemblyPart
(继承自ApplciationPart)
3.2 具体执行过程分析:
- 我们直接来看
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();
}
......
}
}
- 看到上面的代码,我们要知道
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,附图如下: