IntelliJ Idea 下配置 AspectJ,及简单 demo
- Java
- 2017-03-06
- 420热度
- 0评论
导航
零、场景
项目中有大量用于事件处理的代码片段(目测几百处),例如 mousePressed,mouseClicked 等。我们想在执行这些回调函数时,得到具体事件的相关信息,执行一段自定义代码(比如记录日志的代码),大致弄清楚这次程序执行过程中,都触发了哪些事件,何时触发的。
一种方法是,直接在 mousePressed 这样的回调函数中,添加我们想要的功能。但这样做有问题:
- 找到这几百个方法,并添加内容,是一件苦差事
- 在几百个地方,加入功能相近的代码,显然违背了 DRY 原则
- 以后编写新的事件处理程序时,还要单独处理一遍
- ……
前人(帆软前员工 anchore)已经探索过这个问题,可以用 AOP 的思想来解决。
一、AOP 和 AspectJ 简介
面向切面的程序设计(Aspect-Oriented Programming,AOP,又译作面向方面的程序设计、观点导向编程、剖面导向程序设计)是计算机科学中的一个术语,指一种程序设计范型。该范型以一种称为切面(aspect,又译作方面)的语言构造为基础,切面是一种新的模块化机制,用来描述分散在对象、类或函数中的横切关注点(crosscutting concern)。
- 关注点(concern):对软件工程有意义的小的、可管理的、可描述的软件组成部分,一个关注点通常只同一个特定概念或目标相关联。
- 主关注点(core concern):一个软件最主要的关注点。
例如,对于一个信用卡应用程序来说,存款、取款、帐单管理是它的主关注点,日志和持久化将成为横切整个对象结构的横切关注点。我们可以切入这些关注点,加入一些额外的操作。
AspectJ 是 AOP 的一种实现,它扩展了 Java 语言,可以理解为 Java 的超集。AspectJ 定义了 AOP 语法(具体查阅外部资料,本文不讨论),所以它有一个专门的编译器用来生成遵守 Java 字节编码规范的 Class 文件。
AspectJ 允许我们在切面(此处是各种回调函数)插入自定义代码,同时提供切面的上下文信息。它不会影响现有项目的源码,只是在编译的过程中,把自定义代码部分插入切面所在的 class 文件。
二、Idea 下配置 AspectJ
网上都是关于 eclipse 的教程,只好自己折腾 Idea。好在 Idea 官方给出了比较详细的文档(直接看这个文档就行了。貌似只有专业版才支持 AspectJ,社区版不行)。
我自己验证过的步骤如下:
1、激活 Idea 自带的 AspectJ 插件(官方文档)
如图所示,激活“AspectJ Support”和“Spring AOP/@AspectJ”
2、去官网下载并安装 AspectJ(挂上 VPN 速度更快)
Mac 下,默认安装到用户主目录(home)。如图所示:
本文中,我们只需要用到 lib 目录中的两个 jar 包:aspectjrt.jar 和 aspectjtools.jar。
3、添加依赖
把 aspectjrt.jar 添加为工程的依赖库。相对简单,不会的话,可以参考 Idea 的官方文档。
4、将项目的编译器配置为 ajc
编译器路径为 aspectjtools.jar 所在路径。如图所示:
点击右侧的 test 按钮,确保配置成功。
注意 Delegate to Javac 选项。如果勾选,会尽量使用 javac 编译项目,不会向切面中插入代码。看官方文档,理论上不应该这样,但实测的确如此。感兴趣的话,可以自己再验证一下。
三、编写 demo
用一个取色器 demo 作为实例,来说明具体用法。
工程配置如图:
创建一个 AspectJ 源码文件(后缀为 .aj)
写入如下代码:
import org.aspectj.lang.reflect.SourceLocation;
import java.awt.event.ActionEvent;
import java.awt.event.MouseEvent;
public aspect AspectjDemo {
//声明一个pointcut,匹配你需要的方法
pointcut onMouseClicked(MouseEvent e) :
execution(* mouseClicked(MouseEvent)) && args(e);
pointcut onMousePressed(MouseEvent e) :
execution(* mousePressed(MouseEvent)) && args(e);
pointcut onMouseReleased(MouseEvent e) :
execution(* mouseReleased(MouseEvent)) && args(e);
pointcut onActionPerformed(ActionEvent e) :
execution(* actionPerformed(ActionEvent)) && args(e);
//before表示之前的意思
//这整个表示在 mouseXXX(MouseEvent) 方法调用之前,你想要执行的代码
before(MouseEvent e) : onMouseClicked(e) || onMousePressed(e) || onMouseReleased(e) {
SourceLocation sl = thisJoinPoint.getSourceLocation();//切面对应的代码位置
System.out.println(sl);
System.out.println(e);
System.out.println(e.getSource());
}
//同上
before(ActionEvent e) : onActionPerformed(e) { // && !within(LogHandlerBar) {
SourceLocation sl = thisJoinPoint.getSourceLocation();
System.out.println(sl);
System.out.println(e);
System.out.println(e.getSource());
}
}
切入 mouseClicked,mousePressed,mouseReleased 和 actionPerformed 这几个回调函数,打印出具体事件信息。不讨论语法细节。
直接编译运行就可以了,下面放出效果图:
附 AspectJ 参考资料:
- 中文系列博客:跟我学aspectj之一 ----- 简介 - 夜半无人乱语时 - 博客频道 - CSDN.NET
- 官方指南:The AspectJ™ Programming Guide
- Idea 官方帮助文档:AspectJ
I cannot thank you enough, my friend. It helped me alot!
为什么按照步骤配置了还是不行呢?compiler 配置并 testok。library 添加了,aspectj facet 也添加了,command + N 也能新建 Aspectj 文件。
看具体是卡在哪一步了,有没有报错或其他线索。把问题逐步细化之后,再去网上找解决方法,各个击破。