`
javafenger
  • 浏览: 242932 次
  • 来自: ...
文章分类
社区版块
存档分类
最新评论

AOP的进一步实际应用

阅读更多

        通过面向特征的编程减少编码时间和重复。目前编程中最热门的新概念是面向特征的编程(ASpECt-oriented ProgrammingAOP)。AOP曾经主要用于学术和研发机构,如今开始进入主流开发领域。与OOP在面向过程的编程方法基础上的改进一样,AOP是在面向对象编程OOP)方法的基础上进行改进而来的一种创新的软件开发方法。OOP引入了封装、继承和多态性等概念来建立一种对象层次结构,用以模拟公共行为的一个集合。然而,OOP在处理范围扩展到一些无关对象的公共行为方面达不到要求。也就是说,OOP允许你定义从上到下的关系,但并不适合定义从左到右的关系。例如,看一下日志功能。日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。对于其他类型的代码,如安全性、异常处理和透明的持续性也是如此。这种散布在各处的无关的代码被称为横切(CROSs-cutting)代码,这也是AOP编码方法产生的原因。

AOP提供一种提取横切代码的方法,这种横切代码横跨各个对象层次,但与它所跨越的对象代码在功能上没有相关性。AOP不是在中嵌入横切代码,而是允许你将横切代码提取到一个单独的模块中,然后在需要的时候动态地应用该代码,这个单独的模块叫做一个"特征代码"("aspect",也译作"标记")。通过在你的对象模型中需要应用横切代码的地方定义特定的位置--切入点(pointcut)--来实现动态的应用横切代码。在运行或编译时,根据你的AOP框架,横切代码被插入指定的切入点。本质上说,AOP允许你在对象中引入新功能,而对象无需了解所引入的功能。这是一个非常有用的概念。


为了进一步理解AOP是如何工作的,请看一个典型的AOP日志例子。代码清单1显示了两个包含日志代码的简单对象:objectA和ObjectB。

通过标准的面向对象的编程,每次你需要时,都要在相应的对象中编写日志代码。在代码清单1的例子中,使用SySTem.out.println()调用来记日志很烦琐。代替System.out.println()调用的另一种方法是使用log4j这样的日志框架,但这会带来额外的开销并给使用它的类带来不必要的杂乱东西。无论是使用System.out.println()调用还是日志框架来实现日志功能,日志代码都与它被嵌入到的类在功能上没有相关性。

通过AOP,你可以动态地将日志代码插入到需要日志功能的类中。这样,对象可以专注于其核心职责。只专注于其核心职责的Java对象往往被称为POJOs(PlAIn Old Java Objects):普通老式Java对象。你可以使用AOP给POJOs增加日志和一些其他类型的公共功能,而无需嵌入不必要的无关功能。

本文将介绍和解释AOP术语,解释不同AOP框架之间的差别,然后逐步说明一个将AOP用于缓存的示例。

AOP术语

AOP 引入了几个新的术语来描述其基本概念。必须切实理解这些术语才可能理解AOP。下面列出了AOP引入的术语及其描述:

1. 建议--是应用到或横切你现有对象模型的代码。建议代码就是修改已有对象的行为或属性的代码。建议通常也被称为引入件(introductions)或混入件(mix-ins)。

2. 切入点-定义在你的模型中要应用建议的位置点。例如,切入点定义在一个类的什么位置应引入代码或哪个方法要在执行前被拦截。切入点也被通称为连接点(joinpoints)。

3. 特征-它将建议和切入点封装到功能单元中,其封装方式与OOP使用类将字段和方法封装到内聚单元(cohesive unit)的方式基本相同。例如,你可能有一个日志特征,它包含了将日志代码应用于对象的所有设置(setter)和获取(getter)方法的建议和切入点。

选择一个框架

在开始使用AOP之前,必须选择一个所用的AOP框架。Java和任何其他主流的面向对象的编程语言都没有对AOP提供内置支持。不过,有一些支持Java的可用的AOP框架,其中大多数都有着相同的核心功能,但在AOP连接到对象模型的方式上有差别。

一些AOP框架使用字节码操作来连接到对象模型,其他一些则使用基于代理的系统进行连接。使用字节码方法的框架在源代码编译为字节码之前修改源代码或在编译后修改字节码。这两种方式都可以有效地得到相同的结果:经过修改的字节码将AOP嵌入到了原始代码。基于代理的框架使用一个代理系统,AOP框架籍此截取所有对具有某特征代码对象的方法调用,然后代理执行对这些框架想要的对象的方法调用。这种代理功能是透明的,而且可以保持字节码总是不变。

各个AOP框架之间的另一个核心区别是特征代码的定义和应用的方式。有些框架中,通过代码来定义和应用特征,而其他一些框架则要求通过XML配置文件来定义和应用特征代码。更好的是,一些框架同时支持用代码和XML配置文件定义和应用特征代码。

因为大多数AOP框架的功能有重叠,究竟使用哪个框架往往取决于某个特定框架的独特功能。本文中的AOP示例应用程序是用一个新的AOP框架dynaop开发的。但是,这个示例应用的基本原理是通用的,可应用于所有的AOP框架。dynaop框架支持用代码定义和应用特征代码,它还提供了一个通过BeanShell脚本框架定义和应用特征代码的独特解决方案。 BeanShell脚本可以使你轻松地编写Java对象脚本,比如示例应用程序所示。

通过dynaop使用AOP

一个简单的缓存特征对方法的结果进行缓存,它说明了AOP的一些基本概念。第一次对方法的调用具有实用的缓存特征代码,它调用该方法并缓存其执行的结果。对这个方法的后续调用将从缓存中返回方法的调用结果。这个基本的缓存特征代码对于运行时间长的方法非常有用,诸如处理大量数据的方法或对某个数据源发出多个查询的方法等。


如代码清单1中的日志例子,在每个需要的类中嵌入缓存功能会导致大量的重复代码。而且,缓存对于大多数对象并不是一个核心功能,而很适合用AOP来实现统一和重复使用。

这个缓存应用程序包括四个文件:


CachingInterceptor.java-包含对方法调用结果进行缓存的AOP代码。这些代码被称为建议。


User.java-一个基本类,其中包含对"name"字段进行设置和获取"getter"和"setter"的方法。 CachingInterceptor AOP代码将被用于这个类。


CacheTest.java-一个说明CachingInterceptor AOP代码使用情况的示例应用程序。


dynaop.bsh-规定在哪里应用CachingInterceptor advice的BeanShell脚本。这些规定被称为切入点(pointcuts)。

以下部分将详细解释每个文件。

CachingInterceptor.java文件。CachingInterceptor类以一种标准、通用的方式将可以应用到任何类的缓存代码进行封装。这个类将方法初次被调用后的结果缓存起来。然后CachingInterceptor类拦截对其结果已被缓存的方法的后续调用,并直接从缓存中返回这些结果,从而为运行时间很长的方法提供快速响应。代码清单2显示了CachingInterceptor类。

CachingInterceptor类包含intercept()、calculateCacheCode()和getFullMethodName()各个方法。intercept()方法实现dynaop.Interceptor接口并包含这个特征的核心建议代码。该方法在具有特征代码的方法被调用之前被调用,然后它负责调用这个具有特征代码的方法。重要的是,intercept()方法是一个代理,用于插入在该方法被代理前执行的功能。另外,intercept()方法控制代理方法是否真正被调用。

CachingInterceptor类的intercept()方法调用calculateCacheCode()方法为将被调用的方法计算一个缓存键值。然后使用这个键值为被调用的方法(在本例中是User类中的getName()方法,在后面部分有定义)查找缓存,查看是否其结果已被缓存。如果结果已经被缓存,则返回被缓存的结果,不再调用User.getName()方法。如果结果没有被缓存,则调用User.getName()方法,并将其结果缓存起来供后续调用使用。这是一个基本的缓存实现,不考虑缓存中的时效数据项和缓存是否超长。


User.java 文件。User类是一个简单的类,只有一个字段以及针对该字段的获取和设置方法。 CachingInterceptor特征代码被用于这个类的getName()方法。代码清单3 显示了User类。

请注意,这个类中的每个方法都有一个System.out.println()调用,指明该方法什么时候被调用。这些调用用来说明示例应用运行时究竟会产生什么结果。 下一步

了解更多AOP信息
aosd.net
eclipse.org/AspectJ

BeanShell

下载
dynaop

本应用程序

阅读更多James Holmes的文章
Java艺术
Struts: 完整的参考手册
 

CacheTest.java 文件。CacheTest类包含说明CachingInterceptor特征代码如何使用的示例应用程序的代码。这个应用程序只是实例化了一个User对象,并多次调用它的getName()方法,显示出连续调用缓存中的方法结果以及对该方法的直接访问。代码清单4显示了CacheTest类。

CacheTest包含一个main()方法,所以它可以作为一个独立的应用程序来运行。

dynaop框架使用一个基于运行时的编织机制将AOP代码插入对象中,这样来直接实例化User对象,使用这个新的操作符,而不是返回一个具有特征代码的对象。为了在示例应用程序中使用CachingInterceptor特征代码,CacheTest类通过dynaop的ProxyFactory类来实例化User对象。一旦你通过ProxyFactory.getInstance() .extend()实例化了这个对象,你就可以像使用其他任何对象一样正常地使用User对象。从这点来说AOP代码的执行是透明的。


dynaop.bsh文件。dynaop.bsh文件是一个BeanShell脚本,用于指定应用CachingInterceptor建议的切入点。以下是dynaop.bsh文件的内容:


// Apply interceptor to all
// getter methods.
interceptor
(
  User.Class,
  GET_METHODS,
  new CachingInterceptor()
);


这个简单脚本指定了CachingInterceptor建议将应用到User类的所有获取方法。GET_METHODS是dynaop用来方便地指定一组切点的几个常量之一。如果必要,你也可以明确地为一些切点指定各个方法。

编译和运行这个应用程序

最后,编译和运行这个应用程序。假设你将这个应用程序的四个文件放在了你安装的dynaop的目录下,例如c:\java\dynaop,下面的命令行将编译这个应用程序:

 

javac -classpath .\bsh-2.0b1.jar;
                .\cglib-asm-1.0.jar;
               .\dynaop-1.0-beta.jar;
               .\jakarta-oro-2.0.7.jar
               *.java


编译完后,执行以下命令运行它:


java -classpath .\;
               .\bsh-2.0b1.jar;
               .\cglib-asm-1.0.jar;
               .\dynaop-1.0-beta.jar;
               .\jakarta-oro-2.0.7.jar
               CacheTest


图1显示了这个示例应用程序的运行结果。这个应用程序先调用User类的setName()方法,然后调用getName()方法。这两个调用都在User类中被启用,对getName()的后续调用在缓存中查找到结果,而不再在User类中被启用。

无提示--不好意思!

-----
图1: 命令行提示 


结论

AOP提供了一个创建软件的功能丰富的新平台,它去除了类的一些不必要职责,并极大地促进了代码的重用。 AOP的日常实际使用包括提取日志代码、提取安全性和缓存代码等,但AOP的应用远不止于此。随着AOP的日益成熟和发展,它还将会有更多的应用。



分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics