AspectJ 编程指南

15 篇文章 0 订阅
7 篇文章 0 订阅

写在前面的话

最近遇到一个需求:用户快速点击某个按钮时,或者快速点某几个按钮时,只能响应第一个。一开始我是拒绝的,后来觉得这很合理。这功能刚好用AOP的编程思想来实现,说到AOP想到的自然是AspectJ。于是用有道翻译,翻译来一下官方文档,本文只翻译来第一部分:Getting Started with AspectJ,有什么不对的多多指教。

AspectJ 编程指南

AspectJ 团队
Copyright © 1998-2001 Xerox Corporation, 2002-2003 Palo Alto Research Center, Incorporated. All rights reserved.

摘要:
本编程指南描述了Aspect语言,一个附带的指南描述了属于AspectJ开发环境一部分的工具。如果您第一次接触AspectJ,您应该先阅读《Getting Started with AspectJ》,对AspectJ编程有一个广泛的认识。如果您已经熟悉AspectJ,想深入理解,您应该阅读《The AspectJ Language》并查看响应章节中的示例。如果想要更正式的AspectJ定义,应该阅读《Semantics》

前言

本指南做三件事:

  1. 介绍AspectJ语言;
  2. 定义AspectJ的每个构造及其语义;
  3. 提供它们的使用示例。

它包括一些附录,这些附录提供了对AspectJ语法的参考、对AspectJ语义的更正式描述,以及关于AspectJ实现的说明。

第一部分,《Getting Started with AspectJ》提供了编写AspectJ程序的简单概述。它还展示了如何在现有的开发工作中分阶段引入AspectJ,从而减少相关的风险。如果这是您第一次接触AspectJ,并且您想了解AspectJ是关于什么的,那么您应该阅读这一节。

第二部分,《The AspectJ Language》使用代码片段作为示例,更详细地介绍该语言的特性。本文涵盖了该语言的所有基础知识,在阅读本节之后,您应该能够正确地使用该语言。

下一个部分,《Examples》包含一组完整的程序,这些程序不仅显示所使用的特性,而且还试图说明所推荐的实践。在您熟悉AspectJ的元素之后,您应该阅读这一节。

最后,有两个章节,一章讲《术语(Idioms)》,另一章讲《陷阱(Pitfalls)》

后面的内容包含几个附录,其中包括《AspectJ Quick Reference 》对其语义的更深入的介绍,以及对其《实现注释所》享有的纬度的描述。

1. Getting Started with AspectJ

概述

许多软件开发人员被面向方面编程(AOP)的思想所吸引,但不确定如何开始使用这种技术。他们认识到横切关注点的概念,并且知道他们过去在实现这些关注点时遇到了问题。但是关于如何在开发过程中采用AOP还有很多问题。常见的问题包括:

  • 我可以在现有代码中使用aspects吗?
  • 我可以期望从使用aspects获得哪些好处?
  • 我如何在我的程序中找到aspects?
  • AOP的学习曲线有多陡?
  • 使用这项新技术的风险是什么?

本章将在AspectJ: Java面向方面的通用扩展的上下文中讨论这些问题。一系列经过删节的例子说明了程序员可能希望使用AspectJ实现的方面的种类以及这样做的好处。想要更详细地理解示例,或者想要学习如何编写这样的示例的读者,可以从AspectJ网站(http://eclipse.org/aspectj)找到更完整的示例和支持材料链接。

采用任何新技术的一个重大风险都是走得太远太快。对这种风险的关注导致许多组织对采用新技术持保守态度。为了解决这个问题,本章中的示例被分为三个大类,其中一些方面更容易被本章前面的现有开发项目采用。下一节介绍AspectJ,我们将介绍AspectJ的核心特性,在开发方面,我们将介绍一些方面,这些方面有助于完成调试、测试和应用程序的性能调优等任务。在下面的“生产方面”一节中,我们将介绍实现Java应用程序中常见的横切功能的方面。我们将推迟讨论第三类方面,可重用方面,直到AspectJ语言。

这些类别是非正式的,并且这种顺序并不是采用AspectJ的唯一方法。有些开发人员可能希望立即使用生产方面。但是我们对当前AspectJ用户的经验表明,这种顺序允许开发人员快速获得AOP技术的经验(并从中受益),同时最小化风险。

AspectJ概论

本节将简要介绍本章后面使用的AspectJ的特性。这些特性是语言的核心,但这绝不是AspectJ的完整概述。

使用一个简单的图形编辑器系统展示了这些特性。一个Figure由许多FigureElements组成,这些FigureElements可以是Point(点),也可以是Line(线)。Figure类提供工厂服务。还有一个Display(显示器)。本章后面的大多数示例程序也是基于这个系统的。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mJ7graQK-1589899660860)(https://www.eclipse.org/aspectj/doc/released/progguide/figureUML.gif)]

AspectJ(以及面向方面编程)的动机是认识到存在传统编程方法不能很好地捕获的问题或关注点。考虑在某些应用程序中执行安全策略的问题。从本质上讲,安全性跨越了应用程序模块化的许多自然单元。此外,随着应用程序的发展,安全策略必须一致地应用于任何添加。而且正在应用的安全策略本身可能会发生变化。在传统编程语言中,以一种有纪律的方式捕获关注点(比如安全策略)是困难的,而且容易出错。

像安全性这样的问题跨越了模块性的自然单元。对于面向对象的编程语言,模块化的自然单位是类。但在面向对象的编程语言中,横切关注点不容易变成类正是因为他们跨越类,所以这些不是可重用,他们不能精制或继承,他们是通过程序在一个没有纪律的方式传播,简而言之,他们很难处理。

AspectJ只向Java添加了一个新概念,一个连接点(join point),这实际上只是一个现有Java概念的名称。它只向Java添加了一些新结构:切入点(pointcuts)、通知(advice)、类型间声明(inter-type declarations)和方面(aspects)。切入点和通知动态地影响程序流,类型间声明静态地影响程序的类层次结构,而方面封装了这些新构造。

连接点(join point)是程序流中定义良好的点。切入点(pointcut)在这些点上选择特定的连接点和值。一条通知(advice )是到达连接点时执行的代码。这些是AspectJ的动态部分。

AspectJ还有不同种类的类型间声明,它们允许程序员修改程序的静态结构,即类的成员和类之间的关系。

AspectJ的aspect是横切关注点的模块化单元。它们的行为有点像Java类,但也可能包括切入点(pointcuts)、通知(advice )和类型间声明(inter-type declarations)。

在接下来的小节中,我们将首先研究连接点以及它们如何组成切入点。然后我们将查看通知,这是到达切入点时运行的代码。我们将看到如何将切入点和通知组合成方面,即AspectJ的可重用、可继承的模块单元。最后,我们将研究如何使用类型间声明来处理程序类结构的横切关注点。

动态连接点模型(The Dynamic Join Point Model)

在任何面向方面语言的设计中,连接点模型都是一个关键元素。连接点模型提供了公共的参考框架,这使得定义横切关注点的动态结构成为可能。本章描述AspectJ的动态连接点,其中连接点是程序执行过程中定义良好的特定点。
AspectJ提供了许多种类的连接点,但是本章只讨论其中一种:方法调用连接点。方法调用连接点包含接收方法调用的对象的操作。它包括组成方法调用的所有操作,在所有参数被计算到并包括return(正常情况下或通过抛出异常)之后启动。
在运行时,每个方法调用都是一个不同的连接点,即使它来自程序中的同一个调用表达式。在执行方法调用连接点时,可能会运行许多其他连接点——在执行方法体时发生的所有连接点,以及在从方法体调用的那些方法中发生的连接点。我们说这些连接点在原始调用连接点的动态上下文中执行。

切入点(Pointcuts)

在AspectJ中,切入点选择程序流中的某些连接点。例如,切入点

call(void Point.setX(int))

选择每个连接点,该连接点是对具有签名void point.setX(int)的方法的调用,即point的void setX方法带有单个int参数。
切入点可以由其他带有and, or, and not(拼写&&,||,and !)的切入点构建而成。例如:

call(void Point.setX(int)) ||
call(void Point.setY(int))

选择每个连接点,要么是对setX的调用,要么是对setY的调用。

切入点可以识别来自许多不同类型的连接点——换句话说,它们可以横切类型。例如:

call(void FigureElement.setXY(int,int)) ||
call(void Point.setX(int))              ||
call(void Point.setY(int))              ||
call(void Line.setP1(Point))            ||
call(void Line.setP2(Point));

选择每个连接点,该连接点是对五个方法之一的调用(顺便说一下,第一个是接口方法)。

在我们的示例系统中,当一个FigureElement移动时,这个切入点捕获所有连接点。虽然这是指定横切关注点的一种有用的方法,但是它有点冗长。所以AspectJ允许程序员用切入点形式定义他们自己命名的切入点。因此,下面的代码声明了一个名为pointcut的新点:

pointcut move():
    call(void FigureElement.setXY(int,int)) ||
    call(void Point.setX(int))              ||
    call(void Point.setY(int))              ||
    call(void Line.setP1(Point))            ||
    call(void Line.setP2(Point));

只要这个定义是可见的,程序员就可以简单地使用move()来捕获这个复杂的切入点。

前面的切入点都基于一组方法签名的显式枚举。我们有时称之为基于名称的横切。AspectJ还提供了一些机制,允许根据方法的属性而不是它们的确切名称来指定切入点。我们称之为基于属性的横切。最简单的方法涉及在方法签名的某些字段中使用通配符。例如,切入点:

call(void Figure.make*(..))

选择每个连接点,该连接点是对在Figure上定义的void方法的调用,该方法的名称以“make”开头,而不管该方法的参数是什么。在我们的系统中,它选择对工厂方法makePoint和makeLine的调用。切入点:

call(public * Figure.* (..))

选择每个对Figure的公共方法的调用。

但是通配符并不是AspectJ支持的唯一属性。另一个切入点cflow根据连接点是否出现在其他连接点的动态上下文中来识别连接点。所以,

cflow(move())

选择发生在由move()选择的连接点的动态上下文中的每个连接点,上面我们定义了的命名切入点。因此,它会挑选出在调用move方法和返回方法之间发生的每个连接点(正常情况下或通过抛出异常)。

通知(Advice)

所以切入点(pointcut)选择连接点(join point)。但是它们除了选择连接点之外什么都不做。为了实际实现横切行为,我们使用通知(advice)。通知将切入点(用于挑选连接点)和代码体(用于在每个连接点上运行)结合在一起。
AspectJ有几种不同的通知(advice)。Before advice 在程序处理连接点之前,到达连接点时运行。例如,在对方法调用连接点的通知开始运行之前,在实际方法开始运行之前,也就是在对方法调用的参数求值之后。

before(): move() {
    System.out.println("about to move");
}

After advice 在程序处理该连接点之后,在特定连接点上运行。例如,after advice 在方法调用中,连接点在方法体运行之后运行,就在控制权返回给调用者之前。因为Java程序可以“正常”地离开连接点或抛出异常,所以有三种 after 通知:after returning、after throwing 和 plain after(它在返回或抛出后运行,就像Java的finally一样)。

after() returning: move() {
    System.out.println("just successfully moved");
}

Around Advice 当到达连接点时,在连接点上运行,并对程序是否继续使用连接点进行显式控制。本节不讨论有关建议。

在切入点中公开上下文(Exposing Context in Pointcuts)

切入点不仅可以选择连接点,还可以在连接点上公开部分执行上下文。切入点公开的值可以在通知声明体中使用。

一个通知声明有一个参数列表(类似于一个方法),为它所使用的上下文的所有部分提供名称。例如,after advice:

after(FigureElement fe, int x, int y) returning:
        ...SomePointcut... {
    ...SomeBody...
}

使用三个公开的上下文、一个名为fe的FigureElement和两个名为x和y的int。

通知的主体使用名称就像方法参数一样:

after(FigureElement fe, int x, int y) returning:
        ...SomePointcut... {
    System.out.println(fe + " moved to (" + x + ", " + y + ")");
}

通知的切入点发布通知的参数的值。三个基本切入点this、target和arg用于发布这些值。所以现在我们可以写下完整的一条通知(advice) :

after(FigureElement fe, int x, int y) returning:
        call(void FigureElement.setXY(int, int))
        && target(fe)
        && args(x, y) {
    System.out.println(fe + " moved to (" + x + ", " + y + ")");
}

从调用setXY切入点公开三个值:目标FigureElement——它发布fe,所以它变成了after advice d 第一个参数,两个int参数,它发布x和y,所以他们成为 after advice 的第二个和第三个参数。
因此,在每次setXY方法调用之后,通知将输出被移动的figure元素及其新的x和y坐标。
一个命名的切入点可能有一些参数,比如一条通知(advice)。当使用命名的切入点(通过通知,或者在另一个命名的切入点中)时,它通过名称发布它的上下文,就像this、target和args切入点一样。所以另一种写上述通知(advice)的方法是

pointcut setXY(FigureElement fe, int x, int y):
    call(void FigureElement.setXY(int, int))
    && target(fe)
    && args(x, y);

after(FigureElement fe, int x, int y) returning: setXY(fe, x, y) {
    System.out.println(fe + " moved to (" + x + ", " + y + ").");
}

类型间声明(Inter-type declarations)

AspectJ中的类型间声明是跨越类及其层次结构的声明。他们可以声明跨多个类的成员,或更改类之间的继承关系。不同于主要动态运行的通知,引入在编译时静态地运行。

考虑表达由已经属于类层次结构的某些现有类共享的功能的问题,即它们已经扩展了一个类。在Java中,创建一个接口以捕获此新功能,然后将一个实现此接口的方法添加到 每个受影响的类中。

AspectJ可以通过使用类型间声明在一个地方表达关注。该方面声明实现新功能所必需的方法和字段,并将这些方法和字段与现有类相关联。

假设我们要让Screen对象观察对Point对象的更改,其中 Point是现有的类。我们可以通过编写一个声明方面来实现这一点,该方面声明Point Point类 具有一个实例字段Observers,该字段 跟踪 正在观察Point的Screen对象 。

2. The AspectJ Language

3. Examples

4. Idioms

5. Pitfalls

A. AspectJ Quick Reference

B. Language Semantics

C. Implementation Notes

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值