面向接口可扩展框架之“Mvc扩展框架及DI”
标题“Mvc扩展框架及DI”有点绕口,我也想不出好的命名,因为这个内容很杂,涉及多个模块,但在日常开发又密不可分
首先说Mvc扩展框架,该Mvc扩展就是把以前的那个Mvc分区扩展框架迁移过来,并优化整合了一下
一、Mvc扩展框架主要功能:
1、Mvc的依赖注入(DI)功能(类MvcDependency)
依赖IContainerFactory接口,不再依赖具体容器
2、Mvc全局过滤器(GlobalFilterProvider)
配置在Mvc的依赖注入容器中就能自动被Mvc调用。其实逻辑很简单,就是继承IFilterProvider接口,对外暴露Filters属性,需要增加过滤器就配置到Filters属性中
3、分模块(Area分区)开发支持
Area基类及相关支持类(AreaRoute、AreaListService)
4、分区过滤器(AreaGlobalFilterProvider)
5、分区注册的HttpModule(AreaMergeModule)
使用该HttpModule初始化分区及分区的依赖注入容器配置
二、依赖注入(DI)也挺复杂的
1、Mvc的依赖注入(DI)支持
Mvc使用IDependencyResolver接口定义依赖注入,实现该接口并覆盖Mvc默认的DI配置,主要是把Mvc的依赖注入适配到容器来支持,以便使用容器来做
2、容器的依赖注入(DI)
成熟容器支持依赖注入功能,为用户构造对象,并按当前对象(类)的依赖注入配置递归进行依赖注入操作
3、框架的依赖注入(DI)扩展(Fang.DI)
其一、本框架不依赖任一成熟容器,所以为了便于更好的使用DI技术,所以扩展了DI支持,以便使用任意容器功能都可以使用DI
其二、每种成熟容器的DI配置(标注)的方式不一致,导致使用特定容器的DI写的代码给使用其他容器技术的团队复用性不好
其三、本框架支持“快速检索黑科技”(参考核心容器那篇),使用该依赖注入(DI)扩展可以使用“快速检索黑科技“语法定义依赖注入标记
本篇内容涉及”Mvc扩展框架“子模块,”依赖注入(DI)扩展“子模块、主框架的核心容器及外部成熟容器(本篇继续使用Unity容器做外部成熟容器示例)
介绍的差不多了,先上例子
一、Mvc的依赖注入(DI)
1、使用Unity容器做DI
1.1 配置初始化
首先注入Unity容器,初始化Mvc依赖配置(替代Mvc默认的DependencyResolver)
1.2 建一个测试页面,使用Unity配置一个依赖注入服务
Ok,依赖注入成功
1.3 看一下Unity容器配置
2、使用Fluent代码做依赖注入
木有问题,效果出来。使用容器技术不一定非要用配置文件,Fluent方式也是配置容器的选项之一。当然配置文件搭配Fluent也是木有问题。
以上例子虽然简单,但用到了很多技术,有Mvc扩展框架、Unity容器及依赖注入(DI)扩展。
二、全局过滤器
1、过滤器代码很简单
以上只是ActionFilter的例子,Mvc支持的AuthorizationFilter\ExceptionFilter\ActionFilter\ResultFilter都可以这样配置,是不是很爽啊
2、看一下容器配置信息
有人说,你能用Fluent代码再作一次过滤器的例子吗?当然可以,只要创建一个IFilterProvider对象,注入到容器里面就可以了。没必要再做这个重复的工作。
我是非常推荐使用配置文件的方式,项目和调用的服务具体实现类才能彻底的隔离,才能更好的体现IOC容器的作用
三、分模块(area分区)
1、分区路由配置
2、两个分区配置
3、执行效果
以上两个分区运行结果,且两个分区调用的服务稍有不同,导致结果稍不同
分区的讲解不展开了,参看原来的文章”分区扩展框架“,只是分区过滤器稍有不同,后面再讲解分区过滤器
四、分区过滤器
1、还是使用全局过滤的代码,直接看结果
以上其实是分区过滤器和全局过滤器整合测试,两个分区共用一个全局过滤器GlobalTest,每个分区还有自己的分区过滤,效果不错吧
2、看一下过滤器配置(配置和全局过滤器是不同的)
看以上配置,分区过滤器和全局过滤器配置几乎一致(实际上也是继承关系),而且从分区中拆分出来,效果和原来(参看原来文章”分区扩展框架“)一样,结构是不清晰多了,而且也减轻了area初始化的负担,算是优化吧。
3、AreaMergeModule(分区注册的HttpModule)配置
配置方法还是那么简单,这方面的内容还是参看前篇文章
五、框架的依赖注入(DI)扩展(Fang.DI)
1、配置方式
和Unity的属性依赖注入相似度很高吧,只是命名空间不一样,但是作用大很多,还可以对不支持DI的容器进行DI(依赖注入)
2、执行效果
效果和Unity容器配置的一样。使用DI扩展对不同容器就可以统一依赖注入语法了。但是美中不足的是,我只在依赖注入扩展模块(Fang.DI)中实现了属性依赖注入。
这种扩展DI并不会覆盖原容器(比如Unity)的DI,只是补充,也会检测DI的属性是否已经初始化(!=null),也就是说扩展的DI是补充,原容器DI成功就不再DI,也可以同时使用(如果你不嫌乱的话)
我个人认为有属性依赖注入就够用了。当然很多人都有反驳的理由,最为关键的应该是安全性。如果我们把服务的初始化都交给容器,不要去修改服务对象,也就没有安全一说;如果你非要修改,就算是private的,类内部也可以修改,外部使用反射也能修改,没有绝对的安全性。
3、使用“快速检索黑科技“语法定义依赖注入标记
上面标注中重点要看到有个点(.),本例就是把全局服务容器中的默认时间格式化服务DI到当前Controller的属性上;也就是说可以把任意容器中的任意服务都DI过来,绝对的黑科技!!!
六、各功能模块都是如何协同工作的
1、Mvc扩展框架、Unity容器及依赖注入(DI)扩展模块相互独立,相互没有依赖关系
上图可以清晰看到,他们相互独立,但是他们都依赖着面向接口主框架(Fang.Framework),那主框架就非常重要,各自和主框架的关系就非常重要
(这就是我设计这个框架的初衷,以后增加再多的功能,或者引入再多的第三方组件,都只依赖主框架,都是对作为主框架的服务(插件)或者Mvc等Ms框架的插件来调用运行)
2、以上例子中各模块式如何工作
先看项目配置
以上先注册Unity容器,再注册依赖注入扩展(Fang.DI),分区及其路由规则使用HttpModule注入
我们还看到一个有意思的东西,初始化DI居然和初始化Unity容器那么的相似,其实DI扩展就是容器封装,说白了就是容器扩展,也就是说我是通过扩展容器来实现DI的
3、那我们看看依赖注入扩展(Fang.DI)怎么工作的
3.1 依赖注入扩展(Fang.DI)项目截图
A:DependencyAttribute就是依赖注入标注(Attribute),靠他标注哪个属性需要注入
B:Container和ContainerFactory是容器封装,封装后的容器对象就有了扩展出来的DI功能,所以我们调用的原始容器即使没有依赖注入功能经过这么一封装就有依赖注入功能了
C:PropertyChecker类就是负责找到那些属性需要依赖注入,PropertyChecker实例对象封装了一个属性的依赖注入过程(功能)
D:CheckContainer定义了一个使用容器处理依赖注入的接口(ICheckContainer)及多个Checkers属性包含多个ICheckContainer对象,并自己实现ICheckContainer接口
(其实就是容器处理依赖注入的组合模式,其中前面的PropertyChecker就是实现的ICheckContainer接口,一般来说CheckContainer对象包含一个PropertyChecker的数组)
注:这块刚开发完,CheckContainer这个命名有点问题,以后再改,如果有建议好的命名可以回复我一下,谢谢
3.2 再来看一下CheckContainer的主要逻辑(还是比较简单的)
解读一下:
A:字典(ConcurrentDictionary)按类型缓存对象
B:MonitorWrapper是我写的一个对象锁的封装,避免死锁和控制锁定的最长时间(默认50毫秒)及返回是否锁定成功;
(既然是缓存,那锁只是帮我们提高缓存利用率的工具,所以不管是否锁定成功,都继续执行)
C:如果没有缓存,我们按类型获取可写并标注(DependencyAttribute)的属性,按属性构造注入功能(ICheckContainer)对象
D:这里的注入比较简单,我直接使用的反射了;
(打算以后使用Emit,但是我的Emit功底太差,Emit个功能要半天,以后在好好学习Emit技巧或者花时间好好调试,为了性能不能有半点马虎)
本篇内容就讲完了。本篇内容和原来的那篇”分区扩展框架“有很多相同的地方,但本质的东西区别很大。原来的分区框架是强依赖Unity容器的,本框架是使用配置到主框架的容器工厂。
很多配置也都有更多的自定义化,而且本框架也照顾到不使用分区的开发使用者的利益(全局过滤和MvcDependency(DI))。
还有就是依赖注入扩展模块(Fang.DI)和Mvc扩展框架无缝结合,真是如虎添翼。当然,Fang.DI在非Mvc中的也是一样有效,这里就不举例说明了。