AOP(Aspect Orient Programming),也就是面向切面编程,做为面向对象编程的一种补充,当前已经成为一种比较成熟的编程思想,其实AOP问世的时间并不长,甚至在国内的翻译还不太统一(另有人翻译为“面向方面编程”)。AOP和OOP(Object Orient Programming,面向对象编程)互为补充,OOP将程序分解成各个层次的对象,而AOP则将程序运行过程分解成各个切面。能够这样理解:OOP是从静态角度考虑程序结构,而AOP则从动态角度考虑程序运行过程。php
为何须要AOPjava
在传统OOP变成立,以对象为核心,整个软件系统由系列相互依赖的对象组成,而这些对象被抽象成一个一个的类,并容许使用类继承来管理类与类之间从通常到特殊的关系。随着软件规模的增大,应用的逐渐升级,慢慢出现了一些OOP很难解决的问题。
shell
咱们能够经过分析、抽象出一系列具备必定属性与行为的对象,并经过这些对象之间的协做来造成一个完整的软件功能。因为对象能够继承,所以咱们能够把具备相同功能或相同特性的属性抽象到一个井井有条的类结构体系中。随着软件规模的不断扩大,专业化分工愈来愈系列,以及OOP应用实践的不断增多,随之也暴露出一些OOP没法很好解决的问题。
编程
假设系统中有3段彻底类似的代码,这些代码一般会采用“复制”、“粘贴”方式来完成,经过这种复制和粘贴完成的代码在后期将很难维护:想一想一下,若是有一天,这些被复制和粘贴的代码须要修改,那么,是否是会修改这3处呢?若是这段代码被复制和粘贴了100遍呢,1000遍呢,如何维护?大多数人会想到将这段代码抽取出来,做为一个公共的方法,在须要使用这段代码的地方,调用这个方法便可。这样若是这段代码须要修改,只须要修改这个公共的方法便可。但实际的状况是:即便将公共的部分抽取出来了,每一个地方仍是须要去显式调用这个方法,这可以解决大部分问题。可是对于一些更加特殊的状况:应用须要将公共的部分与调用的地方完全分离,那又应该如何解决呢?
缓存
由于软件系统需求变动时很频繁的事情,假设系统前期设计方法一、二、3时只实现了核心业务,一段时间以后,咱们须要对这些方法都进行事务控制;又过了一段时间,客户提出这些方法须要进行合法的用户验证,只有合法的用户才能调用这些方法;又过了一段时间,客户又提出这些方法须要增长日志记录;又过了一段时间……面对这种问题,咱们应该怎么办呢?是否是每次先定义一个新的方法,而后再去修改方法一、二、3增长调用新的方法的代码块呢?这样作的工做量也不小啊!
安全
咱们但愿有一种特殊的方法:咱们只要定义该方法,无需在方法一、二、3中显式调用它,系统会“自动”调用该方法。
框架
注意:上面的自动被加上了引号,是由于在编程过程当中,没有所谓的自动的事情,在程序的世界里,任何事情都是由代码驱动的。这里的自动是指,无需开发者关心,由系统来驱动。
eclipse
上面的想法听起来很神奇,甚至有些不切实际,但实际上是彻底能够实现的,实现这个需求的技术就是AOP。AOP专门用于处理系统中分布于各个模块(不一样方法)中的交叉关注点的问题,在JavaEE应用中,经常经过AOP来处理一些具备横切性质的系统级服务,例如事务管理、安全检查、缓存、对象池等等,AOP已经成为一种很是经常使用的方案。
工具
使用AspectJ实现AOP性能
AspectJ是一个基于Java语言的AOP框架,提供了强大的AOP功能,其余不少AOP框架都借鉴或采纳其中的一些思想。因为Spring 3.0的AOP与AspectJ进行了很好的集成,所以掌握AspectJ是学习Spring AOP的基础。
AspectJ是Java语言的一个AOP实现,其主要包括两个部分:第一个部分定义了如何表达、定义AOP编程中的语法规范,经过这个规范,咱们能够方便的用AOP来解决Java语言中存在的交叉关注点的问题;另外一个部分是工具部分,包括编译器、调试工具等。
AspectJ是最先、功能比较强大的AOP实现之一,对嵌套AOP机制都有较好的实现,不少其余语言的AOP实现,也借鉴或采纳了AspectJ中的不少设计。在Java领域,AspectJ中的不少语法结构基本上已成为AOP领域的标准。
从Spring 2.0开始,Spring AOP已经引入了对AspectJ的支持,并容许直接使用AspectJ进行AOP编程,而Spring自身的AOP API也努力与AspectJ保持一致。所以,学习SpringAOP就必然须要从AspectJ开始,由于它是Java领域最流行的AOP解决方案,咱们甚至能够直接使用AspectJ进行AOP编程。
AspectJ的下载和安装(安装AspectJ以前,请确保系统已经安装了JDK)。
1. 下载AspectJ的最新稳定版: http://www.eclipse.org/aspectj/downloads.php#stable_release 下载下来后是一个jar包。
2. 打开命令行,cd到该jar包所在的文件夹,运行java -jar aspectj-1.7.4.jar命令,打开AspectJ的安装界面。第一个界面是欢迎界面,直接next。
3. 第二个界面中,选择jre的安装路径,next。
4. 第三个界面中,选择AspectJ的安装路径,next。由于安装过程的实质是解压一个压缩包,并不须要太多的依赖于系统,所以路径能够任意选择,这里我选择和Java安装在一块儿。
5. 安装完成后,按照界面提示,须要配置classpath和PATH,这里不作介绍。
至此,AspectJ安装完成。
AspectJ提供了编译、运行AspectJ的一些工具命令,这些命令放在AspectJ的bin目录下,而lib路径下的aspectjrt.jar则是AspectJ的运行时环境,因此咱们须要分别添加这两个环境变量。
AspectJ的使用入门
成功安装AspectJ后,在其安装目录下有以下结构:
bin:该路径下存放了AspectJ的经常使用命令,其中ajc最为经常使用,其做用相似于javac,用于对普通Java类进行编译时加强
docs:该路径下存放了AspectJ的使用说明、参考手册和API等文档
lib:该路径下的4个jar文件是AspectJ运行的核心类库
license和readme文件
这里要提到的是,一些文档、AspectJ的入门书籍,一谈到使用AspectJ,就认为必须使用Eclipse工具,彷佛离开了该工具就不能使用AspectJ了。实际上,虽然AspectJ是Eclipse基金组织的开源项目,并且提供了Eclipse的AJDT(AspectJ Development Tools)插件来开发AspectJ应用,但AspectJ绝对无需依赖于Eclipse工具。
AspectJ的用法很简单,就像咱们使用JDK编译、运行Java程序同样。下面经过一个简单的程序来示范AspectJ的用法:
public class HelloWorld { public void sayHello(){ System.out.println("Hello AspectJ!"); } public static void main(String args[]) { HelloWorld h = new HelloWorld(); h.sayHello(); } }
毫无疑问,结果将输出"Hello AspectJ!"字符串。假设如今客户须要在执行sayHello方法前启动事务,当该方法结束时关闭事务,在传统编程模式下,咱们必须手动修改sayHello方法——若是改成使用AspectJ,则能够无需修改上面的sayHello方法。下面咱们定义一个特殊的“类”:
public aspect TxAspect { void around():call(void sayHello()) { System.out.println("Transaction Begin"); proceed(); System.out.println("Transaction End"); } }
可能有人已经发现,上面的类文件中不是使用class、interface或者enum来定义Java类,而是使用aspect——难道Java语言又增长关键字了?No!上面的TxAspect根本不是一个Java类,因此aspect也不是Java支持的关键字,它只是AspectJ才认识的关键字。
上面"void around"中的内容也不是方法,它只是指定当程序执行HelloWorld对象的sayHello方法时,执行这个代码块,其中proceed表示调用原来的sayHello方法。正如前面提到的,Java没法识别TxAspect.java文件中的内容因此咱们须要使用ajc.exe来执行编译:
ajc HelloWorld.java TxAspect.java
咱们能够把ajc命令理解为javac命令,都用于编译Java程序,区别是ajc命令能够识别AspectJ的语法。从这个角度看,咱们能够将ajc命令当成一个加强版的javac命令。
运行该HelloWorld类依然无需任何改变:
java HelloWorld
其结果将是:
从上面的运行结果来看,咱们能够彻底不对HelloWorld.java文件作修改,也不用对执行HelloWorld的命令作修改,就能够实现上文中的实现事务管理的需求。上面的“Transaction Begin”和“Transaction End”仅仅是模拟事务的事件,实际开发中,用代码替换掉这段输出便可实现事务管理。
若是客户又提出了为方法增长日志的需求,那也很简单,咱们能够再定义一个LogAspect类,以下:
//一样使用aspect做为“关键字” public aspect LogAspect { //定义一个名为logPointcut的PointCut,对应于HelloWorld对象的sayHello方法 pointcut logPointcut():execution(void HelloWorld.sayHello()); //在logPointcut以后指定下面的代码 after():logPointcut() { System.out.println("Log Recoding"); } }
上面的代码中定义了一个PointCut——logPointcut,等同于执行HelloWorld对象的sayHello方法,并指定在logPointcut以后执行简单的代码块,也就是说,在sayHello方法结束以后执行输出语句。使用以下命令编译这几个java文件:
ajc *.java
再次运行HelloWorld类,将输出如下结果:
因而可知,经过使用AspectJ提供的AOP支持,咱们能够为sayHello方法不断增长新功能。
实际上,AspectJ容许同时为多个方法添加新功能,只要咱们定义Pointcut时指定匹配更多的方法便可,下面是一个代码片断:
pointcut xxxPointcut:execution(void H*.say*());
上面的程序中的xxxPointcut将能够匹配全部以H开头的类,以say开头的方法名和返回值为void类型的全部方法。AspectJ甚至容许下面的形式:
pointcut xxxPointcut:execution(* H*.say*());
若是装有Java反编译工具,能够将HelloWorld.class进行反编译,咱们将发现该HelloWorld.class文件不是由HelloWorld.java文件编译获得的,HelloWorld.class里面增长了不少新的内容——这代表AspectJ在编译时已经加强了HelloWorld.class的功能,所以AspectJ一般被称为编译时加强的AOP框架。
拓展:与AspectJ相对的还有另一种AOP框架,它们不须要在编译的时候对目标类进行加强,而是运行时生成目标类的代理类,该代理类要么实现了目标类实现的相同接口,要么是目标类的子类。总之,代理类都对目标类进行了加强处理,前者是JDK动态代理的处理策略,后者是CGLIB代理的处理策略。Spring AOP以建立动态代理的方式来生成代理类,底层既可以使用JDK动态代理,也能够采用CGLIB代理。通常来讲,编译时加强的AOP框架在性能上更有优点——由于运行时动态加强的AOP框架须要每次运行时都进行动态加强。
【未完,待续】