.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的处理请求的执行顺序,具体的可以看看官方的文档
其实中间件都是由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
上一篇: 姜汁醋是很养生的,来看看它的营养价值吧
下一篇: 关键时刻这货还挺在乎我的