Xoohoo系列(二):初始化配置、加载模块或后台服务
从开发的角度讲,Global.asax文件是基于ASP.NET 应用程序的入口,而Application_Start方法正是一切的开始。
链接:IIS 5.0 和 6.0 的 ASP.NET 应用程序生命周期概述
Xoohoo实现了一个XoohooApplication类,其继承自HttpApplication。而XoohooApplication类供Global.asax来继承。
XoohooApplication类的Application_Start方法会完成两大工作。
一、初始化配置
1、设置IoC/DI容器。我们知道,虽然MVC3提供了依赖注入的支持,但并没有一个严格意义上的DI容器,其内部创建对象是通过实现自IDependencyResolver接口的私有类DefaultDependencyResolver。具体点就是GetService和GetServices方法。GetService方法调用Activator.CreateInstance会创建对象,要求类型定义必须提供无参的构造函数,而GetServices方法只会返回一个空的迭代器Enumerable.Empty<object>()。
Xoohoo提供了一个间接实现自IDependencyResolver接口的UnityDependencyInjector类。通过DependencyResolver.SetResolver方法,将DefaultDependencyResolver替换为UnityDependencyInjector。
UnityDependencyInjector 采用对象适配器模式对Unity容器进行了包装 。它直接实现自Xoohoo的IDependencyInjector接口,而后者继承自IDependencyResolver。IDependencyInjector接口提供了一种在运行时(Runtime)进行映射注册的机制。当然如果您不喜欢用Unity,可以实现自己的DI容器,如AutofacDependencyInjector,Xoohoo提供了相关机制允许您做这样的替换;
UnityDependencyInjector除了创建容器外,还会完成一些类型注册的工作。
2、注册FilterProvider。将Xoohoo提供的FilterRegistryFilterProvider添加到FilterProviders.Providers中。
如果想要使用基于外部配置的Filter注册解决方案,FilterRegistryFilterProvider将是一个不错的选择。
XoohooControllerActionInvoker继承自ControllerActionInvoker类并重写了CreateActionResult方法,目的是为了让Action支持更丰富的返回值类型。
Controller的ActionInvoker属性一般会返回一个ControllerActionInvoker实例,在DependencyInjectorControllerFactory中将设置Controller的ActionInvoker为XoohooControllerActionInvoker,这也正是使用DependencyInjectorControllerFactory的目的。
4、设置控制器工厂。通过ControllerBuilder.Current.SetControllerFactory方法,使用DependencyInjectorControllerFactory类替换MVC内置的DefaultControllerFactory类。
二、加载模块及后台服务
我们知道,想要在开机后运行某些程序,我们可以用注册表或“开始菜单->程序->启动”等方式添加要启动的程序信息。总有一种机制来保证来读取注册表或启动菜单并完成启动吧?对于Xoohoo来说,这就是引导程序。创建一个新的模块或后台服务后,在Xoohoo.config(位于站点的Configs目录,也可以自行修改配置文件名或路径)对其进行配置,引导程序负责它们的加载工作。
1、模块或后台服务配置
模块或后台服务设计完成后,将其配置在Xoohoo.config文件中。
上图是模块的配置,比如上面有名称为Core、Skinning和Logging等模块。只需要配置名称和模块,如果模块有私有配置,也加上去。
上图是后台服务的配置,比如上面有一个名称为MailMessageService的模块。除了配置名称和服务类型外,还需要加上服务执行器的类型(executorType),以及执行的时间间隔(interval)。InProcessBackgroundServiceExecutor是Xoohoo内置的服务执行器,它内部使用一个Timer来控制服务的执行。
2、引导程序
引导程序指实现了IBootStrapperTask接口的类。
模块或后台服务在Xoohoo.config配置后,在程序启动时将由引导程序来加载。
Xoohoo内置了两种引导程序,LoadModules和LoadBackgroundServices,两者的关系就像windows的注册表和启动菜单的关系。模块由LoadModules类来加载,而后台服务则由LoadBackgroundServices类来加载。
我们在DI容器中(UnityDependencyInjector)对这两个引导程序进行了注册。
Application_Start方法会调用Load方法会获取引导程序并控制其执行。
一般将具有UI界面,需要路由信息、配置ModelBinder等的插件,做成一个模块(Module)。当然,无UI的插件也可以做成模块的形式,比如日志、缓存等模块;
如果需要定时完成一些任务,比如每隔5分钟邮件报告一次服务器资源情况,就做成一个后台服务(BackgroundService)。 当然,一次行的操作也可以做成服务。
2、加载模块
LoadModules类用于加载模块,其核心方法是Execute:
moduleRegistry 对应的是模块注册器ModuleRegistry实例,用于保存已经加载的模块。这种映射关系在UnityDependencyInjector进行的,保存的是一个单例。模块注册器将在紧接着的下面介绍;
routes对应的是System.Web.Mvc.RouteTable.Routes;
modelBinders对应的是System.Web.Mvc.ModelBinders.Binders;
moduleConfig对应的是Xoohoo.config的配置文件的反序列化结果;
globalFilters对应的是System.Web.Mvc.GlobalFilters.Filters;
filterRegistry对应的是Xoohoo.FilterProviders.FilterRegistry.Filters。
这些对象都是单例。
调用routes、globalFilters和filterRegistry的将其清空。
然后遍历Xoohoo.config中的模块设置,通过moduleRegistry.Load进行加载并返回Module的实例。
如果成功创建模块,则依次调用模块的Initialize、RegisterModelBinders和RegisterRoutes等方法。将路由配置注册入MVC中的路由表中,将Filter过滤器注册入MVC的GlobalFilters.Filters或FilterRegistryFilterProvider中,将模型绑定器注册入ModelBinders.Binders中。
moduleRegistry.RegisterFilters是一个扩展方法,内部调用的是Module的RegisterFilters方法。将其放在foreach外部是希望所有路由配置都加入的情况下,再注册Filter。因为Xoohoo提供了一种可以根据Route数据设置Filter的机制,避免创建Filter的时候由于Route不存在而导致失败。
3、模块注册表 (ModuleRegistry)
模块注册表用于注册(ModuleRegistry.Load方法)和保存模块引用。
ModuleRegistry.Load方法,根据Xoohoo.config模块配置中模块的类型,创建模块的实例。并将模块本身的一些私有配置(Settings)存入模块实例中。将模块实例的引用存入ModuleRegistry的内部容器中,然后返回模块的实例。
另注:ModuleRegistry.Load方法还会根据模块DataProvider的配置进行相关设置,这里暂时略去。
4、加载后台服务
后台服务是一个实现了IBackgroundService接口的类,接口定义如下:
后台服务有对应的后台服务执行器(BackgroundServiceExecutor)负责服务的调度执行。后台服务执行器指实现了IBackgroundServiceExecutor接口的类,接口定义如下:
LoadBackgroundServices类用于加载模块,其核心方法是Execute:
与模块注册表类似,backgroundServicesRegistry对应的是后台服务注册表BackgroundServiceRegistry实例,后台服务注册表并不是用于保存后台服务本身,而是用于保存已经加载的后台“后台服务执行器(BackgroundServiceExecutor)”实例的引用(执行器包含“后台服务”的引用)。BackgroundServiceRegistry的映射关系在UnityDependencyInjector进行的,保存的是一个单例;
moduleConfig对应的是Xoohoo.config的配置文件的反序列化结果。
Execute方法遍历Xoohoo.config中的后台服务配置,通过backgroundServicesRegistry.Add对“后台服务执行器”进行加载并注册。接着调用对应的后台服务执行器将服务启动(Start)。
LoadBackgroundServices比较简单,具体的包括实体化后台服务及其执行器,将在接下来要介绍的BackgroundServiceRegistry类中进行。
5、后台服务注册表(BackgroundServiceRegistry)
如上所述,后台服务注册表并不是用于保存后台服务本身,而是用于加载和保存“后台服务执行器(BackgroundServiceExecutor)”,具体操作在其BackgroundServiceRegistry.Add方法中。
Add方法根据Xoohoo.config配置中“后台服务”的类型(type)以及“后台服务执行器”类型(exectorType),创建“后台服务”的实例和“后台服务执行器”的实例。并将一些私有配置(Settings)存入后台服务中。然后将“后台服务执行器”放入BackgroundServiceRegistry的内部容器中。
6、后台服务执行器(BackgroundServiceExecutor)
创建后台服务执行器时候,后台服务实例会作为参数传给其构造函数。
Xoohoo内置了一个InProcessBackgroundServiceExecutor执行器。当调用执行器的Start方法是,会完成后台服务的第一次执行,之后根据配置中的时间间隔再次执行。具体的请看InProcessBackgroundServiceExecutor类的源码。
Xoohoo中有一系列的内置模块和后台服务,以后将有专门的篇幅加以介绍。