zoukankan      html  css  js  c++  java
  • 扒一扒asp.net core mvc控制器的寻找流程

    不太会排版,大家将就看吧.

    asp.net core mvc和asp.net mvc中都有一个比较有意思的而又被大家容易忽略的功能,控制器可以写在非Web程序集中,比如Web程序集:"MyWeb",引用程序集"B.bll",你可以将所有的控制器写在"B.bll"程序集里面.mvc框架仍然可以寻找到这个控制器.

    仔细想一想,mvc框架启动的时候寻找过程:1.找到所有包含控制器的程序集;2.反射找到所有控制器类型;3.反射找到所有的action;4.缓存这些controller与action.

    那么有意思的就是第一步"找到所有包含控制器的程序集",一开始我认为是扫描当前应用程序域已加载的程序集,然后反射判断存不存在控制器类型.单如果一个程序有上千个程序集,那么反射无疑是一种灾难,mvc的启动也没这么慢啊,怀着好奇的心,这里就扒一扒官方的实现原理(asp.net mvc源码没去看,但原理估计差不多,这里就扒asp.net core mvc).

    这里建议大家先了解下 asp.net core mvc的启动流程:http://www.cnblogs.com/savorboard/p/aspnetcore-mvc-startup.html.

    action的匹配:http://www.cnblogs.com/savorboard/p/aspnetcore-mvc-routing-action.html

    这里引用杨晓东博客中的图片:

    1.AddMvcCore,mvc核心启动代码:

            public static IMvcCoreBuilder AddMvcCore(this IServiceCollection services)
            {
                if (services == null)
                {
                    throw new ArgumentNullException(nameof(services));
                }
                //获取ApplicationPartManager管理类,该类保存了ApplicationPart集合,而ApplicationPart最重要的子类AssemblyPart记录了程序集信息,另外PopulateFeature用于填充各种功能
                var partManager = GetApplicationPartManager(services);
                services.TryAddSingleton(partManager);
    
                ConfigureDefaultFeatureProviders(partManager);
                ConfigureDefaultServices(services);
                AddMvcCoreServices(services);
    
                var builder = new MvcCoreBuilder(services, partManager);
    
                return builder;
            }

    ApplicationPartManager:

        /// <summary>
        /// Manages the parts and features of an MVC application.
        /// </summary>
        public class ApplicationPartManager
        {
            /// <summary>
            /// Gets the list of <see cref="IApplicationFeatureProvider"/>s.
            /// </summary>
            public IList<IApplicationFeatureProvider> FeatureProviders { get; } =
                new List<IApplicationFeatureProvider>();
    
            /// <summary>
            /// Gets the list of <see cref="ApplicationPart"/>s.
            /// </summary>
            public IList<ApplicationPart> ApplicationParts { get; } =
                new List<ApplicationPart>();
    
            /// <summary>
            /// Populates the given <paramref name="feature"/> using the list of
            /// <see cref="IApplicationFeatureProvider{TFeature}"/>s configured on the
            /// <see cref="ApplicationPartManager"/>.
            /// </summary>
            /// <typeparam name="TFeature">The type of the feature.</typeparam>
            /// <param name="feature">The feature instance to populate.</param>
            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);
                }
            }
        }
    View Code
     private static ApplicationPartManager GetApplicationPartManager(IServiceCollection services)
            {
                var manager = GetServiceFromCollection<ApplicationPartManager>(services);
                if (manager == null)
                {
                    manager = new ApplicationPartManager();
    
                    var environment = GetServiceFromCollection<IHostingEnvironment>(services);
                    if (string.IsNullOrEmpty(environment?.ApplicationName))
                    {
                        return manager;
                    }
                    //使用默认的程序集发现提供器查找程序集,这个类就是查找的核心类
                    var parts = DefaultAssemblyPartDiscoveryProvider.DiscoverAssemblyParts(environment.ApplicationName);
                    foreach (var part in parts)
                    {
               //将找到的程序集添加到集合中 manager.ApplicationParts.Add(part); } }
    return manager; }

     2.DefaultAssemblyPartDiscoveryProvider:

            public static IEnumerable<ApplicationPart> DiscoverAssemblyParts(string entryPointAssemblyName)
            {
    //使用应用程序名称加载应用程序的入口程序集
    var entryAssembly = Assembly.Load(new AssemblyName(entryPointAssemblyName)); var context = DependencyContext.Load(entryAssembly);
    //找到候选的程序集,这里就是"可能"包含了控制器的程序集
    return GetCandidateAssemblies(entryAssembly, context).Select(p => new AssemblyPart(p)); }

    DefaultAssemblyPartDiscoveryProvider代码我就不全贴了(点击查看完整源码),

    这里使用了一个DependencyContext 依赖上下文,注意这个依赖上下文不是依赖注入的那个上下文,这个是指程序集引用关系的依赖上下文.

    实现原理看起来其实有点low,就是递归计算并判断程序集是否有引用mvc程序集,如果有引用就作为候选程序集.

    这里看核心的一个计算方法:

                private DependencyClassification ComputeClassification(string dependency)
                {
                    if (!_runtimeDependencies.ContainsKey(dependency))
                    {
                        // Library does not have runtime dependency. Since we can't infer
                        // anything about it's references, we'll assume it does not have a reference to Mvc.
                        return DependencyClassification.DoesNotReferenceMvc;
                    }
    
                    var candidateEntry = _runtimeDependencies[dependency];
                    if (candidateEntry.Classification != DependencyClassification.Unknown)
                    {
                        return candidateEntry.Classification;
                    }
                    else
                    {
                        var classification = DependencyClassification.DoesNotReferenceMvc;
                        foreach (var candidateDependency in candidateEntry.Library.Dependencies)
                        {
                            var dependencyClassification = ComputeClassification(candidateDependency.Name);
                            if (dependencyClassification == DependencyClassification.ReferencesMvc ||
                                dependencyClassification == DependencyClassification.MvcReference)
                            {
                                classification = DependencyClassification.ReferencesMvc;
                                break;
                            }
                        }
    
                        candidateEntry.Classification = classification;
    
                        return classification;
                    }
                }

    拿到所有候选程序集(就是可能包含控制器的程序集)后,就是调用ApplicationPartManager的PopulateFeature将控制器类型缓存起来.至于后面的action什么的就不再扒了,有兴趣的可以看源代码.

    从这个过程可以发现DependencyContext这个类,以及Microsoft.Extensions.DependencyModel这个库,那么我们可以利用这个东西也可以玩出很多花样来.

    哦 再补充一个点,我们可以外挂式的加载程序集:

            public void ConfigureServices(IServiceCollection services)
            {
                services.AddMvc()
                    .AddApplicationPart(Assembly.LoadFrom(@"C:demodemo.dll"));
            }

    那么从这个点,你又想到了什么?我想到了.net core mvc插件化的思路. 

  • 相关阅读:
    排序算法说明
    easyExcel 读写excel表格
    POI 读写excel表格
    JVM虚拟机详解
    SSM相关的配置文件模板
    SSM 统一异常处理
    ssm框架实现发送邮件
    springboot发送邮件
    SpringBoot Ajax请求Json数据
    协程(Coroutine)(二)
  • 原文地址:https://www.cnblogs.com/gucaocao/p/8422889.html
Copyright © 2011-2022 走看看