欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

.net core mvc启动顺序以及主要部件3-Startup

程序员文章站 2022-05-27 20:22:46
前面分享了.net core Program类的启动过程已经源代码介绍,这里将继续讲Startup类中的两个约定方法,一个是ConfigureServices,这个方法是用来写我们应用程序所依赖的组件。另一个Configure,它是我们MVC请求的中间件方法,也就是我们每个请求来要执行的过程都可以写 ......

前面分享了.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都是全新的实例
.net core mvc启动顺序以及主要部件3-Startup

如果我们稍微改变一下注入的方法,将原本的 services.addtransient<transientservice>();改成services.addscoped<transientservice>();就会产生如下结果:
.net core mvc启动顺序以及主要部件3-Startup

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

接下来再来说说startup类中的configure方法,configure方法中的参数是可以变化的,也就是说你可以用依赖注入的方法在参数中注入你想要注入的实例,前面说了 这个方法是我们请求的中间件方法,这个方法中会整合我们注入的iapplicationbuilder 中调用的各种use方法中定义的中间件  并不是说这里面定义的代码每次请求都会被执行,这个概念一定要搞清楚,configure方法只会在启动的时候执行一次,后面就不会再执行了,configure方法只是让我们可以定义整个mvc的处理请求的执行顺序,具体的可以看看官方的文档

其实中间件都是由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