IoC容器与面向方面编程在SP无线运营系统设计中的应用
一直以来,在SP无线运营系统的中关于同步接口的设计由于上下家系统设计的规范不一致,导致这一部分不能很好的抽象共用,在我近2年来接触的各方平台接口大部分都是以一个接口作为一个页面的形式开放给上下家使用,表现形式只是编程平台的不同,或asp或php等,同一系统各接口的共用部分也多以数据访问层为主。
接下来将就我如何应用IoC控制反转容器和AOP来从技术角度解决这一问题做一个描述。
做无线运营的朋友众所周知,关于用户上下行(以短信,彩信,WAP等方式)直到扣费,涉及到的相关步骤有 预提交,MO同步,MT状态报告,依业务的不同又有可能没有预提交(依靠指令分辨下家),MO同步(MT流程内容控制在SP方),MT状态报告(如SP直接同步状态报告,MT报告就以MO同步形式表现)。
因此这里我首先抽象出三个对象, 分别是 ApiRequest表示预提交原始信息(包括号码,下家ID,附加参数等),ApiSync表示MO同步的原始信息(包括号码,MO上行内容,LINKID或MOID等), ApiStateReport表示状态报告原始信息(包括号码,LINKID,状态参数等)
理所应当的,有了这些对象自然要有处理这些对象的人(?)和处理结果,接下来再添加一些东西
如图所示分别添加三个处理器来处理对应的预提交,MO同步以及状态报告的原始信息对象并返回相应的处理结果。
经过上面一番步骤,我们有了大体的设计思路,但是应该如何使用它们呢?让我们跳出来稍微休息一会谈谈别的相关话题。
一般所谓的SP接口,不是编程语言里所谓的Interface,但也有相同之处,无非是调用与被调用双方实现约定好参数规范,然后传输数据,这一步骤不会涉及到用户界面之类的UI处理。在WEB环境下我们使用.NET平台来处理这一问题,很自然的就会想到借助IHttpHandler,没错,我们正是需要这位老朋友来帮助处理问题。
如图所示,我们有了分别处理预提交,MO同步与MT状态报告的三个Handler入口。遵循前辈们“组合优于继承”的设计原则,我们并没有让handler实现相应的invoker接口,而是“拥有”它们,为了我们下一步的“注入依赖”做好准备。
IApiRequestHandler简单的实现代码如下
接下来将就我如何应用IoC控制反转容器和AOP来从技术角度解决这一问题做一个描述。
做无线运营的朋友众所周知,关于用户上下行(以短信,彩信,WAP等方式)直到扣费,涉及到的相关步骤有 预提交,MO同步,MT状态报告,依业务的不同又有可能没有预提交(依靠指令分辨下家),MO同步(MT流程内容控制在SP方),MT状态报告(如SP直接同步状态报告,MT报告就以MO同步形式表现)。
因此这里我首先抽象出三个对象, 分别是 ApiRequest表示预提交原始信息(包括号码,下家ID,附加参数等),ApiSync表示MO同步的原始信息(包括号码,MO上行内容,LINKID或MOID等), ApiStateReport表示状态报告原始信息(包括号码,LINKID,状态参数等)
理所应当的,有了这些对象自然要有处理这些对象的人(?)和处理结果,接下来再添加一些东西
如图所示分别添加三个处理器来处理对应的预提交,MO同步以及状态报告的原始信息对象并返回相应的处理结果。
经过上面一番步骤,我们有了大体的设计思路,但是应该如何使用它们呢?让我们跳出来稍微休息一会谈谈别的相关话题。
一般所谓的SP接口,不是编程语言里所谓的Interface,但也有相同之处,无非是调用与被调用双方实现约定好参数规范,然后传输数据,这一步骤不会涉及到用户界面之类的UI处理。在WEB环境下我们使用.NET平台来处理这一问题,很自然的就会想到借助IHttpHandler,没错,我们正是需要这位老朋友来帮助处理问题。
如图所示,我们有了分别处理预提交,MO同步与MT状态报告的三个Handler入口。遵循前辈们“组合优于继承”的设计原则,我们并没有让handler实现相应的invoker接口,而是“拥有”它们,为了我们下一步的“注入依赖”做好准备。
IApiRequestHandler简单的实现代码如下
public interface IApiRequestHandler : IHttpHandler
{
IRequestInvoker RequestInvoker { get; set; }
ApiRequestResult Request(ApiRequest apiRequest);
}
{
IRequestInvoker RequestInvoker { get; set; }
ApiRequestResult Request(ApiRequest apiRequest);
}
既然IHttpHandler已然出场,IHttpHandler的老爸IHttpHandlerFactory不出来实在是说不过去了。只要使用合适的参数,咱们完全可以使用统一的入口来调用预提交,MO同步或是MT状态报告了。
接下来咱们编程的好帮手IoC容器将第一次入场亮相。。。。。
随着Spring 的面世,IoC反转容器在JAVA界迅速得到应用,.NET也很快有相应的实现,比如大家熟知的Castle项目。在这个应用中我使用了Spring.net的解决方案,程序入口如下面的代码所示
接下来咱们编程的好帮手IoC容器将第一次入场亮相。。。。。
随着Spring 的面世,IoC反转容器在JAVA界迅速得到应用,.NET也很快有相应的实现,比如大家熟知的Castle项目。在这个应用中我使用了Spring.net
ApiHandlerFactory
通过上面大家很容易看出,这里直接通过提交的参数type来决定最终调用哪个handler来处理上下家同步过来的数据。
相应的spring配置文件如下
<!-- 声明分别实现了IApiRequestInvoker,IApiSyncInvoker,IApiStateReportInvoker的三个类-->
<object id="DefaultRequestInvoker" type="Service.ApiService.DefaultRequestInvoker, Service"/>
<object id="DefaultSyncInvoker" type="Service.ApiService.DefaultSyncInvoker, Service"/>
<object id="DefaultStateReportInvoker" type="Service.ApiService.DefaultStateReportInvoker, Service"/>
<!-- 声明执行预提交动作的实现IApiRequestHandler的类 -->
<object id="request" type="Api.DefaultRequestHandler,Api">
<property name="RequestInvoker" ref="DefaultRequestInvoker" />
</object>
<!-- 声明执行MO同步的实现IApiSyncHandler的类 -->
<object id="sync" type="Api.DefaultSyncHandler,Api">
<property name="SyncInvoker" ref="DefaultSyncInvoker" />
</object>
<!-- 声明执行接受状态报告的实现IApiStateReportHandler的类
<object id="state" type="Api.StateReportHandler,Api">
<property name="ReportExecutor" ref="SmsStateReportExecutor" />
</object>
<!-- 声明执行电影业务的MO同步的实现IApiSyncHandler的类 -->
<object id="filmSync" type="ApiExtension.FilmSyncHandler,ApiExtension">
<property name="SyncInvoker" ref="FilmSyncInvoker" />
</object>
相应的web.config配置如下
<httpHandlers>
<add verb="GET,POST" path="entry.aspx" type="Api.ApiHandlerFactory, Api"/>
</httpHandlers>
如此,我们的所有数据处理的接口都通过entry.aspx来进行访问了,为了避免比如entry.aspx?type=request&spid=xxx这样的表现形式,我们可以通过使用asp.net mvc或者是url重写来“美化”一下。这里为了方便我直接使用了urlrewritter控件来执行重写
<rewriter>
<rewrite url="^~/api/request/?\??(.*)" to="~/entry.aspx?type=request&$1" />
<rewrite url="^~/api/(\d+)/(\w+)/?\??(.*)" to="~/entry.aspx?spid=$1&type=$2&$3" />
</rewriter>
这样一来提供给下家的预提交接口就变成了 http://mysite.com/api/request?@#¥!@¥#!@#¥
提供给SP上家的MO同步和状态报告地址就变成了 http://mysite.com/api/100/sync 或 http://mysite.com/api/100/state
看起来是不是清爽多了 ?结合相应的数据库设计,大部分情况下,每次新增接口就不用再写那没一点技术含量的接口页面了,配置都在后台完成。
如果有人问实在是有个别处理过程十分BT不人道的无线接口,连上面的可扩展设计都无法适用怎么办?好在咱们还有一把"金刚钻"——AOP。
咱们还是使用Spring.net的AOP解决方案。
正好,咱们来了一个BT的MO同步接口要处理,解决办法如下
相应的spring配置文件如下
<!-- 声明分别实现了IApiRequestInvoker,IApiSyncInvoker,IApiStateReportInvoker的三个类-->
<object id="DefaultRequestInvoker" type="Service.ApiService.DefaultRequestInvoker, Service"/>
<object id="DefaultSyncInvoker" type="Service.ApiService.DefaultSyncInvoker, Service"/>
<object id="DefaultStateReportInvoker" type="Service.ApiService.DefaultStateReportInvoker, Service"/>
<!-- 声明执行预提交动作的实现IApiRequestHandler的类 -->
<object id="request" type="Api.DefaultRequestHandler,Api">
<property name="RequestInvoker" ref="DefaultRequestInvoker" />
</object>
<!-- 声明执行MO同步的实现IApiSyncHandler的类 -->
<object id="sync" type="Api.DefaultSyncHandler,Api">
<property name="SyncInvoker" ref="DefaultSyncInvoker" />
</object>
<!-- 声明执行接受状态报告的实现IApiStateReportHandler的类
<object id="state" type="Api.StateReportHandler,Api">
<property name="ReportExecutor" ref="SmsStateReportExecutor" />
</object>
<!-- 声明执行电影业务的MO同步的实现IApiSyncHandler的类 -->
<object id="filmSync" type="ApiExtension.FilmSyncHandler,ApiExtension">
<property name="SyncInvoker" ref="FilmSyncInvoker" />
</object>
相应的web.config配置如下
<httpHandlers>
<add verb="GET,POST" path="entry.aspx" type="Api.ApiHandlerFactory, Api"/>
</httpHandlers>
如此,我们的所有数据处理的接口都通过entry.aspx来进行访问了,为了避免比如entry.aspx?type=request&spid=xxx这样的表现形式,我们可以通过使用asp.net mvc或者是url重写来“美化”一下。这里为了方便我直接使用了urlrewritter控件来执行重写
<rewriter>
<rewrite url="^~/api/request/?\??(.*)" to="~/entry.aspx?type=request&$1" />
<rewrite url="^~/api/(\d+)/(\w+)/?\??(.*)" to="~/entry.aspx?spid=$1&type=$2&$3" />
</rewriter>
这样一来提供给下家的预提交接口就变成了 http://mysite.com/api/request
提供给SP上家的MO同步和状态报告地址就变成了 http://mysite.com/api/100/sync
看起来是不是清爽多了 ?结合相应的数据库设计,大部分情况下,每次新增接口就不用再写那没一点技术含量的接口页面了,配置都在后台完成。
如果有人问实在是有个别处理过程十分BT不人道的无线接口,连上面的可扩展设计都无法适用怎么办?好在咱们还有一把"金刚钻"——AOP。
咱们还是使用Spring.net的AOP解决方案。
正好,咱们来了一个BT的MO同步接口要处理,解决办法如下
BT_SyncInterceptor
相应的spring配置文件的sync对象部分也要改动
<object id="sync" type="Spring.Aop.Framework.ProxyFactoryObject">
<property name="Target">
<object type="Api.DefaultSyncHandler, Api" >
<property name="SyncInvoker" ref="DefaultSyncInvoker" />
</object>
</property>
<property name="InterceptorNames">
<list>
<value>BT_SyncInterceptor</value>
</list>
</property>
</object>
<object id="BT_SyncInterceptor" type="AnotherAssembly.BT_SyncInterceptor, AnotherAssembly" />
如上所示,咱们通过修改spring配置文件把这个BT的mo同步接口指向了另一个经过包装的DefaultSyncHandler对象,WAPPER就是Spring.Aop.Framework.ProxyFactoryObject,通过装在另一个程序集里的BT_SyncInterceptor类横切处理相关的业务逻辑,这样咱们就既保留了原有的处理方式不变来适应大部分的业务需求,碰到极端的情况,还可以通过第三方组件的形式来进行弥补,正所谓你有张良计我有AOP,哈。
当然,实际的业务需求比上面所述要复杂的多,这里展示的只是一个大概的设计框架,实现方法。
谨记,备忘。
<object id="sync" type="Spring.Aop.Framework.ProxyFactoryObject">
<property name="Target">
<object type="Api.DefaultSyncHandler, Api" >
<property name="SyncInvoker" ref="DefaultSyncInvoker" />
</object>
</property>
<property name="InterceptorNames">
<list>
<value>BT_SyncInterceptor</value>
</list>
</property>
</object>
<object id="BT_SyncInterceptor" type="AnotherAssembly.BT_SyncInterceptor, AnotherAssembly" />
如上所示,咱们通过修改spring配置文件把这个BT的mo同步接口指向了另一个经过包装的DefaultSyncHandler对象,WAPPER就是Spring.Aop.Framework.ProxyFactoryObject,通过装在另一个程序集里的BT_SyncInterceptor类横切处理相关的业务逻辑,这样咱们就既保留了原有的处理方式不变来适应大部分的业务需求,碰到极端的情况,还可以通过第三方组件的形式来进行弥补,正所谓你有张良计我有AOP,哈。
当然,实际的业务需求比上面所述要复杂的多,这里展示的只是一个大概的设计框架,实现方法。
谨记,备忘。
--------------------------------------
PS:对于这类IO密集型的处理,应用异步方式处理应该效率更好,,正在努力学习老赵infoQ里关于异步托管的实现方式。。。。。希望有下文描述解决这方面的机会。。。
posted on 2009-04-03 21:41 yyliuliang 阅读(1847) 评论(0) 编辑 收藏 举报