初探面向方面的开发(AOP)

本文介绍了一个面向切面编程(AOP)的实际应用场景,通过一个工作流系统案例,展示了如何使用AOP来分离关注点,实现时间约束和日志记录等外围功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最初接触到AOP是在什么时候我已经不记得了,大概是1年半之前。不过在这个概念出现之前,我曾经做过这样的一件事情:通过截获Remoting代理的Invoke方法来记录一些远程方法的调用和异常情况,任何时候,领域程序员写的Remoting插件里抛出的未处理异常都能被外部的截获机制自动记录,大大得方便了调试和纠错。这种把通用的错误处理和领域业务处理分开的实现方法是不是可以看作是面向了两个不同的方面呢?在牛顿发现万有引力之前,我敢肯定,苹果曾不止一次的落在了别人的头上;在AOP的概念出现之前,我也敢肯定,这方面的实践已经有相当多的人经历过。不同的,是牛顿发现了万有引力定律而不是别人;不幸的,是我至今不知道到底是谁提出了AOP的概念。谁?还有谁(冯调)...算了,不管是谁,我还是说正经的。
现在我先假想出这样的一个应用情境:一个工作流的系统起初包括了最核心功能,在不同的业务领域中,还需要为这些核心的功能加载其它的约束或者自定义功能。定义如下:
ExpandedBlockStart.gif ContractedBlock.gif /**/ ///<summary>
InBlock.gif
///假设的工作流引擎
ExpandedBlockEnd.gif
///</summary>

None.gif public class WorkflowEngine
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
ExpandedSubBlockStart.gifContractedSubBlock.gif
/**////<summary>
InBlock.gif
///登录到工作流系统。
InBlock.gif
///这个函数只有对登录用户的判断规则,其它变动的规则等待注入
InBlock.gif
///</summary>
InBlock.gif
///<paramname="user"></param>
ExpandedSubBlockEnd.gif
///<returns></returns>

InBlock.gifpublicvirtualboolLogon(stringuser)
ExpandedSubBlockStart.gifContractedSubBlock.gif
dot.gif{
InBlock.gif
InBlock.gif
if(user=="wzcheng"||user=="xyz"||user=="*")
ExpandedSubBlockStart.gifContractedSubBlock.gif
dot.gif{
InBlock.gif
returntrue;
ExpandedSubBlockEnd.gif}

InBlock.gif
else
ExpandedSubBlockStart.gifContractedSubBlock.gif
dot.gif{
InBlock.gif
returnfalse;
ExpandedSubBlockEnd.gif}

ExpandedSubBlockEnd.gif}

InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif
/**////<summary>
InBlock.gif
///得到工作文档
InBlock.gif
///</summary>
InBlock.gif
///<paramname="user"></param>
InBlock.gif
///<paramname="docID"></param>
ExpandedSubBlockEnd.gif
///<returns></returns>

InBlock.gifpublicvirtualstringGetWorkDocument(stringuser,intdocID)
ExpandedSubBlockStart.gifContractedSubBlock.gif
dot.gif{
InBlock.gif
if(this.Logon(user)&&docID>0)
InBlock.gif
return"thisisadocument!";
InBlock.gif
else
InBlock.gif
returnstring.Empty;
ExpandedSubBlockEnd.gif}

InBlock.gif
ExpandedBlockEnd.gif}

很容易想到的办法就是在具体的业务领域里的工作流引擎从WorkflowEngine派生并覆写它的某些方法。值得一提的是,无论是面向过程、面向对象还是面向方面的解决方案都可以达到预期的目标,不同的是对问题进行剖析的角度和实施的复杂度不同,所以下面的提到的解决方法请读者不要和OP或者OO比出一个谁优谁劣来,就如同米饭和食盐没有任何优劣可比性一样。你看我,又罗嗦了。言归正传, 首先提出问题:如果某领域的业务规则是过了中午12点就不能登录工作流系统,并且任何用户只要取文档都要被日志记录下来。从AOP的角度考虑,核心问题方面是:登录和取文档;外围问题方面是:时间约束和日志记录。核心问题是相对稳固的,外围问题是会随着用户需求的变化了变化的。

接着就是解决问题,我们可以形象的成这种办法为间谍技术、伪装技术、特使技术等等,别看我忽悠的挺夸张,不知道还以为我是国家安全局的。不过的确有点相似之处,比如某人在BBS上发帖妄言时政,他一提交就被告知:您的言论涉及敏感关键字,系统怀疑你灌水等等。你说这是什么机制,难道不是AOP吗?从BBS的角度触发,谈论面广,人气旺那是求之不得的事情,不过别忘了,大家都是生活在构造和谐社会这个大的设计框架之下,不该咱说的就别乱说嘛,就如同过了12点工作流引擎不提供服务一样,做人要厚道,不要不自觉嘛。可有些人就是不自觉,那么对这种不自觉的行为采取必要的措施不得不说是合乎天理人情的,所以AOP的应用还是有相当大的前景吧?
根据上文提到的AOP机制,我采取的做法就是采用运行时生成动态程序集的技术,放弃了平常常用new实例化对象的方式,改用类厂机制。什么是类厂呀?看官,如果这个问题还没明白的话,你得赶紧补充一下,劳驾上网查,关键字是:类厂、设计模式。我们继续,通过类厂动态实现原型类的代理类,代理类从原型类派生,按照一个需求清单覆写原型类的方法,在这些方法中添加了对其它方法的调用,我们把这种添加成为代码注入行为。对于之前提出的需求,我做了如下的实现:
ExpandedBlockStart.gif ContractedBlock.gif /**/ ///<summary>
InBlock.gif
///工作流的约束对象
ExpandedBlockEnd.gif
///</summary>

None.gif public class WorkFlowEngineBuilder:ObjectProxy
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif
staticWorkFlowEngineBuilderbuilder=newWorkFlowEngineBuilder();
InBlock.gif
InBlock.gif
publicoverrideTypeTarget
ExpandedSubBlockStart.gifContractedSubBlock.gif
dot.gif{
ExpandedSubBlockStart.gifContractedSubBlock.gif
getdot.gif{returntypeof(WorkflowEngine);}
ExpandedSubBlockEnd.gif}

InBlock.gif
InBlock.gif
publicoverrideTargetTypeTargetType
ExpandedSubBlockStart.gifContractedSubBlock.gif
dot.gif{
ExpandedSubBlockStart.gifContractedSubBlock.gif
getdot.gif{returnTargetType.Special;}
ExpandedSubBlockEnd.gif}

InBlock.gif
InBlock.gif
publicoverrideInjectCase[]OverridableList
ExpandedSubBlockStart.gifContractedSubBlock.gif
dot.gif{
InBlock.gif
get
ExpandedSubBlockStart.gifContractedSubBlock.gif
dot.gif{
InBlock.gifInjectCase[]caseColl
=newInjectCase[2];
InBlock.gif
InBlock.gifcaseColl[
0]=newInjectCase();
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif
/**//*向匹配Logon(*)原型的函数注入此类的Constrain函数*/
InBlock.gif
InBlock.gifcaseColl[
0].SourceInfo=this.GetType().GetMethod("Constrain");
InBlock.gifcaseColl[
0].TargetInfo=this.GetMatchMethodInfo("Logon(*)",true);
InBlock.gif
InBlock.gifcaseColl[
1]=newInjectCase();
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif
/**//*向匹配Get(*)原型的函数注入此类的WriteLog函数*/
InBlock.gifcaseColl[
1].SourceInfo=this.GetType().GetMethod("WriteLog");
InBlock.gifcaseColl[
1].TargetInfo=this.GetMatchMethodInfo("Get*(*)",true);
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif
/**//*注意:当多个条件命中同一函数时,系统会出错,这里等待进一步的改进*/
InBlock.gif
InBlock.gif
returncaseColl;
ExpandedSubBlockEnd.gif}

ExpandedSubBlockEnd.gif}

InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif
/**////<summary>
InBlock.gif
///过了中午12点,系统就被锁定,除非以wzcheng登录的用户
ExpandedSubBlockEnd.gif
///</summary>

InBlock.gifpublicvoidConstrain()
ExpandedSubBlockStart.gifContractedSubBlock.gif
dot.gif{
InBlock.gif
stringcurrentUser=WorkFlowEngineBuilder.GetParameter<string>(0);
InBlock.gif
InBlock.gif
if(currentUser!="wzcheng")
ExpandedSubBlockStart.gifContractedSubBlock.gif
dot.gif{
InBlock.gif
if(DateTime.Now>DateTime.Parse(DateTime.Now.ToString("yyyy-MM-dd12:00")))
ExpandedSubBlockStart.gifContractedSubBlock.gif
dot.gif{
InBlock.gif
thrownewException("下班时间不能访问工作流引擎");
ExpandedSubBlockEnd.gif}

ExpandedSubBlockEnd.gif}

ExpandedSubBlockEnd.gif}

InBlock.gif
InBlock.gif
publicvoidWriteLog()
ExpandedSubBlockStart.gifContractedSubBlock.gif
dot.gif{
InBlock.gif
stringcurrentUser=WorkFlowEngineBuilder.GetParameter<string>(0);
InBlock.gif
intdocID=WorkFlowEngineBuilder.GetParameter<int>(1);
InBlock.gifConsole.WriteLine(
"{0}在{1}开始取ID为{2}文档",currentUser,DateTime.Now,docID);
ExpandedSubBlockEnd.gif}

InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif
/**////<summary>
InBlock.gif
///创建一个新的代理对象
InBlock.gif
///</summary>
InBlock.gif
///<typeparamname="T"></typeparam>
InBlock.gif
///<paramname="parameters"></param>
ExpandedSubBlockEnd.gif
///<returns></returns>

InBlock.gifpublicstaticTBuildObject<T>(paramsobject[]parameters)
InBlock.gifwhereT:
class
ExpandedSubBlockStart.gifContractedSubBlock.gif
dot.gif{
InBlock.gif
returnbuilder.NewObject<T>(parameters);
ExpandedSubBlockEnd.gif}

ExpandedBlockEnd.gif}

客户端调用的代码如下:

None.gif WorkflowEngineworkEng = WorkFlowEngineBuilder.BuildObject < WorkflowEngine > ();
None.gif
None.gif
try
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif
boollogon=workEng.Logon(this.comboBox1.Text);
InBlock.gifMessageBox.Show(logon
?"登录成功":"登录失败");
ExpandedBlockEnd.gif}

None.gif
catch (Exceptionex)
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gifMessageBox.Show(ex.Message);
ExpandedBlockEnd.gif}

从调用代码可以发现,除了用 WorkFlowEngineBuilder.BuildObject<WorkflowEngine>()代替了new WorkflowEngine() 之外没有其它的变化,但运行时可以发现,变量 workEng其实保存的是 WorkflowEngine的派生类型。这些都是表象,能说明的问题只有一个:这样的AOP实现方式在应用层还是很平滑的,不至于让程序员一开始就觉得接受不了,但这不是主要问题或者说不是主要的矛盾。是什么原因导致了AOP概念浮出水面,让G#或者aspectj取得了生存空间呢?回头我们再分析WorkFlowEngineBuilder类的如下代码:

None.gif public override TypeTarget
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
ExpandedSubBlockStart.gifContractedSubBlock.gif
getdot.gif{returntypeof(WorkflowEngine);}
ExpandedBlockEnd.gif}

None.gif

上面的代码是指WorkFlowEngineBuilder所针对的要注入代码的目标类型,如果把 typeof(WorkflowEngine) 该成别的,WorkFlowEngineBuilder是不是就成为其它类型的注入者呢?是的。再看:

None.gif public override InjectCase[]OverridableList
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif
get
ExpandedSubBlockStart.gifContractedSubBlock.gif
dot.gif{
InBlock.gifInjectCase[]caseColl
=newInjectCase[2];
InBlock.gif
InBlock.gifcaseColl[
0]=newInjectCase();
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif
/**//*向匹配Logon(*)原型的函数注入此类的Constrain函数*/
InBlock.gif
InBlock.gifcaseColl[
0].SourceInfo=this.GetType().GetMethod("Constrain");
InBlock.gifcaseColl[
0].TargetInfo=this.GetMatchMethodInfo("Logon(*)",true);
InBlock.gif
InBlock.gifcaseColl[
1]=newInjectCase();
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif
/**//*向匹配Get(*)原型的函数注入此类的WriteLog函数*/
InBlock.gifcaseColl[
1].SourceInfo=this.GetType().GetMethod("WriteLog");
InBlock.gifcaseColl[
1].TargetInfo=this.GetMatchMethodInfo("Get*(*)",true);
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif
/**//*注意:当多个条件命中同一函数时,系统会出错,这里等待进一步的改进*/
InBlock.gif
InBlock.gif
returncaseColl;
ExpandedSubBlockEnd.gif}

ExpandedBlockEnd.gif}

如果改写这个提供覆写清单的方法中的匹配规则,那么结合 Tagert 的改变,WorkFlowEngineBuilder是不是就完全服务于一个全新的应用场景了?那么我们平时提到的重构、复用、责任链模式等等是不是都可以在AOP的驱动下完成呢?需要提出的是:我的例子只是为了肤浅的说明一下AOP,让不了解的人有一点点了解,代码部分写的比较烂,应该没有什么借鉴之处。download source and demo / for .net 2.0.



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值