扒一扒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:\demo\demo.dll"));
        }

那么從這個點,你又想到了什么?我想到了.net core mvc插件化的思路. 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM