0.AspectJ简介
在上篇文章【Spring——AOP(1)之Spring1中的配置】中,简要介绍了AOP(Aspect-Oriented Programming,面向切面编程)的概念,并介绍了AOP中的一些术语,最后通过使用Spring1中编写切面、配置切面的方式讲解了一个案例。本节我们介绍AspectJ框架,学习AspectJ框架是因为Spring2以后的AOP开发中引入了很多AspectJ的概念和配置方式。
AspectJ是Eclipse基金组织的开源项目,它是Java语言的一个AOP实现,是最早、功能比较强大的AOP实现之一,对整套AOP机制都有较好的实现,很多其他语言的AOP实现也借鉴或者采纳了AspectJ中的很多设计。在Java领域,AspectJ中的很多语法结构基本上已经成为AOP领域的标准。
要知道的是,AspectJ框架和Spring框架实现AOP的方式是不一样的,AspectJ是在编译时进行增强,所以它有一个专门的编译器来生成遵守Java字节码编码规范的Class文件。而Spring采用的是动态代理的方式,它并不需要有一个专门的编译器。故也称AspectJ为静态AOP实现,而Spring AOP为动态AOP实现。
AspectJ主要包含两个部分:第一个部分定义了如何表达、定义AOP编程中的语法规范;第二个部分是工具部分,包括编译器、调试工具等。
1.安装AspectJ
安装AspectJ首先要到AspectJ官网下载一个可执行的Jar包,然后双击该Jar包即可打开进行安装。
多次点击Next
按钮、并选择合适的安装目录,即可成功安装AspectJ。
在安装了AspectJ之后,在其安装目录下,可以看到如下的文件结构:
├─bin // 该路径下存放了 aj、aj5、ajc、ajdoc、ajbrowser 等命令。
│ ├─aj.bat
│ ├─aj5.bat
│ ├─ajbrowser
│ ├─ajbrowser.bat
│ ├─ajc // 其中 ajc 命令最常用,它的作用类似于 javac,用于对普通 Java 类进行编译时增强。
│ ├─ajc.bat
│ ├─ajdoc
│ ├─ajdoc.bat
├─doc // 该路径下存放了AspectJ的使用说明、参考手册、API文档等文档。
├─lib // 该路径下的4个Jar文件是AspectJ的核心类库
│ ├─aspectjrt.jar
│ ├─aspectjtools.jar
│ ├─aspectjweaver.jar
│ ├─org.aspectj.matcher.jar
├─LICENSE-AspectJ.html
└─README-AspectJ.html
2.IntelliJ IDEA下配置AspectJ
虽然AspectJ是Eclipse基金组织的开源项目,而且提供了Eclipse的AJDT插件(AspectJ Development Tools)来开发AspectJ应用,但AspectJ并不是只能在Eclipse中开发。由于我使用IntelliJ IDEA,所以这里介绍IDEA中如何开发AspectJ。
只有专业版的IntelliJ IDEA才支持AspectJ的开发,而且IDEA也提供了官方文档。
2.1.激活AspectJ支持插件
在专业版IDEA中开发AspectJ,需要确保下述插件被激活:
- Spring AOP/@AspectJ
- AspectJ Support
打开Settings
(Ctrl+Alt+S)对话框,做如下配置:
2.2.添加aspectjrt.jar依赖
在项目中添加aspectjrt.jar
依赖,aspectjrt.jar
即AspectJ安装目录中lib
目录下的jar包。我的习惯是在项目中新建一个lib
目录,然后将该jar包拷贝过来。
接着进行如下操作,将该Jar包添加进项目依赖中:
- 打开
Project Structure
对话框(Ctrl+Shift+Alt+S)。 - 对应于创建项目级别的或者IDE级别的库,分别选择
Libraries
或者Global Libraries
。 - 点击
+
号并选择java
。 - 在弹出的对话框中,选择刚才我们添加进项目的lib目录下的
aspectjrt.jar
文件。 - 最后点击OK按钮即可。
2.3.使用AspectJ编译器(ajc)
IDEA默认使用javac
编译器,如果要使用AspectJ的编译器ajc
,需要在IDEA中进行相应的配置。
打开settings
对话框,然后做如下配置:
3.一个简单的案例
1.首先编写一个简答的Java类,该Java类用于模拟一个业务组件。
public class Hello {
public void sayHello() {
System.out.println("Hello, AspectJ!");
}
public static void main(String[] args) {
Hello hello = new Hello();
hello.sayHello();
}
}
该类中有一个sayHello()
方法,该方法打印出了一句话!
假设现在我们需要在sayHello()
方法之前启动事务,当该方法结束时关闭事务,那么在传统的编程模式下,我们必须手动修改sayHello()
方法。而如果使用AspectJ
,我们则不需要修改上面的方法,只需要添加一个切面即可。
下面我们来开发一个切面Aspect
。
如下图示,IDEA中可以直接支持开发Aspect
。
我们创建TxAspect
切面,如下代码:
public aspect TxAspect {
void around():call(void Hello.sayHello()){
System.out.println("开始事务...");
proceed();
System.out.println("事务结束...");
}
}
此时发现TxAspect
的类型是aspect
,并不是一个类class
,这就是AspectJ提供的一个特殊的模块——切面。其后缀为.aj
,该文件的完整文件名为TxAspect.aj
。切面的语法只有AspectJ
可以识别,并使用其特殊的编译器ajc
来编译。
这段代码拦截Hello.sayHello()
方法,并在其执行之前开始事务,proceed()
方法代表回调原来的sayHello()
方法,执行结束后结束事务。
因为我们已经配置过了编译器,所以这里我们直接运行Hello.java
代码,可以看到运行结果:
运行结果完全符合我们的预期。我们并没有对Hello
类做任何修改,就给它插入了事务管理的功能,这正是面向切面编程的意义所在。从这个例子中我们也可以体会到AspectJ的易学易用、无侵入(不需要继承任何类和接口)的特性。