一、AOP编程概览

  面向对象编程技术进入软件开发的主流对软件的开发方式产生了极大的影响,开发者可以用一组实体以及这些实体之间的关系将系统形象地表示出来,这使得他们能够设计出规模更大、更复杂的系统,开发周期也比以前更短。OO开发的唯一问题是,它本质上是静态的,需求的细微变化就可能对开发进度造成重大影响。

  Aspect-Oriented Programming(AOP)是对OO技术的补充和完善,它允许开发者动态地修改静态的OO模型,构造出一个能够不断增长以满足新增需求的系统,就象现实世界中的对象会在其生命周期中不断改变自身,应用程序也可以在发展中拥有新的功能。

  例如,许多人想必有过在开发简单的Web应用时将Servlet作为入口点的经验,即用Servlet接收HTML表单的输入,经过处理后返回给用户。开始时的Servlet可能是非常简单的,只有刚好满足用户需求的最少量的代码。然而,随着“第二需求”的实现,例如实现异常处理、安全、日志等功能,代码的体积就会增加到原来的三、四倍——之所以称之为“第二需求”,是因为Servlet的基本功能是接受和处理用户的请求,对于这个目标来说,日志、安全之类的机制并不是必不可少的。

  AOP允许动态地改变OO的静态模型,不必修改原来的静态模型也可以加入满足第二需求所需的代码(实际上,甚至连原来的源代码也不需要)。更令人称奇的是,后来加入的代码往往可以集中在一个地方,而不必象单纯使用OO时那样将后来加入的代码分散到整个模型。

  二、基本术语

  在介绍AOP开发实例之前,我们先来了解几个标准的AOP术语,以便更好地掌握相关的概念。

   Cross-cutting concern

  在OO模型中,虽然大部份的类只有单一的、特定的功能,但它们通常会与其他类有着共同的第二需求。例如,当线程进入或离开某个方法时,我们可能既要在数据访问层的类中记录日志,又要在UI层的类中记录日志。虽然每个类的基本功能极然不同,但用来满足第二需求的代码却基本相同。

   Advice

  它是指想要应用到现有模型的附加代码。在本例中,它是指线程进入或退出某个方法时要运行的日志代码。

   Point-cut

  这个术语是指应用程序中的一个执行点,在这个执行点上需要采用前面的cross-cutting concern。在本例中,当线程进入一个方法时出现一个Point-cut,当线程离开方法时又出现另一个Point-cut。

   Aspect

  Point-cut和advice结合在一起就叫做aspect。在下面的例子中,我们通过定义一个point-cut并给予适当的advice加入了一个日志(logging)aspect。

  AOP还有其它许多特性和术语,例如引入(Introduction),即把接口/方法/域引入到现有的类——它极大地拓宽了开发者的想象力。不过本文只介绍一些最基本的持性,熟悉这里介绍的概念后,你再深入一步研究AOP的其它特性,看看如何在自己的开发环境中使用它们。

  三、现有的框架

  目前最成熟、功能最丰富的AOP框架当数AspectJ,AspectJ已成为大多数其它框架跟从的标准。但是,AspectJ也走出了非同寻常的一步,它的实现为Java语言增添了新的关键词。虽然新的语法并不难学,但却意味着我们必须换一个编译器,还要重新配制编辑器,只有这样才能适应新的语法。在规模较大的开发组中,这些要求可能难以办到,因为整个开发小组都会受到影响。由于语言本身的变化,开发小组把AOP技术引入到现有项目的学习周期随之延长。

  现在我们需要的是这样一个框架,它可以方便地引入,且不会对原来的开发和构造过程产生任何影响。满足这些要求的框架不止一个,例如JBoss AOP、Nanning、Aspectwerkz(AW)。本文选用的是Aspectwerkz,因为它可能是最容易学习的框架,也是最容易集成到现有项目的框架。

  Aspectwerkz由Jonas Boner和Alexandre Vasseur创建,它是目前最快速、功能最丰富的框架之一。虽然它还缺乏AspectJ的某些功能,但己足以满足大多数开发者在许多情形下的需要。

  Aspectwerkz最令人感兴趣的特性之一是它能够以两种不同的模式运行:联机模式和脱机模式。在联机模式下,AW直接干预属于JVM的底层类装入机制,截取所有的类装入请求,对字节码实施即时转换。AW提供了干预类装入过程的许多选项,另外还有一个替代bin/java命令的封装脚本,这个脚本能够根据Java版本和JVM能力自动生成一组可运行的配制。对于开发者,联机模式有许多优点,它能插入到任何类装入器并在类装入期间生成新的类。也就是说,我们不必手工修改应用程序的类,只要按通常的方式部署即可。不过,联机模式要求对应用服务器进行额外的配制,有时这一要求可能很难满足。

  在脱机模式下,生成类需要二个步骤。第一步是用标准的编译器编译,第二步是重点——以脱机模式运行AWcompiler编译器,让它处理新生成的类。编译器将修改这些类的字节码,根据一个XML文件的定义,在适当的point-cut插入advice。脱机模式的优点是AWcompiler生成的类能够在任何JVM 1.3以上的虚拟机运行,本文下面要用的就是这种模式,因为它不需要对Tomcat作任何修改,只要对构造过程稍作修改就可以照搬到大多数现有的项目。

  四、安装

  本文将以一个简单的Web应用程序为例,它用Ant编译,部署在Tomcat 4 Servlet容器上。下面我们假定读者己准备好上述环境,包括JVM 1.3 ,同时Tomcat被设置成从webapps文件夹自动部署应用,自动将WAR扩展到目录(这是Tomcat默认的操作方式,因此只要你尚未修改Tomcat的运行方式,下面的范例可直接运行)。我们将把Tomcat的安装位置称为%TOMCAT_HOME%。

  ⑴ 从http://apectwerkz.codehaus.org/下载Aspectwerkz,解开压缩到适当的位置。我们将把这个位置称为%ASPECTWERKZ_HOME%。

  ⑵ 设置%ASPECTWERKZ_HOME%环境变量。

  ⑶ 将Aspectwerkz加入到PATH环境变量,即设置set PATH=%PATH%;%ASPECTWERKZ_HOME%inaspectwerkz

  ⑷ 下载本文的示范程序,将它放入%TOMCAT_HOME%webapps文件夹。

  ⑸ 将Aspectwerkz的运行时类加入到Tomcat的classpath。你可以将它的JAR文件放入示例应用的WEB-INFlib文件夹,或放入%TOMCAT_HOME%commonlib。

    五、编译示例应用

  如果你想深入研究一下本文的示例应用,可以解开WAR文件提取它的内容。你会发现根目录下有一个aspectwerkz.xml文件,构造应用时它会被复制到WEB-INF/classes目录。Servlet和advice的源文件在WEB-INF/src目录下,另外还有一个构建这些类的ANT脚本。

  在运行这个示例程序之前,你还要对它进行后期编译。下面是具体的操作步骤:

  ⑴ 在命令行窗口中,转到解开WAR文件的目录。

  ⑵ 输入下面的命令调用AW编译器:aspectwerkz -offline aspectwerkz.xml WEB-INF/classes -cp %TOMCAT_HOME%commonlibservlet.jar。如后期编译顺利通过,应看到下面的输出:

  ( 1 s )

  SUCCESS: WEB-INFclasses

  在构建文件中有一个名称为war的ANT任务,你可以用它重新创建WAR文件。

  六、运行示例应用

  首先启动(或重新启动)Tomcat,然后在浏览器中打开http://localhost:8080/demo/。

  页面打开后,可以看到一个带二个输入框的HTML表单,一个输入名字,一个输入邮件地址。输入一些数据,然后点击按钮提交表单,出现一个页面显示出联系人信息和一个指向联系人清单的链接。

  七、代码分析

  JSP页面就不分析了,现在我们对它不感兴趣。我们来看看AOPServlet的代码。

  package example;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class AOPServlet extends HttpServlet {
 public void doGet(HttpServletRequest request, HttpServletResponse response)
 throws ServletException, IOException {
  Person person = new Person();
  if (request.getParameter("name") != null) {
   person.setName(
   request.getParameter("name"));
  }
  if (request.getParameter("email") != null) {
   person.setEmail(
   request.getParameter("email"));
  }
  request.setAttribute("person", person);
  RequestDispatcher rd =request.getRequestDispatcher("/view.jsp");
  rd.forward(request, response);
 }
}

  在这个例子中,Servlet的代码己尽量精简,只包含一些必不可少的代码,如创建了一个绑定请求参数的对象等,但没有持久化操作,不需要额外的imports,它只实现了作为Servlet必须实现的最基本的操作。

  然而,根据说明文档的要求,这个应用程序必须将所有Person类型的对象特久化,所以要为这个应用程序加入一个aspect。为创建这个aspect,我们首先要创建一个aspectwerkz.xml文件并将该文件放入classpath指定的目录。本文示例提供了一个简单的例子,你可以用编辑器打开查看。

  aspectwerkz.xml的第一部份定义了可用的advice,我们可以根据需要加入任意数量的advice:

  <advice-def name="persist" class="example.PersistenceAdvice" deployment-model="perJVM"/>

  在这个片段中,我们定义了一个名称为persist的advice,它的类型是example.PersistenceAdvice。最后一个属性定义了该advice的排它性,在这里它的值是perJVM,表示在每一个JVM中只创建该advice的一个实例(有关部署模式的更多说明,请参见Aspectwerkz的文档。

  第二部份开始定义aspect,这里就是我们将advice映射到point-cut创建aspect的地方。

  <aspect name="servlet">
  <pointcut-def name="all" type="method"
  pattern="* example.*Servlet.doGet(..)"/>
  <bind-advice pointcut="all">
  <advice-ref name="persist"/>
  </bind-advice>
  </aspect>

  下面我们一行一行地分析这段代码:

  ⑴ 我们创建了一个叫做servlet的aspect。如有必要,我们可以创建任意数量的aspect。

  ⑵ 在第二行,我们创建了一个叫做all的point-cut,它只适用于方法(type="method")。

  ⑶ 第三行我们用一个正则表达式规定了把advice应用到哪里。在这个例子中,我们指出应用advice的条件是:不管返回值的类型是什么(第一个“*”),名称以servlet结尾(*servlet)且包含一个带任意参数的doGet方法(doGet(..))的example包里面的类。

  ⑷ 在第四行,我们告诉Aspectwerkz编译器要把后面的advice应用到所有的point-cut。

  ⑸ 在这里我们声明要使用的advice是persist。

  现在我们知道了如何映射point-cut与advice创建出aspect,下面来看看一个提供advice的类的实例。在映射文件中,我们注册了一个example.PersistenceAdvice类型的advice,下面是该类型的源代码:

  package example;

import javax.servlet.http.*;
import org.codehaus.aspectwerkz.advice.*;
import org.codehaus.aspectwerkz.joinpoint.*;

public class PersistenceAdvice extends AroundAdvice {
 public PersistenceAdvice() {
  super();
 }
 public Object execute(final JoinPoint joinPoint)
 throws Throwable {
  MethodJoinPoint jp =(MethodJoinPoint) joinPoint;
  final Object result = joinPoint.proceed();
  Object[] parameters = jp.getParameters();
  if (parameters[0] instanceof HttpServletRequest) {
   HttpServletRequest request =(HttpServletRequest) parameters[0];
   if (request.getAttribute("person") != null) {
    Person contact =(Person) request.getAttribute("person");
    ContactManager persistent = new ContactManager();
    String fileName =(request.getRealPath("/") "contacts.txt");
    persistent.save(contact, fileName);
   }
  }
  return result;
 }
}


  execute()方法的第一行很容易理解,就是尽量把它定型成最具体的类型,第二行或许是最重要的:因为我们想要运行该方法并检查结果,所以必须调用proceed()。在下一部份,我们捕获HttpServletRequest,提取由Servlet放入的对象(记住,此时doGet()方法己运行结束)。

  最后,我们创建一个名称为ContactManager的类,它的功能是把Person的数据保存到一个文本文件。实际上,要把数据保存到XML文件、数据库或其它持久化存储机制也很方便。

  这里需要掌握的一点是,在设计应用或建立原型的阶段,Servlet并不知道未来会发生什么变化,第二阶段的功能可以随时加入,正因为如此,所以我们说应用程序能够在发展过程中学习新的能力,以后要添加新的功能非常方便。

  【结束语】 我们在前面的例子中试验了一个简单的应用,将它部署到Tomcat,并用浏览器运行和测试它的功能。虽然这个应用本身并无任何实际用途,但它示范和证实了一些非常有用的概念。想象一下,你将可以快速地建立原型,完成后再引入安全、日志、持久化、缓冲之类的Cross-cutting concern。不管原始应用的规模有多大,你将能够在十分钟之内轻松地为整个应用加入日志功能!

  希望你能够超越本文的简单例子,去看看如何在自己的项目中采用AOP技术。熟悉AOP的概念当然需要一定的时间,但肯定会得到回报,对于一个中等规模的项目,它会让你省下数星期时间,或者少写数千行重复的代码。

 

Java中基于Aspectwerkz的AOP的更多相关文章

  1. 基于EJB技术的商务预订系统的开发

    用EJB结构开发的应用程序是可伸缩的、事务型的、多用户安全的。总的来说,EJB是一个组件事务监控的标准服务器端的组件模型。基于EJB技术的系统结构模型EJB结构是一个服务端组件结构,是一个层次性结构,其结构模型如图1所示。图2:商务预订系统的构架EntityBean是为了现实世界的对象建造的模型,这些对象通常是数据库的一些持久记录。

  2. ios – 未捕获的异常:CALayer位置在执行动画时包含NaN

    我在iPad上运行时在应用程序中收到此错误:Uncaughtexception:CALayerpositioncontainsNaN相同的代码在iPhone上运行良好.我知道,分配的帧包含NaN值,我用CGRectEmpty函数检查了这个并将该帧设置为CGRectZero,但它不起作用.我从这里得到了这个工作代码,https://stackoverflow.com/a/7045916/160323

  3. 10.5 Swift类方法

    /**类型方法通过类名称来调用的方法,就像类型属性一样。

  4. python中的import、from import及import as的区别解析

    在Python中,如果import的语句比较长,导致后续引用不方便,可以使用as语法,这篇文章主要介绍了python中的import、from import以及import as的区别,需要的朋友可以参考下

  5. ftp类(example.php)

    flush();$ftp_ini_datei=$argv[1];require('./ftp_class.php');require($ftp_ini_datei);echo"\nCronjobstarted:";echodate("d.m.Y-H:i:s");echo"\n";$newftp=newmyftp;if(!$anonymous){$result=$newftp->connect($host,$user,$password);}else{$result=$newftp->connect($ho

  6. JSP开发入门(五)--JSP其他相关资源

    JSP其他相关资源:ServletsandJavaServerPages(JSP)1.0:ATutorialhttp://www.apl.jhu.edu/~hall/java/Servlet-Tutorial/JavaServerPagesTM:ADeveloper'sPerspectivehttp://developer.java.sun.com/developer/technicalArtic

  7. 如何防止IE缓存jsp文件

    1,使用java提供的方法,在jsp或者servlet中都可以2,使用HTML标记,如下面:

  8. 一个开发人员眼中的JSP技术(上)

    本文从一个开发人员的角度对JSP技术做了一个全面介绍。在JSP网页中,要把用户界面和应用程序分开可以考虑在网页设计人员和开发人员之间执行一个非常方便的授权任务。如果需要的话,JSP网页还可以进行预编译。开发人员可以提供定制化的JSP标签库。同样,开发人员也无须一个个编辑页面而只须对组件进行合理的改变。通常,JSP允许开发人员向许多网页设计人员分发功能性应用程序。这就意味着JSP注释并不返回到用户的浏览器中。

  9. 利用PHP实现与ASP Banner组件相似的类

    ********************************************************//*广告条管理程序publicmethodGetAdvertisementparametersTarget=Width=Height=Border=Redirect=*/classAdRotator{var$Target="_blank";var$Width="460";var$Height="60";var$Border=0;var$Redirect="";var$BannerData=ar

  10. Jsp结合XML+XSLT将输出转换为Html格式

    我们知道XMLXSLT就可以直接输出到支持XML的浏览器上,如IE5.0以上,但是,我们还要考虑到有不少浏览器不直接支持XML,在这种情况下,我们需要在服务器上进行转换成html输出到浏览器,这种临时过渡办法恐怕要在一段时间内一直要使用.使用Jsp加上tablib标识库,我们可以完成这种转换。

随机推荐

  1. 基于EJB技术的商务预订系统的开发

    用EJB结构开发的应用程序是可伸缩的、事务型的、多用户安全的。总的来说,EJB是一个组件事务监控的标准服务器端的组件模型。基于EJB技术的系统结构模型EJB结构是一个服务端组件结构,是一个层次性结构,其结构模型如图1所示。图2:商务预订系统的构架EntityBean是为了现实世界的对象建造的模型,这些对象通常是数据库的一些持久记录。

  2. Java利用POI实现导入导出Excel表格

    这篇文章主要为大家详细介绍了Java利用POI实现导入导出Excel表格,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  3. Mybatis分页插件PageHelper手写实现示例

    这篇文章主要为大家介绍了Mybatis分页插件PageHelper手写实现示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  4. (jsp/html)网页上嵌入播放器(常用播放器代码整理)

    网页上嵌入播放器,只要在HTML上添加以上代码就OK了,下面整理了一些常用的播放器代码,总有一款适合你,感兴趣的朋友可以参考下哈,希望对你有所帮助

  5. Java 阻塞队列BlockingQueue详解

    本文详细介绍了BlockingQueue家庭中的所有成员,包括他们各自的功能以及常见使用场景,通过实例代码介绍了Java 阻塞队列BlockingQueue的相关知识,需要的朋友可以参考下

  6. Java异常Exception详细讲解

    异常就是不正常,比如当我们身体出现了异常我们会根据身体情况选择喝开水、吃药、看病、等 异常处理方法。 java异常处理机制是我们java语言使用异常处理机制为程序提供了错误处理的能力,程序出现的错误,程序可以安全的退出,以保证程序正常的运行等

  7. Java Bean 作用域及它的几种类型介绍

    这篇文章主要介绍了Java Bean作用域及它的几种类型介绍,Spring框架作为一个管理Bean的IoC容器,那么Bean自然是Spring中的重要资源了,那Bean的作用域又是什么,接下来我们一起进入文章详细学习吧

  8. 面试突击之跨域问题的解决方案详解

    跨域问题本质是浏览器的一种保护机制,它的初衷是为了保证用户的安全,防止恶意网站窃取数据。那怎么解决这个问题呢?接下来我们一起来看

  9. Mybatis-Plus接口BaseMapper与Services使用详解

    这篇文章主要为大家介绍了Mybatis-Plus接口BaseMapper与Services使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  10. mybatis-plus雪花算法增强idworker的实现

    今天聊聊在mybatis-plus中引入分布式ID生成框架idworker,进一步增强实现生成分布式唯一ID,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

返回
顶部