前面谈到SOA,思维可能确实有点发散,但是,我还是坚持这样一种更高抽象层次的Service Oriented设计思想绝对是有益的。当然这个有待实践检验。另一方面,本人正在规划中的一个SOAHelper开源框架将会为基于通用的SOA思想的开发提供一些便利,当然,这里提到的SOA都不是业界对SOA严格的标准定义。而是更高层次的抽象,不过,对于严格的SOA,同样是有益的,不,应该是主要的服务对象。这个就是后话了,这里随便提一下。回到正题,本文是重新诠释SOA & AOP的下篇,即重新诠释AOP。大家放心,本篇不会像SOA那样发散,以至于什么都是AOP,毕竟AOP还是以OO为基础的抽象思想的扩展,这里谈论的主题还是在于AOP的意义及怎样的AOP实现方式是AOP未来的发展趋势,尤其会阐述一种所谓容器式的二进制/IL级别基于AOP的拦截机制。
-
什么是AOP呢?
这里引用wayfarer的《第三只眼看AOP》一文中的定义和解释(感兴趣的朋友也可以参考本人关于.Net下的AOP的一个文章系列):
简单地说,AOP就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任,例如事务处理、日志管理、权限控制等,封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。
实质上,AOP只是OOP的一种补充或某种改进,它转换了编程的范式和视角,关注了一直以来被OOP忽略或者说未能解决好的角落,使开发人员可以更好地将本不该彼此纠缠在一起的责任(如银行业务和事务处理)分离开来。通过面向方面的编程,可以将程序的责任分开,对象与方面互不干扰。面向方面的模块并非显式地为对象所调用,而是通过或注入或截取的方式,去获得被封装的对象内部方法间的消息,然后做出相应地处理。也许面向方面的模式破坏了对象的封装,却正其如此,方才能降低模块与模块之间的耦合度。同样地,通过对“方面”的封装,将这些通用的功能从不同的类中分离出来,使不同的模块都能共享同样的“方面”,这也极大地减少了重复代码。
-
以上,是对于AOP的一般理解,当然,这样的阐述是没错的,本质上,AOP就是这么回事。但是,我相信,看完以上解释,还是会发现对于何时使用AOP,为什么我要用AOP,而不是传统的OO设计思想来实现我的软件功能呢?一旦觉得AOP听起来很美好去尝试过了,可能又会有更多关于性能、关于调试、关于事务一致性等等的疑问和担忧。如果您感觉到这些担忧,那么,非常好,您正在做非常理性的思考。会让人有这样的担忧,一方面是因为实现AOP,本身还不像OO那样直观,实现AOP的工具,也不像OO那样便利;另一方面,其实大多数人对使用AOP的时机,即如何去面对应用AOP可能带来的附带问题还是比较茫然的。我不指望您看完本文后能消除这种茫然,但是,我将尽力使您的这种感觉有所缓解。
-
说起AOP,我要提到一下MDA,我没有考证这两个词哪一个最先被提出,不过,我怀疑是MDA。AOP和MDA扯上什么关系呢?让我们先来谈谈MDA的基本思想。
MDA,Model Driven Architecture,是一种模型驱动的软件构架思想,发明者希望将来的软件开发能够用模型这种更直观的图形语言(一般来讲,这里的模型指UML),像搭积木一样来设计软件系统。理想情况下,一旦模型定义充分,从平台无关的模型(PIM)到平台相关的模型(PSM),进而到最终的可执行程序代码的过程是尽可能自动化的。
-
当然,这个是MDA最开始的思想,随着研究的深入,人们发现很多不那么实际的问题,首先就是PIM层往往过于复杂,很难构件化,我们可以想象,即使对于工作流这样一个相对比较雷同的领域,即使同一套系统,用户对于安全性、日志、事务这样的方面也往往有不同的要求,但是,可能对核心工作流引擎方面,很多系统有很相似。正在此时,有人发现了AOP,试想用AOP的思想将某些变化的可能性更大的逻辑分离出来,先关注核心业务逻辑精进和的重用,那么PIM不就会更清晰吗?
于是,有人提出MDA的过程,可以修正成一个新的模型:最开始除了核心基本不变平台无关的模型(PIM),新增一组Aspect(方面)层次,如日志、安全性、事务、Qos等等横切面模型(Aspects);第二步,通过一定的Converter将PIM和Aspects在一定的上下文条件下进行整合,成为平台无关的特定的模型,姑且称之为改进的平台无关的整合模型(EPIM);最后,再将这个EPIM转换到PSM。
这样做有什么好处呢?主要的好处是,有一些系统核心业务是基本不变的,比如,工作流,又如CRM,但是却有很多不确定的,易变化的,有时需要有时不需要的方面,那就可以将核心业务做成构件尽量充用,而将易变的方面做成Aspects,这就使得混乱的PIM,变得稍稍那么清晰一点。看起来,AOP,似乎有那么点用武之地了。可是,这就够了吗?
-
难道只能在一个整体雷同的业务模型上获得重用,不能再分的更细吗?如果一个构件流程显示比较美观,但是另一个构件性能更好,我就只能两者选一吗?问题的关键在哪里呢?关键在于,构件分得不够细,但是,很遗憾的是,这种分得不够细,不是我们不想分,而是无能为力。为什么无能为力呢?问题的瓶颈在哪里?非常遗憾,在于OO。
问题的瓶颈在于OO,为什么这么说呢?这也是我在关于SOA的上篇中说smalltalk的作者让我们认为世间万物都是对象,所有的对象都可以通过不断地继承,通过接口,通过重载等等这些OO元素就能完全描述了。这其实可笑之极!且不说,是不是世上就没有过程了,毕竟我可以让一个对象内只包含过程,并美其名曰对过程的对象化封装。OO中的对象和现实世界的对象的最大的区别在哪儿呢?
最大的区别在于,现实世界的对象,比如说人吧,必须在一个特定的上下文环境中才是有明确意义的。比如,当一个人在单位工作时,它是一个Employee,当他在健身房里,它是一位锻炼体魄的用户,当他和妻子在一起时,它是一个丈夫。而每种的角色的属性和方法是不尽相同的。那么,请问,一个人应该包含什么属性呢?以OO的思想,他只有用继承来描述。Ok,Employee继承自一个最简单的人,健身房的用户也可以继承自人,丈夫也可以继承自人,那我请问您了,假如这个人在单位的健身房里和妻子一起正在有说有笑的健身,这个时候你怎么表示这个人?
发现什么问题了吗?重复一下,一个对象,只有在一个特定的上下文环境,它才是有明确意义的。当上下文不同,它的属性方法可能完全不同。当一个程序员在单位的电脑前写代码的时候,谁关心他妻子是谁呢?老板在关心他写的代码质量的时候,时不时会关心他的二头肌脂肪含量多少呢?没错,我要说明的是,纯粹的OO没有描述这种非常常见的场景的自然方法。
-
那么让我们想象怎么来解决这个问题吧。很明显我们要解决类似当一个人处在一个特定的上下文环境时,自然而然的具有这些附加属性和方法,而当这个人处在集中叠加的环境时,自然而然的拥有叠加的附加属性和方法的问题。
解决这样的问题的最直观的方法,就是用一个容器来表示一个上下文环境,环境是可变的,当环境变化时,促使环境中的人变化的因素,应该自动的使得被管理的人获得附加的属性和方法。
有没有哪一种技术,可以达到这样的效果呢?有的,正是我们谈论的主题,AOP!
根据这种思想,有人提出了这样一种思路,进一步扩展上面的第二种改进的MDA过程:
将构件细分成更细的颗粒,比如人这样一个个构件,和描述不同上下文环境的人的附加属性和方法的不同的Aspects。这样,不论对于前面模型中的PIM和Aspect,都首先用一个容器来承载,对于每个容器包含人或者甚至是一个通用的事务处理方面这样的主构件的同时,只要插上不同的描述附加属性的特殊的Aspect构件,就使得这个容器描述了一个组合的,更加满足上下文需求的混合体。假设我们称这样的混合体为MPIM,和MAspect,那么,后面的不走不变,相应的再生成EMPIM和EMAspect,最后转换成PSM和可行性的代码。这样一来,是不是觉得MDA的实现更加实际呢?
-
当然,虽然以上分析以MDA的过程改进为主线来说AOP的地位,即使在目前MDA未被广泛采用的时候,AOP的这种应用方式,个人觉得,同样也是未来构件化的发展方向。细颗粒的构件,配合容器管理的AOP混合就能将软件构件重用发挥到一个非常高的层次。我想,通过以上论述,您多少应该有些认同吧?
如果再说到具体的AOP实现方式,一般有两种选择,也是现在的AOP框架的两条主线,即DynamicProxy和StaticWeave(也有一些可选方案,比如在.Net2.0中允许动态的为一个类附加动态方法,这里暂不评论其局限性),对于这两种实现线条,我倾向于,或者说得更肯定一些,我只看好StaticWeave,为什么呢?你就设想一下,以后的软件系统中的某个上下文环境中的一个人,它是由上百种甚至上千种叠加的上下文混合而来,如果用DynamicProxy来实现,是什么效率呢?短期内,像Castle这样的容器配合Aspect#这样基于DynamicProxy的AOP工具也是能达到一定的效果,但是,如果从长远来看,本人以为,基于容器管理的静态织入思想为基础的构件化软件自动化设计方法,一定是未来的发展方向。
这样一个易于静态织入的通用容器,也是本人希望努力尝试实现的方向。具体能达到怎样的效果,我不敢妄下判断,但是,我相信,尽管不坚信,能够起到很好的效果。还是有待实践的检验~~