背景
自从学习spring起,AOP
这个词几乎是天天挂在嘴边,好像一切都是天经地义的。
尤其在springboot里面,经常见到的@Before
@After
等之类的内容,一般更是没有多少解释,通常被告知"这是aop的用法"。
能有人特别说明一下下面这点信息就算不错的了。
-
@Before
@After
等这些的实现,底层是aspectj
。官网地址:aspectJ官网 -
aspectj
并不天然是spring framework
的一部分,人家本是一个独立项目,以优秀的方式实现了aop;还有一些其他的aop实现,如Jboss Aop
什么的。 -
spring framework
有自己的aop实现,但难用,麻烦;后来spring官方索性将 优秀的aspectj
集成到了spring framework
源码中。
从此以后就成了现在我们看到的
spring framework
的aop似乎天然就是aspectj
的样子。
但我总觉得不对劲,距离透彻的理解,似乎总差着一段距离。
于是今天势必追查清楚,求个究竟。
引出具体问题
首先,明确一个基本事实,就是 :
aspectj
并不天然是spring framework
的一部分,它本是一个独立项目,它以自己的方式实现了aop;
那么,我们就不仅仅是一定要把它用在spring项目中;比如我想把它用在我的一个简单的控制台项目中,该怎么做呢?
显然地,网上的文章一大抄,基本上一搜都是教你什么是 pointcut,什么是 joinpoint等等,然后如何用在springboot
项目中。
这个太简单,太程式化,已经麻木了,不值一提。
实现步骤
经过一些搜索、尝试,起初我的项目并没有如预期一样执行,输出的指定的 before after之类的内容。
经过更多的搜索和思考,想明白了原理之后,一切都变得水到渠成了。
其中,在外网搜到一个有用的文章,给了我很多启发。
How to create a Hello World with IntelliJ and Aspect J 里面的步骤也挺详细的。
正式进入实现步骤
1. 安装编译器
想要正常使用aspectj将自己写好的pointcut等内容织入到对应的class文件,这是一个类似于编译的过程。就像编译C语言需要gcc编译器,编译java需要javac编译器一样(当然也可能是别的编译器)。我们需要下载aspectj
,并安装在我们的电脑中。
下载和安装步骤依次如下图:
下载地址:
https://www.eclipse.org/aspectj/downloads.php
截至此处,已经下载并安装了 aspectj到本地。
2.配置环境变量
同样地,类似我们安装了jdk到本地,但cmd命令行里并不能直接使用javac xxx.java
命令,就是因为我们没有把javac
可执行文件配置到我们的path
环境变量里。
这个aspect编译器也一样,同样需要配置环境变量。
如下图:
我只配置了CLASSPATH
,并且只把C:\aspectj1.9\lib\aspectjrt.jar
配置进去就最终成功了。
3. 验证一下
随便试了一下,ajc xxx
,别管它报错不报错,有反应就行。说明这个ajc编译器已经配成功地配置并且可用了。
至此,系统级环境配置结束。
开始设法将aspectj
用在常规项目中。
IDEA里新建一个最简单的项目
1. 新建一个空maven项目
2. 添加aspectj依赖
3. 随便写个mai方法
4.新增一个AspectJ文件
我猜测这里之所以能这么新增,是因为我的IDEA已经安装了aspectj插件。来看一下。
应该是上图两个插件中的第一个,不去验证了。
5. aspectj文件里添加pointcut等内容
这个时候就可以在其他网页里随便抄一段了。如
显然,这里我是试图在 main方法调用之前拦截到的。
如果能拦截到,则控制台会输出绿色字的内容。
下面直接启动一下main方法,看效果。
然而并没有。
不管你再怎么clean,clean install,rebuild,等等等等,都一样,不用再试了。
开始思考和解决aspectj为什么不生效
这段代码居然有了个aspectj
关键字,居然编译没报错,已经很让我惊讶了。
难道jdk编译的时候认识它?
或者是由于插件的原因,使他显示为蓝色?
但编译为什么没报错呢?
既然没报错那为什么又不生效呢?
经过网上搜索查资料和思考,我悟了。
原因其实也很简单,就是这段代码不应该由jdk来编译,因为jdk又不知道遇到aspect该怎么办;而应该由
aspectj
提供的编译功能来编译,也即ajc
。
(但我也没弄明白为什么有aspect字,jdk编译却不报错)
1. 下面开始给IDEA配置ajc编译器
给Path to aspectjtools.jar
配置路径,测试一下,如下图。
2. 此时让我们再编译一下项目
如果配置正确的话,现在我们就是用ajc
编译的了。
3. 然后运行:
立竿见影。
至此完整演示成功。
基本原理,总结起来就是
- 安装aspectj,也就有了aspectj编译器
- 项目中配置编译器,使用ajc编译器
- 项目中新增aspect文件,指定各种切点等。
- 使用ajc编译项目,生成如何预期的结果。
几行 的demo,地址如下:
aspectj-demo.zip
如果此文真的给你解惑了,或者起到了帮助,可考虑打赏。
后记补充
写完之后,我以为就完了,但是后来马上又发现了问题。
就是在前面引入的两个maven依赖,
它为什么要是两个依赖呢?
我偶然地发现,一个依赖也能正常执行。
更奇怪的是,这两个依赖,只依赖任何一个就可以正常执行了。
已经亲测@2021年8月23日23点04分。
那么,这里又引出了下一个问题
aspectjrt
和aspectjweaver
是什么关系,各自有什么作用
现在来看一下
由图可知,这是很明显的,aspectjweaver
包含了aspectjrt
全部的内容。
另外主要多出来的内容,以我的经验,我一眼就看出了重点:(以下是猜测,但我感觉是对的)
后者比前者多出的内容,主要是 asm
和 weaver
。
那么,
asm是什么
ASM 是一个 Java 字节码操控框架。
它能被用来动态生成类或者增强既有类的功能。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。Java class 被存储在严格格式定义的 .class 文件里,这些类文件拥有足够的元数据来解析类中的所有元素:类名称、方法、属性以及 Java 字节码(指令)。ASM 从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。
weaver是什么
weaver这个词,就是“织入”的意思啊,字面意思不解释了。
所以,很明显,weaver + asm,实现了可以动态织入 pointcut等内容到字节码中去的。
那么,下一步再可以得出推论。
推论
-
aspectjrt
功能单纯一些,应该是提供一个编译过后的已经织入了pointcut等内容的java字节码的程序的运行环境
。
关键词:rt
,也就是 runtime
。这个概念我们已经比较熟悉了。 -
aspectjweaver
包含了aspectjrt
全部内容,另外多出了 动态织入的能力
,主要是asm + weaver
部分的代码。
上面的示例中,之所以需要配置ajc编译器,要编译之后才能正常执行,正是因为推论1的原因。
而推论2
我猜测多出来的这部分能力,就是让java项目可以不用再专门指定ajc编译器,而是使用asm + weaver
可以对java代码进行动态编译和织入内容,这样的话直接使用通用的javac编译器就可以了。
当然,还看得出来,也支持了注解的用法。
顺便多数一句,这个情况我已经用代码验证了,符合猜想,大概是对的无疑了。
ps: spring项目应用三步曲:
aspect
(需要标记为@Component) --> pointcut
--> advice
看似不起眼,其实ps:这句话很精髓,懂的自然懂。
所以,项目里,想要使用aspectj注解的写法,需要引入 aspectjweaver
而不是aspectjrt
,比如spring
里的aop相关的用法。
如果愿意自己指定ajc编译器,则可只引入体积较小的aspectjrt
。
我忽然觉得自己理解得更通透了。