zoukankan      html  css  js  c++  java
  • .net core mvc启动顺序以及主要部件3-Startup

    前面分享了.net core Program类的启动过程已经源代码介绍,这里将继续讲Startup类中的两个约定方法,一个是ConfigureServices,这个方法是用来写我们应用程序所依赖的组件。另一个Configure,它是我们MVC请求的中间件方法,也就是我们每个请求来要执行的过程都可以写在这个方法里面。
          为什么说Startup类中的两个方法是基于约定的呢?其实是这样的,在.net core Program类Main方法中有个调用了Run方法这个方法从IServiceCollection容器中拿到一个IStartup类型的实例然后调用了IStartup中定义的两个方法法,如果我们的Startup类是实现了这个接口的类 那么就不是基于约定了,直接就可以使用,但是我们发现在vs给我们生成的Startup类并没有实现任何接口,所以就不会是IStartup类型,那么内部是如何去做的呢?  其实是这样的,在注册Startup实例的时候还有个类型叫做ConventionBasedStartup从名称上解读这个类就是转换为基础的Startup,其实却是也是这样的,这个类中是实现了IStartup接口,它的两个方法中分别调用了各自的对用委托,这些委托实际执行的就是我们Startup类中定义的两个方法,请看源代码:

     public class ConventionBasedStartup : IStartup
        {
            private readonly StartupMethods _methods;
    
            public ConventionBasedStartup(StartupMethods methods)
            {
                _methods = methods;
            }
    
            public void Configure(IApplicationBuilder app)
            {
                try
                {
                    _methods.ConfigureDelegate(app);
                }
                catch (Exception ex)
                {
                    if (ex is TargetInvocationException)
                    {
                        ExceptionDispatchInfo.Capture(ex.InnerException).Throw();
                    }
                    throw;
                }
            }
    
            public IServiceProvider ConfigureServices(IServiceCollection services)
            {
                try
                {
                    return _methods.ConfigureServicesDelegate(services);
                }
                catch (Exception ex)
                {
                    if (ex is TargetInvocationException)
                    {
                        ExceptionDispatchInfo.Capture(ex.InnerException).Throw();
                    }
                    throw;
                }
            }
        }
    

      现在Startup类的方法说清楚了,我们具体来说说方法中的内容,首先说ConfigureServices(IServiceCollection services),这个方法的参数是约定好的,不能随意改变,里面的IServiceCollection接口其实就是我们依赖注入的容器,说的再直白一点就是我们整个MVC所需要的实例都由IServiceCollection所管理,IServiceCollection有几个重要的扩展方法,他们都是定义在ServiceCollectionServiceExtensions静态类中,AddTransient方法,表示用这个方法添加到IServiceCollection容器的实例在需要注入的实例中都是一个全新的实例,AddScoped方法,这个方法表示在一次请求的生命周期内共用一个实例,AddSingleton方法,这个方法表示整个程序共用一个实例,例如日志服务,IConfiguration服务等都属于典型Singleton。请看例子:

     public class Startup
        {
            public void ConfigureServices(IServiceCollection services)
            {
                services.AddTransient<TransientService>();
                services.AddTransient<ServiceDemo1>();
                services.AddTransient<ServiceDemo2>();
            }
    
            public void Configure(IApplicationBuilder app, ServiceDemo1 demo1, ServiceDemo2 demo2 )
            {
                demo1.Test();
                demo2.Test();
                app.Run(async (HttpContext context) =>
                {
                  await  context.Response.WriteAsync("test successd");
                });
            }
        }
        public class TransientService
        {
            private int _updateCount;
            public int GetUpdateCount()
            {   
    this._updateCount = this._updateCount + 1; return this._updateCount; } } public class ServiceDemo1 { private readonly TransientService _service; public ServiceDemo1(TransientService service) { _service = service; } public void Test() { Console.WriteLine($"我是demo1的计数:{this._service.GetUpdateCount()}"); } } public class ServiceDemo2 { private readonly TransientService _service; public ServiceDemo2(TransientService service) { _service = service; } public void Test() { Console.WriteLine($"我是demo2的计数:{this._service.GetUpdateCount()}"); } }

      上面的例子中会产生一下结果,可以看得出来这两个注入的TransientService都是全新的实例

    如果我们稍微改变一下注入的方法,将原本的 services.AddTransient<TransientService>();改成services.AddScoped<TransientService>();就会产生如下结果:

    这个能说明什么呢,我们有两次注入  这个就表示TransientService保持了之前demo1的状态  demo1和demo2是可以共用这个实例来传输数据的,AddSingleton方法理解起来比较简单就不过多絮叨了,上面已经说明。

    接下来再来说说Startup类中的Configure方法,Configure方法中的参数是可以变化的,也就是说你可以用依赖注入的方法在参数中注入你想要注入的实例,前面说了 这个方法是我们请求的中间件方法,这个方法中会整合我们注入的IApplicationBuilder 中调用的各种Use方法中定义的中间件  并不是说这里面定义的代码每次请求都会被执行,这个概念一定要搞清楚,Configure方法只会在启动的时候执行一次,后面就不会再执行了,Configure方法只是让我们可以定义整个MVC的处理请求的执行顺序,具体的可以看看官方的文档https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/middleware/?view=aspnetcore-2.2

    其实中间件都是由IApplicationBuilder 所管理的ApplicationBuilder类实现了IApplicationBuilder 接口中的方法,看到ApplicationBuilder中的源代码中有个属性_components 它是一个IList<Func<RequestDelegate, RequestDelegate>>类型的委托容器,容器中的委托就是请求过来需要执行的中间件委托,当你在Configure方法中调用app.UseXXX的时候就会被注册到这个容器中去,然后请求过来就按照顺序执行容器中的每一个委托,所以这里就解释了前面说的Configure方法只会被执行一次的说法。下面也贴一下ApplicationBuilder类的源代码:

     public class ApplicationBuilder : IApplicationBuilder
        {
            private readonly IList<Func<RequestDelegate, RequestDelegate>> _components = new List<Func<RequestDelegate, RequestDelegate>>();
    
            public IServiceProvider ApplicationServices
            {
                get
                {
                    return GetProperty<IServiceProvider>(Constants.BuilderProperties.ApplicationServices);
                }
                set
                {
                    SetProperty(Constants.BuilderProperties.ApplicationServices, value);
                }
            }
    
            public IFeatureCollection ServerFeatures => GetProperty<IFeatureCollection>(Constants.BuilderProperties.ServerFeatures);
    
            public IDictionary<string, object> Properties
            {
                get;
            }
    
            public ApplicationBuilder(IServiceProvider serviceProvider)
            {
                Properties = new Dictionary<string, object>(StringComparer.Ordinal);
                ApplicationServices = serviceProvider;
            }
    
            public ApplicationBuilder(IServiceProvider serviceProvider, object server)
                : this(serviceProvider)
            {
                SetProperty(Constants.BuilderProperties.ServerFeatures, server);
            }
    
            private ApplicationBuilder(ApplicationBuilder builder)
            {
                Properties = new CopyOnWriteDictionary<string, object>(builder.Properties, StringComparer.Ordinal);
            }
    
            private T GetProperty<T>(string key)
            {
                if (!Properties.TryGetValue(key, out object value))
                {
                    return default(T);
                }
                return (T)value;
            }
    
            private void SetProperty<T>(string key, T value)
            {
                Properties[key] = value;
            }
    
            public IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware)
            {
                _components.Add(middleware);
                return this;
            }
    
            public IApplicationBuilder New()
            {
                return new ApplicationBuilder(this);
            }
    
            public RequestDelegate Build()
            {
                RequestDelegate requestDelegate = delegate (HttpContext context)
                {
                    context.Response.StatusCode = 404;
                    return Task.CompletedTask;
                };
                foreach (Func<RequestDelegate, RequestDelegate> item in _components.Reverse())
                {
                    requestDelegate = item(requestDelegate);
                }
                return requestDelegate;
            }
        }

    好啦,这篇关于Startup类就算介绍完成了,下篇开始正式介绍MVC



    
    
  • 相关阅读:
    Unity3D_08_XML文件创建,读取,修改,添加
    Unity3D_07_日志、文本打印
    Unity3D_06_根据Transform、GameObject和Tag获取子对象集合
    Unity3D_05_理解Unity的新GUI系统(UGUI)
    Unity3D_04_GameObject,Component,Time,Input,Physics
    Unity3D_03_代码及效率优化总结
    Unity3D_02_基类MonoBehaviour/自带函数以及脚本执行的生命周期
    Unity3D_01_各种寻找GameObject方法
    人工智能相关
    ubuntu 安装 typora
  • 原文地址:https://www.cnblogs.com/lvshunbin/p/11110615.html
Copyright © 2011-2022 走看看