面向方面编程中必要语义约束的研究
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
面向方面编程中必要语义约束的研究1
杨剑青,杨宗源,谢瑾奎
华东师范大学计算机科学技术系,上海 (200062)
E-mail:learwwj@
摘要:面向方面编程(AOP)可以很好地解决非功能性关注点(Non-Functional Concern)的封装问题,但是目前的AOP技术过于灵活,甚至会不恰当地破坏原有对象的封装性,以及代码正确性。
本文讨论了在软件设计或者编程实现过程中,对于AOP应该加以适当约束,具体包括常量性质的约束,单件(Singleton)类型的约束等等,在各个不同的具体应用领域中,还可以相应有不同的约束,通过这些约束,AOP才能更好地为软件开发所服务。
关键词:AOP;约束;软件开发;软件工程
中图分类号:TP311
1.引言
面向方面编程(Aspect Oriented Programming,以下称为AOP)可以用来解决软件开发中,对于系统级关注点实现的代码纠缠和难以管理等问题。
但是对其任意使用可能在某些情况下,造成对原有面向对象模型语义的破坏,本文提出了在现有的AOP实现机制上,添加适当的约束来解决这一问题,并且刻画出了所提出约束的设计模式。
2.AOP简介
面向对象方法已经在软件工业界取得了巨大的成功。
面向对象方法通过把不同大小级别的功能单元封装在类中,来提高软件的开发效率。
但是,对于一些系统级别的,非功能性的关注点(Non-Functional Concern),比如操作同步,资源的互斥,数据加密解密,数据的压缩解压缩等等,面向对象技术的解决方案却会引入一系列的问题,比如代码纠缠(Code Tangling)和难以管理[2]。
所谓代码纠缠,就是指相同的代码反复地出现在不同的类中,而没有办法使用面向对象方法(比如继承)加以消除;而难以管理主要指的是面向对象技术只提供了封装功能关注点的单元,而没有提供封装横切关注点(Cross-Cutting Concern)的单元,导致这些关注点的修改十分困难。
AOP的方法对于解决上述问题能够提供极大的帮助,使用AOP的方法,开发人员可以显式地捕捉横切关注点的结构,将横切关注点封装为一个单独的实体,并且可以便捷地修改实现横切关注点的代码,下面结合最常使用的AOP实现,AspectJ (/ajdt)来简单介绍一下AOP中的一些重要概念[3]:
a. Join Point是在程序的执行流程中具有良好定义的点,AspectJ提供几种不同类型的Join Point,方法调用Join Point,域设置Join Point以及异常抛出Join Point。
比如calls(void Rectangle.setWidth(float))定义了所有在Rectangle对象上,对方法setWidth的调用。
b. Pointcut是Join Point的集合,比如Pointcut setArea() : calls (void Rectangle.setWidth(float)) || calls(void Rectangle.setLength(float)),定义了所有在Rectangle对象上,对方法setWidth和setLength的调用。
c. Advice是当程序执行到Pointcut时所运行的代码。
Before Advice在程序运行到Join Point,计算执行之前运行;After Advice在Joint Point结束,计算执行之后运行;Around Advice 在程序执行到Join Point时开始运行,并且对该Join Point的计算是否确实运行有显式的控制。
1本课题得到高等学校博士学科点专项科研基金(项目编号:20060269002)的资助。
d. Introduction 是一种可以向类中添加新的域,以及改变类的继承关系的机制,但和Advice 有所不同,Introduction 是在编译时静态实现的。
上文介绍的一些概念,就是在使用AspectJ 进行AOP 开发时经常使用到的一些编程实体,下面就结合一个具体的实例,提出无约束地使用这些元素所带来的问题。
3. 无约束AOP 引起的问题
3.1问题描述
任意地使用AOP 中提供的各种机制,有可能会引起一系列的副作用。
比如没有约束地使用AOP 会严重地破坏原有面向对象模型的封装性,改变原有对象中域的值以及方法的执行流程,从而导致了原有对象的语义发生变化。
下面所要给出的一个实例就是通过使用AOP 来改变了原有对象的行为,从而使程序中出现了难以发现的错误。
3.2实例
实例中的原有类(即实现功能关注点的类)是一个结构十分简单的类Rectangle ,其中只有两个分别代表长和宽的域width 和length ,以及这两个域的setter 和getter 方法,实现功能性关注点的方法是getCircumference 和getArea 分别计算类Rectangle 的周长和面积。
Rectangle 的类图如下所示:
图1 Rectangle 的类图
BadAspectDemo 中只有一个main 方法,简单地构造一个Rectangle 类的对象,然后设置width 和length 的值,最后调用它的getCircumference 方法和getArea 方法,以及输出计算结果。
具体代码如下所示:
public class BadAspectDemo {
public static void main(String[] args) {
Rectangle r = new Rectangle();
r.setLength(4);
r.setWidth(5);
System.out.println(r.getCircumference());
System.out.println(r.getArea());
}
}
在没有使用AOP 技术的情况下,这段程序的输出结果一目了然,必定是18.0以及20.0,但是在采用了AOP 对Rectangle 中的一些方法进行了增强以后,输出结果就会发生十分微妙的变化。
为了对Rectangle类,进行增强,本实例中采用的环境是ajdt 1.5开发包以及eclipse 3.3,在给项目加上AspectJ特性以后,再重新运行BadAspectDemo,输出结果就会变成36.0以及20.0,面积的计算结果仍然正确,但是周长的结果变成了原来的2倍,可见,Rectangle的getCircumference方法的逻辑或者语义已经被改变,Rectangle类的封装性已经被破坏了,具体破坏封装性的Aspect代码如下所示:
public aspect BadRectangleAspect {
pointcut calculateCircumference(Rectangle r):
Rectangle.getCircumference())&&target(r);
call(float
r):calculateCircumference(r){
before(Rectangle
doubleSizeAndLength(r);
}
after(Rectangle r):calculateCircumference(r){
halfSizeAndLength(r);
}
private void doubleSizeAndLength(Rectangle r) {
2);
r.setLength(r.getLength()
*
2);
*
r.setWidth(r.getWidth()
}
private void halfSizeAndLength(Rectangle r) {
r.setLength(r.getLength()
2);
/
/
2);
r.setWidth(r.getWidth()
}
}
BadRectangleAspect这个Aspect对Rectangle类中的getCircumference方法作了拦截,在调用这个方法之前,执行了BadRectangleAspect中的doubleSizeAndLength私有方法,将Rectangle的width和length域的值设置为原来的2倍,所以在执行getCircumference方法计算周长时,程序使用的width和length值是原来的2倍,因此计算结果也就变为了正确值的2倍;而在调用getCircumference方法之后,BadRectangleAspect又去执行了私有方法halfSizeAndLength,将Rectangle的width和length域设置为当前值的1/2。
所以在执行getCircumference方法后,width和length又变回了正确的值。
getArea方法计算的Rectangle 面积仍然是正确的,程序就输出了变成了令人迷惑的结果。
上面这个实例中,Rectangle采用的是面向对象技术进行功能性关注点的建模,其代码都是正确无误的,但是BadRectangleAspect对Rectangle类,进行了增强以后,在不修改Rectangle类代码的前提下,反而扭曲了Rectangle类的行为,破坏了原有的封装性,并且这种情况的破坏是没有任何收益的。
而且通过这种方式引起的软件错误往往更难定位,因为绝大多数的开发人员都会将注意力集中在实现功能性关注点的Rectangle类中,而容易忽略实现非功能性关注点的Aspect中的代码。
综上所述,AOP虽然是一种解决横切关注点的利器,但是如果对其的使用过于的随意,则将会导致极其不理想,甚至是危险的结果,因此,下面一个部分就着重提出了几种Aspect 的约束[5],并且给出了实现这些约束的设计模式,通过使用这些设计模式来实现约束,可以让AOP更好地为开发人员服务。
4.对AOP施加适当的约束
4.1 Constant型的Aspect
在上面这个实例中,就是由于BadRectangleAspect中的Before Advice对Rectangle的域,进行了写操作,从而改变了getCircumference的行为以及正确性。
因此,对于AOP的应用必须要加以约束。
在设计AOP程序或者实现AOP机制时,应该可以通过在Aspect上定义关键字的方法来为AOP添加约束,虽然现在的AspectJ还没有这种机制,但是后续的版本应该要提供这种特性。
首先Aspect应该可以有Constant型的约束,这种Aspect的功能只是增加原有类中没有的功能,而不是改变它们,首先Constant型的Aspect中不可以有Around类型的Advice,因为Around类型的Advice实际上是重写了被拦截的方法,有可能改变被拦截方法,也就是原有类中方法的逻辑;其次,Constant型的Aspect不能调用任何修改运行时信息或者原有类本身数据的方法,因为这样会导致不可测的运行结果,而且引起的错误难以发现。
此外,Constant型的Aspect比较适合做记录日志以及追踪程序运行状态等类型的工作。
如果要在系统中实现Constant型的Aspect,则需要实现以下的辅助Aspect,其设计模式如下(建模的方法参见[7]):
图2 Constant型辅助Aspect的设计模式
上图所示的是实现Constant型Aspect的辅助Aspect ConstantHelperAspect,基于这个Aspect,可以实现各种不同的Constant型Aspect。
首先,这个ConstantHelperAspect必须被赋予最高的优先级,先于其他的Aspect执行;其次,这个ConstantHelperAspect,通过Pointcut changeField,拦截了所有对关键域criticalField的赋值操作,并且使用Around Advice来判断赋值操作的调用者是原有类DomainModel,还是其他的Aspect。
ConstantHelperAspect中使用了类型间的声明(Intertype Declaration),在原有类DomainModel添加了,域invoker以及invoker的setter和getter方法来记录赋值操作的调用
者。
在Constant型的Aspect中实现相应功能时,必须给这个域赋值,这样当Constant型的Aspect中错误的设置关键域的值时,这个ConstantHelperAspect就可以给出警告信息。
4.2 Timing型的Aspect
在各个不同的应用领域中,还可以有相应的不同约束来规范Aspect的行为。
比如在实时系统中,操作的时间限制是一个重要的需要考虑的问题,有些操作是否执行取决于另外一些操作是否在时限之内完成,这时就可以使用Timing型的Aspect来完成这种任务。
Timing型的Aspect应该可以设置一个操作时间的上限,这个时间上限对应的不是Aspect 本身的代码执行时间,而是它所拦截的方法的执行时间,如果超过这个时间上限,那么Aspect 可以报警,或者采取一些补救时间的措施来使系统继续运行。
对于Timing型的Aspect可以采用如下的设计模式实现:
图3 Timing型的Aspect设计模式
如上图所示,TimingAspect中拥有域startTime和endTime,分别代表被拦截方法的开始执行时间和结束执行时间,静态域TIMELIMIT则代表被拦截方法的执行时限。
Pointcut doRealTimeTask负责拦截具体执行实时任务的方法,在该方法执行前,Before Advice设置被拦截方法的开始时间,待该方法执行完毕后,After Advice设置被拦截方法的结束时间,并且计算被拦截方法的执行时间是否超过规定时限,并且采取相应的措施,最后将startTime 和endTime置空,以防止这两个域被其他Advice误用。
4.3 Singleton型的Aspect
除了Constant型,以及Timing型的Aspect以外,有时候AOP还应该提供Singleton型的Aspect,这种Aspect的特性和设计模式中的Singleton有很大的相似之处,即只有一个实
例横切整个的应用程序中,并且这个实例在应用程序的执行过程中,随时都可以被使用,可以完成对于整个应用程序而言,全局的,单一的非功能性应用。
由于这种特性,Singleton型Aspect的Advice有可能在所有的Join Point运行,它比较适合于集中处理或者集中分析性质的Aspect实现,具体来说,这种Singleton型的Aspect 可以实现应用程序整体性能分析,收集应用程序信息等全局性质的功能。
要实现Singleton 型的Aspect,可以采用以下设计模式:
图4 Singleton型的Aspect设计模式
SingletonAspect中也拥有域startTime和endTime,其意义和TimingAspect中的相应域相同,methodInvocationCounter记录被拦截的方法数,methodExecutionTotalTime记录被拦截的方法的执行总时间,在不同的应用中,可以设置不同的SingletonAspect域,这里设置的域,是用来计算被拦截方法的平均执行时间的,SingletonAspect的核心是Pointcut doTask,这个Pointcut必须恰当的拦截所有封装功能关注点对象的方法调用,因此通常用通配符进行操作,Before Advice和After Advice负责累加被拦截方法的计数器和计时器,同样,在不同的应用中,这些Advice可以对不同的域,进行不同的逻辑操作。
SingletonAspect中的域通常有2种类型,一种是局部性的,如startTime和endTime,他们的值和操作只和一次拦截有关,因此每次使用完以后,都要置空;还有一种就是全局性的,如methodInvocationCounter 和methodExecutionTotalTime,他们的值和操作就和程序的全局信息相关。
5.结论
本文通过在软件开发中,任意地使用AOP而导致的对象封装性被破坏,对象域的值,
或者方法的逻辑在执行中被不恰当地修改等等问题,提出了AOP的使用应该加以约束。
并且给出了在现有的编程工具下,实现这些约束的模式。
此外,这些约束可以在AOP程序的设计阶段,人为地添加;也可以通过采用修改现有AOP实现的方式,交由开发环境或者编译器做检查。
任何程序语言元素的灵活性,都势必有两面性(比如C和C++中的指针),只有适当地控制这种灵活性,才能让这种程序语言元素更好地为软件开发而服务。
将来的工作可以围绕2点展开,其一是,进一步从编程角度来实现约束,更新AOP的实现机制(比如AspectJ),使其可以用关键字的方式来定义Aspect的约束;其二是从建模角度出发,在建立AOP模型时,对软件的设计模型添加约束,随后通过代码生成的方式来产生已经添加约束的AOP代码框架,再由开发人员编写具体的横切关注点逻辑。
参考文献
[1]ADRIAN COLYER, ANDY CLEMENT. ECLIPSE ASPECTJ中文版--利用ECLIPSE和ASPECTJ进行面
向方面程序设计[M].钱竹春,邹正武,译.北京:清华大学出版社,2006.
[2]Elrad T., Filman, R.E. and Bader A. Aspect-oriented Programming[J].Communications of the ACM, 2001,
44(10):29-32.
[3]Kiczales G., Hilsdale E., Hugunin J., Kersten M., Palm J. and Griswold W.G. Getting Started with AspectJ
[J].Communications of the ACM, 2001, 44(10):59-65.
[4]Ossher H. and Tarr P. Using Multidimensional Separation of Concerns to (Re)Shape Evolving Software
[J].Communications of the ACM,2001,44(10):43-50.
[5]Gray J., Bapty T., and Neema S. Aspectifying constraints in model integrated computing[C]. In Proceedings
of OOPSLA 1999.
[6]Robert J.Walker, Elisa L.A. Baniassad and Gail C. Murphy. An Initial Assessment of Aspect-oriented
Programming[C]. In Proceedings of the 21st International Conference on Software Engineering.
[7]Iris Groher and Thomas Baumgarth. Aspect-Orientation from Design to Code[C]. In Proceedings of the
Workshop on Early Aspects: Aspect-Oriented Requirements Engineering and Architecture Design, AOSD conference 2004.
Research on Necessary Semantics Constraint in AOP
Yang Jianqing,Yang Zongyuan,Xie Jinkui
Department of Computer Science and Technology, East China Normal University, Shanghai
(200062)
Abstract
AOP methodology can perfectly encapsulate the non-functional concerns, but the current AOP technique is too flexible that it may improperly destroy the encapsulation of original object and even the correctness of the code. This paper studies on inflicting proper constraint on AOP methodology in the phrase of software design or programming. These constraints can be constant constraint, singleton constraint and so on. There will also be some domain constraint in different realm of software. It is these constraints that make AOP serve software development even better.
Keywords: AOP; constraint; software development; software engineering
作者简介:
杨剑青(1984-),男,广东大埔人,硕士研究生,主要研究方向:面向方面编程,设计,以及建模工具的支持;
杨宗源(1953-),男,教授,博士生导师,主要研究方向:软件工程、工具及环境、形式化方法;
谢瑾奎(1975-),男,讲师,主要研究方向:抽象状态机、交互计算及复杂性。