前面分享了.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的处理请求的执行顺序,具体的可以看看官方的文档
其实中间件都是由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