AspectJ使用介绍(分享)-飞
发布时间: 2023-07-06

链接地址:
AspectJ 使用介绍

更新时间:2018-07-02

上一篇文章,我们介绍了 Spring AOP 的各种用法,包括随着 Spring 的演进而发展出来的几种配置方式。

但是我们始终没有使用到 AspectJ,即使是在基于注解的 @AspectJ 的配置方式中,Spring 也仅仅是使用了 AspectJ 包中的一些注解而已,并没有依赖于 AspectJ 实现具体的功能。

本文将介绍使用 AspectJ,介绍它的 3 种织入方式。

本文使用的测试源码已上传到 Github: hongjiev/aspectj-learning,如果你在使用过程中碰到麻烦,请在评论区留言。

目录:

AspectJ 使用介绍Compile-Time WeavingPost-(神魔养殖场作者为什么坐牢?《神魔养殖场》的作者黑瞳王之所以会坐牢,是因为作者的小三不满作者离婚后与前妻之间仍有来往,便毒杀发妻,作者包庇小三,又被小三供出毁灭电子证据,判处有期徒刑2年半。)Compile WeavingLoad-Time Weaving小结AspectJ 使用介绍

AspectJ 作为 AOP 编程的完全解决方案,提供了三种织入时机,分别为

    compile-time:编译期织入,在编译的时候一步到位,直接编译出包含织入代码的 .class 文件post-compile:编译后织入,增强已经编译出来的类,如我们要增强依赖的 jar 包中的某个类的某个方法load-time:在 JVM 进行类加载的时候进行织入

本节中的内容参考了《Intro to AspectJ》,Baeldung 真的是挺不错的一个 Java 博客。

首先,先把下面两个依赖加进来:

<dependency>   <groupId>org.aspectj</groupId>   <artifactId>aspectjrt</artifactId>   <version>1.8.13</version></dependency><dependency>    <groupId>org.aspectj</groupId>    <artifactId>aspectjweaver</artifactId>    <version>1.8.13</version></dependency>

我们后面需要用到下面这个类,假设账户初始有 20 块钱,之后会调 account.pay(amount) 进行付款:

public class Account {    int balance = 20;    public boolean pay(int amount) {        if (balance < amount) {            return false;        }        balance -= amount;        return true;    }}

下面,我们定义两个 Aspect 来进行演示:

AccountAspect:用 AspectJ 的语法来写,对交易进行拦截,如此次交易超过余额,直接拒绝。ProfilingAspect:用 Java 来写,用于记录方法的执行时间

AccountAspect 需要以 .aj 结尾,如我们在 com.javadoop.aspectjlearning.aspectj 的 package 下新建文件 AccountAspect.aj,内容如下:

package com.javadoop.aspectjlearning.aspect;import com.javadoop.aspectjlearning.model.Account;public aspect AccountAspect {    pointcut callPay(int amount, Account account):            call(boolean com.javadoop.aspectjlearning.model.Account.pay(int)) && args(amount) && target(account);    before(int amount, Account account): callPay(amount, account) {        System.out.println("[AccountAspect]付款前总金额: " + account.balance);        System.out.println("[AccountAspect]需要付款: " + amount);    }    boolean around(int amount, Account account): callPay(amount, account) {        if (account.balance < amount) {            System.out.println("[AccountAspect]拒绝付款!");            return false;        }        return proceed(amount, account);    }    after(int amount, Account balance): callPay(amount, balance) {        System.out.println("[AccountAspect]付款后,剩余:" + balance.balance);    }}

上面 .aj 的语法我们可能不熟悉,但是看上去还是简单的,分别处理了 before、around 和 after 的场景。

我们再来看用 Java 写的 ProfilingAspect.java:

package com.javadoop.aspectjlearning.aspect;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Pointcut;@Aspectpublic class ProfilingAspect {    @Pointcut("execution(* com.javadoop.aspectjlearning.model.*.*(..))")    public void modelLayer() {    }    @Around("modelLayer()")    public Object logProfile(ProceedingJoinPoint joinPoint) throws Throwable {        long start = System.currentTimeMillis();        Object result = joinPoint.proceed();        System.out.println("[ProfilingAspect]方法: 【" + joinPoint.getSignature() + "】结束,用时: " + (System.currentTimeMillis() - start));        return result;    }}

接下来,我们讨论怎么样将定义好的两个 Aspects 织入到我们的 Account 的付款方法 pay(amount) 中,也就是三种织入时机分别是怎么实现的。

Compile-Time Weaving

这是最简单的使用方式,在编译期的时候进行织入,这样编译出来的 .class 文件已经织入了我们的代码,在 JVM 运行的时候其实就是加载了一个普通的被织入了代码的类。

如果你是采用 maven 进行管理,可以在 <build> 中加入以下的插件:

<!-- 编译期织入 --><plugin>    <groupId>org.codehaus.mojo</groupId>    <artifactId>aspectj-maven-plugin</artifactId>    <version>1.7</version>    <configuration>        <complianceLevel>1.8

微信