




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
面向方面程序设计语言研究综述
1面向克氏原螯虾的aop研究近年来,面向对象的节目方法(aop)的研究和应用在国内外引起了高度的关注,并迅速发展。作为对面向对象程序设计方法(object-orientedprogramming,OOP)的补充,AOP解决的是软件系统中的横切关注点(crosscuttingconcern)问题,以此进一步提高软件系统的模块化程度。如同其他软件开发方法的演化过程一样,对AOP的研究最早也是从程序设计语言的变化开始的,其中最具代表性的当属PARC研究中心对Java扩展后所开发的AspectJ,并在其后出现了大量面向商用或学术研究的AOP语言以及技术文献,从而为相关研究奠定了强大的发展基础和动力。近年来,针对AOP的研究早已超出传统的程序设计语言领域,并逐步扩展到整个软件生命周期以及各种支持工具和开发环境的研究和应用,而被统称为面向方面的软件开发方法(asepct-orientedsoftwaredevelopment)。AOP语言作为一种技术基础和核心,则一直是最活跃、发展最迅速的领域,并推动着AOP相关研究的发展。有必要对目前国际上的研究成果进行系统地总结和整理,以期为今后更深入地研究打下基础。文章特别针对AOP语言这一领域,对其主要语言特性和关键实现技术进行综述,并探讨了今后的发展与研究方向。2分离关注实现一般而言,可以认为软件系统是由若干满足用户需求的关注点(concern)组成的,而一个关注点就是软件要解决的一个问题。在OOP方法中,这些关注点往往被映射为规模不等的模块或类。实际开发过程中,关注点可以被分为两类:核心关注点(coreconcern)和横切关注点(crosscuttingconcern)。核心关注点通常涉及的是软件系统的核心业务逻辑,如ATM系统中的取款、查询、打印等功能。而像身份认证、交易日志等功能虽然不是系统的核心业务逻辑,但也是系统的重要组成部分,并且其功能所覆盖的范围往往横跨多个业务逻辑模块,因而被称为横切关注点。尽管目前的OOP方法能够让开发人员在需求分析和设计阶段将核心关注点和横切关注点较好地分离开,但是在实现阶段,它们的代码却往往交织在一起。典型的表现就是,对横切关注点中的功能调用语句显式地散布于各个相关的核心关注点的实现代码中。在复杂的软件系统中,这种现象会造成代码的交织与混乱(codetanglingandscattering),进而为系统的重用、维护和扩展带来负面影响。造成这种现象的原因在一定程度上是由OOP的局限性所带来的。虽然OOP能够通过继承机制较好地解决模块之间的纵向关系,但是却缺乏有效的手段来封装模块之间的横向关系,从而使得核心关注点和横切关注点之间的交互关系无法很好地从业务功能中分离出来。针对上述问题,研究人员对“separationofconcerns”提出了很多不同的解决方案,如meta-levelprogramming、adaptiveprogramming、compositionfilters、subject-orientedprogramming、designpatterns等。但这些技术要么由于其实现机制过于复杂,要么由于缺乏编程工具的直接支持而使得一般用户不易理解和使用。随着研究的进展,GregorKiczalesetal在1997年的ECOOP大会上提出的AOP方法以及在此基础上开发的AspectJ语言,作为对OOP的一种补充,日益受到广大学者与技术人员的重视,逐渐成为当前的研究热点。在AOP中,原本分散或交织在核心功能模块中的横切关注点被抽取出来,并单独封装在称为方面(aspect)的结构中,以实现模块化管理。与OOP中的类模块(class)不同,在方面中不但可以定义一个横切关注点所要执行的业务逻辑,还可以定义这个横切关注点与其他核心关注点的交互逻辑,即横切关注点中的业务逻辑应该在系统中的哪些地方被调用。这种方式改变了以前编码时由核心业务模块主动调用横切业务模块的惯例,而是反过来将原本散布于系统各处的对横切模块的调用逻辑集中到方面中。在系统的编译或执行过程中,方面中的业务功能会按照调用逻辑被插入到指定位置,就好像它们仍然是由核心业务模块所主动调用的一样,使整个系统实现核心与横切关注点的无缝集成与交互。显然,在用AOP方法开发复杂软件系统时,通过分离关注点的方式,可以让开发人员在设计核心业务模块时更加专注于核心功能的实现,而不必受到其他因素的干扰。而各个方面的开发也可以独立于核心业务模块的开发,只有在系统组装时刻才需要将其与系统的核心功能编排、融合在一起。模块之间的耦合性变得比较松散,降低了软件开发的难度,并可以同时提高模块的重用性。另外,在软件的维护阶段,还可以通过编写一个方面将横跨系统多个模块的新特性加入到系统中,而无须分别修改各个模块,进而降低了软件的维护难度。3aop语言的主要功能3.1aop的基础语言AOP语言所编写的程序通常由基础程序(baseprogram)和方面程序(aspectprogram)所组成。基础程序由称为基础语言(baselanguage)的传统程序设计语言所编写,负责实现系统的核心关注点;而方面程序作为对基础程序的补充和扩展,在语言层次上提供了对横切关注点的模块化实现,通常由称为方面语言(aspectlanguage)的专门AOP语法模块编写。表1列出了该文提到的部分典型AOP语言及其相应的基础语言。当前大多数AOP语言都是在某种OOP语言的基础上进行扩展后得到的,这也是因为AOP最初就是作为对OOP的补充和完善而提出的,因此OOP语言自然成为这类AOP语言的基础语言,并基本涵盖了所有常见的OOP语言,如Java、C++、Smalltalk等,其中又以Java作为基础进行扩展的情况最为常见。同时,在典型AOP语言中也采用其他类型的程序设计语言作为其基础语言,如C、Cobol等命令式语言,以及某些函数式语言。还有一些AOP语言是针对特定平台或应用领域设计的,其基础语言较为特殊。例如基于.NET平台所开发的Weave.NET、Sourceweave.NET、AspectBuilder,可以同时使用多种语言作为其基础语言,只要这些语言遵循.NET平台的公共语言架构(commonlanguageinfrastructure,CLI)规范。而AO4BPEL则是在基于工作流和Web服务领域的业务流程描述语言BPEL中融入了AOP思想,其基础语言实际上也可以认为是XML。3.2类abs+j类作为AOP发展历史中最具代表性的AspectJ语言,其他大量的AOP语言在功能特性上都与其具有相当程度的相似性,通常把这类语言称为类AspectJ语言。它们最核心和最具特色的功能就是pointcut-advice机制,其中所涉及到的几个核心概念包括:方面、连接点(joinpoint)、切入点(pointcut)、通知(advice)、织入(weave)等。本节将分别对这些核心概念的使用和实现进行说明。3.2.1静态织入机制的使用连接点与切入点是这类语言中两个紧密相关的概念,也是AOP对OOP模型最主要的扩展。连接点一般指的是基础程序执行过程中的某个特定时刻,如方法调用、属性读写、对象初始化、异常抛出等,是基础程序中所有允许织入方面的位置。而切入点则是若干连接点的集合,用来指定具体在哪些连接点上插入方面模块中的程序逻辑。它们通常由专门的语法定义在方面程序中,如AspectJ中的pointcutdesignator结构,其含义实际上就是对特定连接点的选择操作,通过若干预先定义好的选择关键字(如:call、execution、field、reception、cflow等)和用户自定义的选择范围(如:指定的类名或方法名、返回值类型、访问权限等)所组成的选择条件来对指定类型的连接点进行描述,还可以使用通配符以及逻辑组合来提供更加灵活强大的描述能力。这种方式被目前大多数类AspectJ语言所采用,并且在文献[12,18-20]中都进行了形式化地描述和分析。在AOP语言中,如何通过程序所定义的切入点匹配指定的连接点则是具体实现机制中的重要环节,关系到整个系统的功能强弱和执行效率。很多类AspectJ语言采用基于joinpointshadow的机制,系统利用预处理器或相关工具,通过对基础程序的代码结构(如:抽象语法树、字节码文件等)进行扫描和搜索,找到与某个切入点所描述的连接点相对应的静态代码位置(即joinpointshadow),并直接在这些位置上插入对方面模块中程序逻辑的调用语句。而对于那些与控制流或线程相关的连接点,必须在程序执行过程中对动态条件进行检查,系统则会在所有可能的joinpointshadow处插入条件判断语句供运行时检查,如果符合条件则会转而调用相应方面模块中的程序逻辑。这种针对程序代码结构进行直接搜索匹配的方式,其效率通常较高,而且比较容易对搜索算法进行优化,如使用索引结构保存方法之间的调用关系以加快搜索效率。由于系统在预处理或编译阶段就可以完成全部匹配过程,也不会对程序的运行时效率造成太大影响,在采用静态织入机制的AOP语言中较为常见,但由于需要对joinpointshadow处的代码进行改动,往往不利于实现反织入功能。在其他一些AOP语言中则存在着不同的匹配方式。在EAOP中会将程序执行过程中遇到的连接点作为运行时事件(runtimeevent)抛出,并由称为运行监控器(executionmonitor)的模块对这些事件进行捕获并分析,以查看是否存在与其相对应的切入点表达式以便调用相应的方面模块。这种方式通过连接点的执行来主动触发切入点的匹配过程,并能够利用统一的事件处理机制方便地完成比较复杂的功能,例如实现作用于方面的方面,以及多个方面模块的组合等机制。目前基于事件或分布式架构的AOP语言大多使用了这种匹配机制。整个匹配过程发生在程序运行过程中,可以为系统提供更多的动态功能,但是也可能产生更多的系统开销,影响到运行效率。例如每当捕获到一个连接点事件时,就需要对全部切入点表达式进行匹配,这会造成切入点在运行过程中被反复求值而带来的额外开销。文献对此提出了一种优化方案,通过使用前后链接的过滤器层次结构(filterhierarchy)帮助筛除在某个连接点处不需匹配的切入点,从而减少匹配数量以提高效率。在SpringAOP、JAC等语言中,系统在运行环境中对程序的连接点进行实例化处理,即在程序执行到特定位置时,就生成一个相应类型的连接点对象,其属性可以包含一些相关的上下文信息,以便与方面模块相互传递某些环境参数。由于连接点以对象的形式存在于运行环境中,因此在切入点的匹配过程中,可以使用反射机制(reflection)在元模型层次上对连接点对象及其所包含的信息进行检索。Java、Smalltalk等语言都提供了强大的反射应用接口(reflectionAPI),可以帮助实现所需的匹配和检索功能。但是,反射机制所需的运行开销对程序的执行效率往往是一个瓶颈,这些问题在文献[24,27-28]中都有论及。好在目前对反射机制的研究也比较活跃,有很多相应的优化策略可以帮助提高其执行效率。关于连接点与切入点的匹配,在部分AOP语言中还存在一些其他的技术。在JAsCo中,可以基于源代码的抽象语法树生成连接点树(joinpointtree)结构,并在其中搜索符合切入点描述的连接点。在文献中,则需要将基础程序的代码结构转换成字节码指令链表(linkedlistofbytecodeinstruction)的形式之后,再搜索符合条件的连接点。这种字节码结构的转换大多依赖于专门的第三方工具,可以降低转换的难度和出错的可能。表2中列出了主要AOP语言中所实现的连接点与切入点之间的匹配方式。由表2可以看出,有些AOP语言还可能同时采用多种方式来实现其切入点的匹配过程,用户也可以根据不同的使用方式或应用环境选择不同的执行方式,这也使AOP语言的应用更加灵活。3.2.2aop实现机制通知作为AOP语言的主要程序结构之一,通常与某个切入点相关联,用来描述在相应连接点处所需调用的程序逻辑,其结构类似于OOP中的方法(method)。实际上,在很多AOP语言中就是把通知作为方法来进行存储和调用的。不同之处主要在于:调用方法时需要将其调用语句显式地写在基础程序中;而调用通知则体现了AOP中所强调的好莱坞法则(Hollywoodprinciple)———不需要在基础程序的代码中显式调用通知,其调用逻辑会被织入机制自动插入到基础程序的执行流程中。这种方式使开发人员在编写基础程序时不需要考虑通知的存在,可以更加专注于业务逻辑。最常见的通知类型包括beforeadvice、afteradvice和aroundadvice三种。从字面上不难理解,这些通知分别会在相应连接点执行之前或之后被调用执行。aroundadvice会在连接点执行前后都被调用,它甚至可以直接替换掉连接点处的程序逻辑。afteradvice在AspectJ、CaeserJ、AspectWerkz等语言中,还可以根据连接点的执行结果分为正常返回后调用的afterreturnadvice和异常返回后调用的afterthrowingadvice。在JAsCo中,甚至提供了aroundreturnadvice和aroundthrowingadvice两种类型,从而使通知的功能更为强大。在AOP语言中,完成了切入点对连接点的匹配后,如何在指定的连接点处实现对通知的调用也是具体实现机制中的一个重要环节。早期AOP语言中最典型的实现方案就是通过预编译器直接在相应joinpointshadow处插入对通知的调用语句或字节码指令,其优点主要在于程序控制流在执行过程中不必进行复杂切换,运行时效率较高,基础程序的运行环境也不需要改变;其缺点则在于缺乏足够的灵活性,调用指令的插入必须在程序运行前完成,并且在程序运行过程中无法进行改动,从而无法支持更多的运行时特性。另外,这种方式通常还需要第三方代码转换工具对基础程序的源代码或字节码结构进行操纵。文献提到了一种称为adviceinlining的调用方式,即在joinpointshadow处插入完整的通知指令块,直接将其功能逻辑嵌入到基础程序中,而不是通过方法调用的方式去执行通知。这种方式能够省略程序运行过程中的部分调用开销,达到更高的运行效率,但是在某些情况下,实现起来可能非常复杂,需要考虑更多的技术细节。例如通知中所引用变量的作用域范围、代码边界的判断、程序运行栈中代码行标记与基础程序中的代码行不匹配等问题,文献也分析了这种调用方式的相关问题。这使得adviceinlining更多地只是被作为一种对通知调用的优化策略来使用,仅仅用来处理一些较简单的通知模块。新近出现的AOP语言包含了越来越多的动态特性,对通知的调用也趋向于动态化,而不需要在执行前对基础程序进行过多的预处理。结合该文3.2.1小节中所提到的匹配机制,当系统的运行环境捕获到执行过程中的连接点时,会由AOP语言的底层架构(infrastructure)对其类型或上下文信息进行分析,再根据所匹配的切入点的定义转而调用相关通知。由于在这种方式中,对通知的调用指令完全是由底层架构在程序执行过程中发出的,不需要事先插入到基础程序中,因而可以提供相当的动态性,甚至能够根据运行时状态而改变通知的调用逻辑。但是,这种方式会比较明显地影响到程序运行时的效率,并且需要对程序的运行环境进行改造或扩充。在当前很多动态AOP语言中,如SpringAOP、AspectWerkz、CaesarJ、EAOP、JBossAOP等,都采用了这种方式。文献尝试在虚拟机模型的层次上通过代理(delegation)机制提供对通知的直接调用,它希望程序的运行环境能够直接支持AOP所需的各种概念,而不是总要将其映射到面向对象的运行环境来完成对通知的调用。另外,在其他一些AOP语言,如AOP4BPEL、PROSE、AspectS中,也存在着不同的通知调用方式,它们大多利用了其基础语言自身的功能特点,具体细节可参见该文第4.4小节。3.2.3方面模块的定义和运行方面是所有AOP语言中最核心的概念和功能模块,用来对系统中的横切关注点进行抽象和封装,使系统具有更好的模块性和可维护性。不同的AOP语言对方面模块提供了何种程度的支持,则是体现AOP语言功能特征的最重要方式。对于那些在基础语言之上进行语法扩充后而设计的AOP语言,大都提供了专门的语法模块来支持方面的概念,例如AspectJ就在Java中提供了扩展的“aspect”关键字用于定义方面模块,这种方式通常还需要扩展编译器来对这些关键字进行处理。另外一些AOP语言虽然没有直接从语法上提供对方面模块的支持,但是可以使用其他的一些概念或语法结构来完成其声明。例如:在JAC、JBossAOP、AspectWerkz、PROSE、AspectS等语言中,其方面语言并未对基础语言的语法进行任何扩展,而是直接将方面模块声明为一个普通的类结构,之后通过标注或继承的方式表明该类可以作为一个方面使用;CAM/DAOP等语言则要求在类结构中通过包含指定的属性和专门的构造方法来支持方面的功能;而在ObjectTeams中,方面的声明则是通过称为“team”的模块来完成的。虽然每种语言采用的方式不同,但是都可以达到声明方面模块的目的。作为对横切关注点的封装,一个方面模块通常由一个完整的语法结构组成,其中直接包含若干切入点和通知的声明,如AspectJ、AspectC++等。而在另外一些语言中,一个完整的方面模块则可能被分为几个语法上分离的组成部分。例如JBossAOP、SpringAOP、AspectWerkz等语言,方面模块被定义为普通的类结构,只包含作为实例方法定义的通知,而切入点的定义以及切入点和通知之间的绑定则以XML的形式记录在外部配置文件中;在JAC和Steamloom中则正好相反,方面模块只包含切入点的定义和与通知的绑定关系,通知的具体代码则被放在单独定义的类或方法中,这样的方面模块可以被看作是一个保存切入点与通知之间映射关系的容器;在JAsCo中,由于切入点的定义被封装在称为“connector”的类中而独立于方面的声明,使得切入点和方面模块都能够被独立重用。在一些复杂情况下,为了提高重用性,甚至希望能够把多个方面组合起来,成为一个新的功能更加强大的方面模块来使用,但这并不是所有AOP能够支持的,EAOP、JAC、CaeserJ、FuseJ等在这方面进行了一些尝试和探索。例如在EAOP中,可以通过特定的组合运算符并结合运行时的逻辑状态,来规定多个方面模块在运行时的组合关系,如顺序、互斥、替换、选择等,这也是一种动态行为的实现。在程序执行过程中,当多个方面所包含的通知作用于同一个连接点上时,如何控制其执行顺序也是AOP语言需要考虑的一个问题。在AspectJ、AspectC++、CaeserJ等语言中,对于同一方面的若干通知通常按照其在方面模块中的声明顺序来依次执行,而位于不同方面模块中的通知则需要在程序中显式地规定其执行顺序;在JBossAOP、SpringAOP、JAC等语言中,方面的执行顺序被记录在配置文件中;在AspectS中,作用于同一连接点上的通知是根据其类型来决定执行顺序的,子类中的通知会被先执行;在FuseJ中,则是根据方面模块的装载(load)顺序来决定其执行顺序;CAM/DAOP的处理方式是在配置文件中定义一些属性,如果几个方面都依赖于同一个属性,则它们必须顺序执行,否则多个方面可以并发地执行。由于方面模块在大多数AOP语言内部是以类的结构来表示和存储的,在使用时也需生成其实例对象。按照方面实例的作用域范围,不同语言中可能采取单实例(singletoninstantiation)或多实例(multiinstantiation)的方式进行实例化。在单实例的情况下,某个方面模块在整个运行环境中只生成唯一的实例,作用于全局范围,实例状态的改变会影响到整个系统中对该方面的引用。这种方式被大多数AOP语言所支持,并且在AspectJ、AspectC++、CARMA等语言中作为缺省的实例化方式。在JBossAOP、AspectWerkz、CaesarJ等语言中,则可以使用类似“perVM”的关键字来指定其单实例方式,它表示在一个虚拟机执行环境内,只存在该方面模块的唯一实例。在多实例的情况下,每个方面模块可以在运行环境中生成多个实例,分别具有不同的作用域范围。一些语言对这些作用域范围提供了不同的划分粒度,例如,在JBossAOP、AspectWerkz、CaesarJ、JAsCo等语言中,除了前面提到的“perVM”之外,作用域范围还可以是“perClass”、“perInstance”、“perObject”、“perJoinPoint”、“perClassJoinPoint”、“perThread”、“perMethod”等,每种语言使用的具体名称和支持的种类会有所不同;在AspectJ中,可以使用“perThis”、“perTarget”、“perCflow”、“perCflowbelow”、“perTypewithin”来表示;在CAM/DAOP和JAC等支持分布式应用的AOP语言中,还可以使用“perRemoteProcess”和“perHost”等作用范围。上述这些实例作用域范围的具体含义可参考相关文献,它们都是由每种AOP语言预先定义好的,但可能无法满足特定情况下的使用需求。针对这种情况,某些语言甚至可以让用户自定义实例作用域范围,这种方式更为灵活。在AspectC++中,可以通过在方面模块中定义静态aspectof()方法来实现不同的作用域范围;在CaesarJ中,则可以通过AspectContainerIfc和AspectDeployerIfc的子类来自定义作用域范围;而在JAsCo和AspectWerkz中,还可以使用自定义的方面工厂(aspectfactory)或方面容器(aspectcontainer)来决定实例的作用域范围。方面模块实例化过程中的另一个特点则是其实例化的形式,不同语言中可能采取隐式实例化(implicitinstantiation)或显式实例化(explicitinstantiation)的方式。所谓隐式实例化指的是,用户不需要在程序代码中明确生成某个方面的实例,系统会在初次使用一个方面模块时自动为其生成相应作用域范围内的实例。对于那些可能具有多个实例的方面模块来说,每次具体调用哪一个实例则由系统的运行环境提供保证机制。在FuseJ、JBossAOP、AspectJ、ObjectTeams等语言中都采用这种方式;而在CaesarJ中,可以通过在方面模块的定义中使用deployed关键字来指定其实例化是在系统启动时隐式完成的。相对于此,显式实例化则需要用户在代码中明确创建某个方面模块的实例。很多AOP语言中,如AspectS、CaesarJ、PROSE、JAsCo、EAOP等,都使用new操作符来创建实例,这与面向对象语言中的方式是一致的;而在Steamloom中创建方面实例时,还需要先显式创建切入点和通知的实例,再将它们组装成方面的实例,虽然这样比较麻烦,但可以提高灵活性。3.3回归系统的tp-ls-map接口机制Introduction机制也称为inter-typedeclaration或openclass,即在不改变原有代码的前提下通过方面模块改变类的静态结构,可以向指定类中插入新的属性、方法或声明新的接口。这种机制淡化了面向对象程序固有的数据封装特性,使得程序的编写更加灵活,也是很多AOP语言所提供的一种主要功能。该机制主要基于mixin技术,通过改造类继承体系来实现所需功能。在AspectJ中还可以在方面模块中通过“declareerror”或“declarewarn”等关键字针对基础程序的静态结构声明编译时错误或警告,这实际上也是一种特殊的introduction功能。相对于AOP语言,一些新兴的面向对象动态语言,如ruby,则直接在语言基础架构上就能提供对introduction机制的支持。3.4分类模型生成在其他一些AOP语言中采用了不同的模型实现了所需的AOP功能。用于自适应编程(adaptiveprogramming)领域的Demeter提出了一种基于traversal机制的AOP语言,由于程序的静态代码结构不能事先确定,因此无法使用常规的pointcut-advice机制来实现AOP功能。系统需要使用高度抽象的描述语言所定义的类图(classgraph)来指定应用程序的组成结构及各部分之间的相关性,并将程序的运行时状态构建成一个对象图(objectgraph)。系统通过traversalmethod中定义的策略可以在对象图上按照特定路径进行遍历和搜索,并在遇到符合条件的对象时调用相应的访问者对象(visitorobject)执行所需的AOP功能,这样可以满足自适应编程领域的特定应用需求。基于subject-orientedprogramming思想所设计的HyperJ采用的是一种称为compositor的模型来实现AOP功能。这种模型并不明确区分基础程序和方面程序,而是将所有的系统组成部分都看作是具有同等地位的hyperslice,并通过compositionrules将其集成为完整的hypermodule。这种模型中没有单独的方面模块,每个hyperslice都可以看作是组成完整系统的一个方面,这一点与文中第4.5小节所描述的CRX模型具有相同的思想。虽然HyperJ是最早出现的AOP语言,但现在很多新设计的AOP机制又回到了当初的这种模式。4研究人员和用户更快、更快的实现机制当前AOP语言种类繁多,功能各异,各具特点,对这些语言进行有效的分类,可以帮助研究人员及用户更快、更好地掌握其核心特点和实现机制。本章试图根据表3中列出的主要语言特性对AOP语言进行分类,其中每种特性都可以将AOP语言分为若干类别,而这些分类的依据也从一定程度上反映了人们在研究过程中对AOP语言认识和理解的逐步深化。4.1非对称式aop语言如前文所述,AOP最初产生于对OOP的补充和完善,这使得AOP语言的语法结构被自然地划分为基础语言和方面语言两大部分,而这两部分的语法特点及差异也形成了早期对AOP语言的主要分类方式。早期,AOP语言大多以某种传统的OOP语言为基础,通过添加关键字或特定语法结构等方式对其进行扩展,使之成为支持AOP的方面语言。这种扩展方式使得方面语言与基础语言各自具有不同的语法结构,因而也被称为非对称(asymmetric)的AOP语言。例如以AspectJ为代表的一批语言,它通过aspect、pointcut等关键字对Java或其他基础语言的语法进行了扩展,从而使其支持AOP的概念。这种方式需要对编译器进行相应扩展,往往不是一件简单的事情。类似AspectJ的语言虽然语法上是非对称的,但是其方面语言仍然具有与基础语言基本相同或相似的编码风格,用户比较容易学习掌握。而另一部分非对称的AOP语言则融合多种语言来对基础语言进行扩展,从而形成与基础语言风格迥异的方面语言。这种方式能够充分发挥各种语言的特性,并提供额外的灵活性。在如前所述的JBossAOP、SpringAOP中,方面语言是由Java和XML共同组成的,这种方式使得通知模块可被单独重用,或者仅仅修改XML中的配置就可以改变切入点与通知之间的关联,但其缺点在于一个方面模块可能会分散于多个文档中。在CARMA和OReA中,则融合Smalltalk和Prolog作为其方面语言,它利用Prolog强大的声明性(declarative)功能使得切入点的定义更加灵活便捷。相对于非对称性而言,对称(symmetric)的AOP语言则属于另一种类型。例如AspectS,其方面语言是通过在基础语言之上开发一套扩展API来实现的,这使得其基础语言和方面语言都使用相同的标准Smalltalk语法,开发人员无需学习新的语法结构就可以编写AOP程序,其所编写的方面模块与类模块具有完全相同的代码结构。由于这种方式不需要修改底层的编译器等部分,因而具备较强的灵活性,用户可以方便地对API进行扩展而提供自定义功能。而类似HyperJ的这些语言,其基本语法结构在设计之初就能够提供对AOP的直接支持,因而不存在基础语言与方面语言之分,所以也属于对称的AOP语言。在Java5.0发布后,很多AOP语言开始尝试使用Java的annotation机制来定义方面及其组成部分。JBossAOP和SpringAOP在这方面都有比较成熟的方案,而AspectJ的新版本中也添加了对annotation的支持。这种方式的特点在于,在不需扩展基础语言的语法结构,也不需使用外部配置文档的情况下,就能够编写AOP程序,并且这些程序能够在标准Java虚拟机上运行,因而也属于一种对称性的解决方案。使用这种方式的AOP语言,往往需要由其底层架构(infrastructure)或运行环境对annotation中的元数据进行读取和处理,并供系统进行调用。需要指出的是,这种依据语法结构对称与否的分类方式,是AOP语言发展早期一种比较初级的方法,虽然简单明了,但不能有效揭示出各种语言之间的本质差异及动态特征。例如,类似AspectJ与AspectWerkz这些从语法结构上来看分属于不同类型的语言,它们在各种功能特性上又是极其相似的。显然还需要从其他的特征入手对AOP语言进行分类。4.2动态横切逻辑的支持织入机制(weaving)是AOP语言最重要的底层技术。虽然用户通常并不需要具体知道方面是如何织入到基础程序中去的,但是不同的织入方式往往使AOP语言具有不同的功能特性,本节从织入逻辑的表达方式对AOP语言加以分类。织入逻辑指的是在基础程序的哪些地方可以调用通知,也就是连接点的类型。从总体上看,它可以被分为静态横切(staticcrosscutting)和动态横切(dynamiccrosscutting)两大类。所谓静态横切指的是AOP语言的织入逻辑仅依赖于基础程序的静态代码结构,即直接说明在程序代码的某个位置上进行调用或组合。这类语言以HyperJ为代表,它在compositionrules中可以直接以名称映射的方式把若干hyperslice单元集成为hypermodule,描述方式上无需考虑程序的动态特性。这种表达方式简单直观,但支持的表达逻辑不够丰富,受到代码结构的较大限制。目前静态横切的表达方式更多地用于AOP语言的introduction或mixin机制中,用来改变程序的静态结构,即在不改变模块代码的情况下,可向模块中加入某些属性或方法,甚至改变其继承关系。而动态横切指的则是织入逻辑依赖于基础程序的运行时状态,即在程序执行过程中的一些特定时刻来进行通知的调用或组合。这些常见的运行时刻包括:方法的调用或执行、属性的访问与更改、对象的创建或销毁、异常抛出等。需要指出的是,如前文所述,这些特定时刻虽然从概念上指的是程序的动态运行状态,但是能够方便地将其与程序代码中的某个静态位置对应起来,如对方法的调用表达式、对属性的赋值表达式等。这种对应关系就被称为joinpointshadow,它的最大好处在于为后续的织入过程提供了方便。目前以AspectJ为代表的绝大多数AOP语言都支持这种类型的动态横切逻辑,但每种语言所支持的具体种类会有所差别。在AspectJ、JBossAOP、SpringAOP等语言中还可以用类似“cflow”的关键字来表示一种基于程序控制流的动态织入逻辑,用来表达一些复杂的控制流条件。例如,系统要求捕获对方法A的调用,并且这些调用必须是发生在方法B的执行过程中。这种情况可以用类似“cflow(B)&call(A)”的表达式进行描述。另外,对于方法的调用顺序、递归调用的次数等信息也可以加以限制,使织入逻辑的描述更加灵活和丰富。但对这些织入逻辑的支持,往往需要对程序的运行时堆栈进行扫描和检查,以确定控制流状态,这会造成较大的运行时开销。其他一些AOP语言则根据其应用领域或语言特征而支持一些比较特殊的织入逻辑表达方式。例如:在AO4BPEL中可以基于某个业务活动的执行捕获连接点,这与其在工作流和Web服务领域的应用需求是分不开的;FuseJ除支持方法执行之外,还支持JavaBeans的事件触发时刻作为连接点;在CaesarJ、PROSE、Steamloom中则支持基于线程的织入逻辑,能够对线程的属性进行限制和过滤;在CARMA中,可以针对Smalltalk语句块的执行(blockexecution)进行捕获,这是依据特定基础语言的语法结构而设计的;文献[47-48]都提出了基于数据流的连接点类型,可以针对数据的访问和赋值,以及数据用作方法的参数和返回值等情况来描述织入逻辑;文献分析比较了上述几种不同的动态织入逻辑表达方式之间的差异,并提出了一套称为joinpointdesignationdiagrams的符号标记来统一描述这些动态织入逻辑的执行过程;文献则尝试综合利用多种不同的程序语义模型来描述织入逻辑,以提供更大的灵活性和模块化程度。上述这些类型的织入逻辑表达方式对现有的连接点类型进行了补充,但在实用性上还存在一些问题,目前大都还处于研究、探讨阶段。4.3动态织入行为虽然织入逻辑能够以静态和动态两种方式来描述织入的位置,但是织入行为的执行时间并不依赖于这些表达方式的差异,而是取决于AOP语言的具体实现方式,即静态织入(staticweaving)或动态织入(dynamicweaving)两种类型:静态织入发生在程序执行之前,而动态织入则发生在程序执行过程中。这两种方式使得不同类型AOP语言的实现方式及功能行为具有较大差异,也是当前对AOP语言进行分类的主要标准。采用静态织入的典型代表是AspectJ、AspectC++等,这也是早期AOP语言所采用的主要实现方式。由于织入行为通常在编译过程中完成,发生在程序运行之前,因而需要扩展的编译器或预处理器提供支持。静态织入的显著特点在于,织入完成后,所有对通知的调用指令会被直接插入到joinpointshadow处,织入后的程序具有与基础语言相同的指令结构,因而无需对程序的运行环境加以改变,并且程序的运行时效率不会受到太大影响。但其缺点在于,无法根据程序的运行时状态提供对通知的动态调用,也不容易实现反织入操作(即取消方面模块对基础程序的影响)。而在AspectS、SpringAOP、PROSE、Steamloom等语言中则采用了动态织入方式,与静态织入正好相反,其织入行为发生在程序运行过程中。基础程序在运行之前并不包含对通知的调用指令,系统在运行过程中对切入点进行匹配并调用相应的通知。虽然动态织入会给程序的运行时效率带来负面影响,但却具有更大的灵活性,因为程序在运行前并不知道有哪些方面模块会被织入,而这些方面模块甚至可以是在程序运行过程中才动态定义的。另外动态织入也有利于实现反织入操作,这往往是静态织入所做不到的。另外,在某些AOP语言中,如JBossAOP、JAC、EAOP等,其织入行为虽然也常常被称为动态织入,但却与典型的动态织入有所区别。在这些语言中,系统会在编译阶段或类装载阶段,由编译器或类装载器对基础程序中的连接点位置进行预处理,例如:JBossAOP会在这些位置插入一些钩子结构(hook);EAOP则会插入事件触发指令。这些插入的结构并不包含直接调用通知的代码或指令,而是在程序执行到这些位置的时候,会被运行环境所捕捉,并根据运行时状态对切入点进行匹配,继而调用相应的通知。这种实现方式介于静态织入和动态织入之间,部分操作需要在程序运行前完成,能够提高程序的运行时效率,同时又保留了动态织入的灵活性,因而也被称为预织入(preparatoryweaving),目前在很多工业化的AOP语言中多采用这种方式。为了提高织入行为的执行效率,文献提出了一种称为continuousweaving的优化机制。它通过对程序控制流进行预先分析,首先对那些处于控制流前端的连接点进行织入,而对其他后续连接点的处理则尽量推迟。只有当程序控制流到达前端的连接点时,才会逐步处理后续连接点。由于控制流会因程序分支发生跳转,部分代码不会被执行,使得可能的织入范围会根据运行时状态逐步缩小,从而降低织入执行次数。但是由于这种机制需要对控制流进行预先分析,因此不能很方便地用于完全动态的环境中。文献提出了被称为envelope-basedweaving的优化机制,其思想类似于面向对象设计中的get/set机制,通过特定的envelope结构将程序中的属性访问和方法调用进行封装,使得需要在多个连接点上完成的织入操作,可以被转化为仅需改变某一个envelope结构内的调用逻辑,从而减少所需织入的位置及开销,简化了织入过程。对于静态织入和动态织入来说,这都是一种可采用的优化机制。4.4动态织入的实现方式织入行为的结果特征所表现的是基础程序的结构是否会受到织入行为的影响。这是实现AOP语言织入机制的一个重要组成部分,也会对AOP语言的分类产生重要影响。如4.3小节所述,采用静态织入的AspectJ等语言会在编译阶段把通知的调用指令直接插入到基础程序的字节码中,从而改变了程序原有的代码结构,因而被称为侵入式织入(invasiveweaving)。显然,静态织入的AOP语言通常会采用这种实现方式,它对程序的运行时效率影响较小,但会增加程序的编译时间,并且不利于反织入功能的实现。某些采用动态织入的AOP语言也会采用侵入式的实现方式。例如JBossAOP这类采用预织入的AOP语言,由于需要在连接点处插入类似钩子的结构,同样会改变程序原有的代码结构,因而也被看作是侵入式的。虽然这些语言也可以提供动态的反织入功能,但很难完全消除这些钩子结构的痕迹,难以使程序的代码结构恢复到原始状态。在Steamloom中,虽然对织入位置的确定和具体的织入行为都发生在程序运行期间,但所有受到影响的代码模块都要由即时编译器(just-in-timecompiler)进行重编译,使得程序原有的代码结构因此改变,因而也是侵入式的。但由于动态重编译的使用,其反织入功能仍然可以使程序恢复到织入前的代码结构。其缺点在于可能产生较大的运行时开销而影响程序的执行效率,目前一些研究工作都在努力降低这种负面影响。另一方面,非侵入式(non-invasiveweaving)的实现方式则不会直接改变程序原有的代码结构,而是通过其他方式保证通知会在适当的时候被调用。这种方式通常与动态织入的AOP语言相结合,并根据每种语言自身的特点,其实现方式也具有多样性,相互之间差异很大。在SpringAOP和Dynaop中采用了Java语言中的动态代理(dynamicproxy)机制来处理对通知的调用。由于代理对象可以在运行过程中创建或销毁,不会改变相关源对象的结构,因此不会对程序原有的代码结构产生影响,具有非侵入式的效果。在AspectS中则利用了Smalltalk强大的反射机制和methodwrapper技术,在元模型层次上改变methodtable结构中方法名与方法体之间的链接关系,使得对通知的调用能够被插入到方法的调用链中。该过程不会对方法的字节码结构进行任何改动,并且通过改变指针指向就可以很容易恢复方法名与原有方法之间的链接关系,这使得反织入功能得以实现,不会在程序中留下任何痕迹,因而也是一种非侵入的织入方式。对于AO4BPEL,由于程序是解释执行的,可以通过扩展解释器,在程序执行到连接点时进行检查和判断。如果符合特定条件,则由解释器调用相应的通知。由于所有的AOP功能都是由解释器负责完成的,不需要改动基础程序的任何结构,它也是一种非侵入式的织入方式,并且适合于一些解释型的运行环境。在PROSE中,通过把程序运行中遇到的连接点封装成运行时事件及相关上下文信息,由运行环境对其进行捕获和分析,并调用相应的通知。其事件触发过程是由底层运行环境支持的,不需要在基础程序中插入任何代码或指令,因而也是非侵入式的。实际上这种实现方式类似于程序调试器框架,可以把连接点看成由系统设置的程序断点,当执行到断点处时会由系统自动抛出相应事件。4.5织入机制的生成文献根据织入行为的执行过程提出了一种不同的分类方式:回溯式织入(reactiveweaving)与非回溯式织入(nonreactiveweaving)。这种分类方式基于一种被称为CRX的AOP语言模型。在该模型中,方面程序不再被视为处于基础程序的从属地位,它们是整个系统中具有同等地位和不同功能的关注点(concern),这些关注点通过集成规则(rule)由织入机制绑定在一起,从而构造出相互横切的完整程序,即所谓的C×R->X模式。这种模型弱化了基础程序和方面程序之间的语法差异,从更加抽象的层次统一了AOP语言的语义模型。虽然传统上认为方面程序是对基础程序的横切,但从另一方面看基础程序也同样是对方面程序的横切,因此CRX模型更加符合当前对AOP语言的深入认识和发展潮流。在此基础上,非回溯式织入机制指的是整个织入行为的执行过程仅仅依赖于C和R,而不需要依赖于X,类似于一种线性的执行过程,HyperJ的实现被视为这种织入机制的代表。与此相反,回溯式织入机制指的是织入过程不仅仅依赖于C和R,还需要根据当前X的状态做出响应,整个过程构成了一个环状结构,而这种情况在很多AOP语言的实现中都可以找到原形,例如AspectJ和EAOP。这种分类方式不同于以前那种传统的从下至上简单地对已有的AOP语言进行归纳分类的方法,而是采用了一种从上至下的模式对AOP语言的实现机制进行分析细化,更便于设计开发新的织入行为实现细节,因此在某些AOP语言中的不同功能模块的具体实现上,既可以设计成回溯式的,也可以设计成非回溯式的。5aop语言研究的主要热点5.1方面模块的部署应用于分布式环境中的AOP语言是一个热门的研究方向,提供灵活的分布式支持对于AOP技术的商业应用也是一项必不可少的要求。目前的一些AOP语言,如JAsCo、CAM/DAOP、JAC等,以及相关研究也支持基本的分布式应用。其首先需要解决的问题就是如何通过适当的机制把集中管理的方面模块部署到分布式环境中。远程部署(remotedeployment)是解决这一问题的典型方案。方面模块只在定义它的节点上被实例化,并通过远程部署器在相关远程节点上生成方面存根(aspectstub)。所有在远程节点上对方面模块的调用都会通过存根转发到方面实例所运行的节点上。这种方式由于需要显式指定方面模块部署的目的地而无法较好地支持自动部署。由于对方面模块中通知的调用必须通过RMI等远程调用机制来实现,相应地会增加运行时的网络负载。此外,在CAM/DAOP、JAC中还提供了另外一种称为分布式注册(distributedaspectregistry)的方式,通过在分布的节点中加入互相联通方面注册模块,方面实例可以在其实例化后按照配置规则被自动复制到相关节点上。这种方式使得通知的执行可以直接在节点本地完成,因为每个节点上都有相应的拷贝,从而可以提高程序的运行时效率。该方法虽然可以提供较好的自动部署,但如何在分布式环境中提供方面模块的动态同步则需要更加细致的实现。5.2eptsfearwell的元模型方案大量新兴的AOP语言,都使用相似而又各具特点的方法来对横切关注点进行封装,因此一些研究人员开始关注AOP语言的元模型,用以表示语言中最核心的基础概念和特性,便于更加准确快捷地理解AOP语言的核心本质,并能提供在各种AOP语言间进行比较和帮助新语言开发的工具。文献提出了一种称为公共语言概念框架(commonlanguageconceptsframework)的元模型方案,它将AOP语言中的公共概念及其之间的联系划分为四个部分:连接点元模型(joinpointmetamodel)、切入点元模型(pointcutmetamodel)、方面绑定元模型(aspectbindingmetamodel)和通知元模型(advicemetamodel)。任何AOP语言中的语言特征通过映射可以转化为元模型中的相应概念,这样所有的语言可以在一个共同的框架下进行比较。通过对元模型进行特例化(specialization)处理,能够描述指定语言的特性,用以突出不同语言间的差异性。在这个元模型方案中甚至还提供了一个基于Smalltalk的解释器实现,它能够通过解释运行元模型实例来描述AOP语言中的公共概念的操作语义。这为比较或设计新语言提供了一个方便的实验环境。在当前的研究中,对AOP语言元模型的研究常常与AOP语言的语义及形式化方法的研究相结合起来,如AspectSandbox等,它们的共同目的都是为了探究AOP语言的本质。该领域还有大量参考文献[19,20,62,63,64,65]可供查阅,因篇幅所限,该文不进行详述。5.3方面模块之间的相互干扰在基于AOP开发的系统中,一种常见的情况是多个切入点都匹配了同一个连接点,从而导致多个方面模块需要绑定于程序中的同一个位置,而这可能造成多个方面模块之间的相互干扰(aspectinterference)。虽然AspectJ针对此情况提供了基本的优先级规则,但如果这些方面模块来源于不同的程序开发模块则仍然可能导致此问题。文献分析了在已经织入方面模块的字节码中再次织入第三方方面模块时可能导致的相互干扰问题,并提出了相应机制来限定每个方面模块中切入点的作用域范围来避免干扰。文献构建了一种数学模型来描述多个方面模块组合过程中的干扰问题,并在该模型的基础上分析了问题产生的原因以及解决方案。文献则关注多个方面模块之间的递归干扰问题。由于某个方面模块中的切入点可以匹配另一个方面模块中的连接点,从而有可能导致多个方面模块之间的循环引用而产生自我递归问题。该文献通过分层的方式组织方面模块,规定某个方面模块中的切入点只能匹配其下层方面模块中的连接点,从而有效避免了方面模块中的自我递归问题。5.4促进不同语言之间的融合,避免异构和异构的互通上述关于多方面干扰的诸多研究工作也自然促进了多种AOP语言之间的相互融合。由于很多AOP
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 数字化技术对传统媒体的冲击
- 设备员年度工作总结
- 佳沃水果摊点合伙经营协议书模板
- 帕金森病疼痛患者的临床特点及对其生活质量的影响
- ZnO基纳米复合材料的制备及其光电性能的研究
- 2024年郑州市中牟县招聘中小学在职教师笔试真题
- 基于深度学习的肺部CT重建方法研究
- 二零二五年度墓园墓地购置与墓碑雕刻、墓园设施租赁及后期维护服务协议
- 二零二五年度运输车司机雇佣与车辆安全检查合同
- 二零二五年度消防安全宣传与教育培训合作合同
- 环境地质学第一讲-绪论课件
- DB6523-T 387-2023 苹果小吉丁虫监测调查技术规程
- 汽车维修工时收费标准(二类企业)
- (医学课件)腰椎穿刺术课件
- 参保人员转诊就医审核意见单
- 动物免疫接种技术课件
- 大班健康《换牙我不怕》课件
- 93年国际大专辩论赛经典辩词
- 冻猪肉储备投标方案
- 临床科室综合目标管理考核标准
- 幼儿园大班绘本故事-神奇的大蒜【幼儿教案】
评论
0/150
提交评论