1、AOP独立于Java
尽管在上一篇的文章里列举了一个AspectJ的例子,但是AOP不是Java特有的一种编程方式。AOP 的概念最早可以追溯到c with class的时代,after,before的设计就是AOP的原型。不过这个特性最终在C++98中被取消,是因为ISO/ANSI标委会认为这个特性的使用频率太低。整个ISO/ANSI的标委会中只有BJ一个人经常使用这个特性。
现在回过头来重视AOP,不能不惊叹BJ的在语言设计上的功力,同时也不能不对标委会的短视而感到遗憾。这种缺失甚至影响了现代电脑语言在AOP上的处理能力。没有语言特性的支持,Java的AOP就像是老太太的裹脚布,打开JBOSS的代码,到处能够看到成堆成堆为了实现Interceptor而书写的Reflecting代码。即难看懂又难维护。
用Java实现AOP可以说是不伦不类,原因在于Java语言特性基于静态语言和动态语言之间。即没有C++/C#2.0那样的模板能力,又没有Ruby,Python那样的minxin功能。其实只要在语言特性上做到其中的一点,AOP的解决方案就非常得漂亮。Andrei Alexandrescu的Policy base design的方法其实已经将AOP的特性表现的淋漓尽致。
template class Thread
{
Lockpolicy lock;
void Run()
{
lock.aquire();
//do something
lock.release();
}
}
Thread thread1;
Thread thread2;
Thread thread3;
Template的缺点在于需要手工写一点委托代码。但是这些问题都是可以通过工具来解决的。但是从语法上Java根本无法比拟。当然template是一种静态的织入。
Java的采用Relection的AOP与Python,Ruby的mixin相比简直就是小儿科。例如:
class Target1():
def doSomething1(self):
#do Something class Target2():
def doSomething2(self):
#do Something
class AOP:
def __init__(self,target):
self.target=target
self.Interceptor=CreateInterceptorFromConfig()
def __getattr__(self, name):
Interceptor.Predo();
self.returnRst=getattr(self.__target, name)
Interceptor.Postdo();
def CreateInterceptorFromConfig(self):
#从配置文件中生成interceptor
if __name__="__main__":
AOP targetAop=Aop(Target1());
targetAop.doSomething1();
AOP targetAop=Aop(Target2());
targetAop.doSomething2();
Python 这类的动态语言天生就是用来做AOP的。Java需要几十行的实现AOP代码,python不超过5行就能解决。
除了实现上的问题以外,AspectJ还有另外一些问题,一个是AspectJ是不是会过于强大而导致没有用处。为了debug,程序员往往要往代码里加入好多打印语句。当Debug结束后,要是删除这些tracing代码,是个很大的浪费,从测试的角度看,以后万一需要做相同的工作,还要重写。 要是不删除这些代码,效率是个问题,另外,这些代码会模糊正常的business logic代码。 而且,对不同阶段的,不同目标的Debug,你可能希望在不同地方加入不同的Tracing代码,这些代码都留在一块儿,会导致混淆。AOP 所要达到的目标:“把Tracing代码独立出Business Logic代码,通过单独配置的规则动态或静态地编织进目标代码。”。但是,很多Tracing代码是函数进行到中间打印某些变量的值,或者当程序进入到某个分支打印一定的信息。 而AspectJ只能针对函数调用打印信息,这似乎不能解决根本问题。对AspectJ提出的其它例子,我认为用mixin,template是更好的解决方案。AspectJ的方案对于他们来说过于强大了而又显得不必要。
第二个是效率问题:由reflecting来实现AOP本来就会消耗大量的资源,这些消耗会有什么影响至今很难说。另外过于统一的织入会让程序损耗不必要的性能,例如Aspect J当中提出一个织入Lock的问题,我们知道lock是非常耗费资源。如果一个方法中我只需要在某一部分织入Lock,但是AOP目前只能做到函数调用上进行织入的话显然会有很多问题。这样武断的织入Lock是我不能认同的。不必要的资源消耗,甚至DeadLock都有可能出现。
2、AOP的分解方法
编译面向方面的程序包括了大量分离的方面表达,以产生通过横切方面描述的通用概念的结果表示,这个编译过程被称为编织(weaving)过程,可能包括合并组件,修改组件,优化等等。编织不一定是一个静态产生过程,可以通过方面程序的运行时解释或者运行时产生来实现,因此Kicales引入术语方面编织(Aspect Weaving)。
我们了解分离Aspect的想法之后,就需要具体的方法和技术,来获得这种分离。主要要解决三个问题:
- 什么是需要被分离的重要问题。
- 除了调用通用过程,我们还可以使用什么组合机制?
- 如何捕捉Aspect?
横切(crosscutting)是方面的核心。