aspectJ最简单的HelloWorld例子

aspectJ最简单的HelloWorld例子

这是aspectJ的入门。aspectJ和aop有什么区别? aspectJ也能够实现和aop功能,aop一般指spring的aop,就是指使用@Aspect/@Pointcut/@Before/@Around等注解的那一套,实现原理是动态代理。而aspectJ是更加高效的在编译器的层面上植入通知的

可以猜测,aspectJ 和 lombok 类似,都是编译器帮你 “写入” 了通知的代码(拦截后打印日志的逻辑,叫 “通知”)。虽然aspectJ高效,但是需要特别的编译器,所以没有aop来得方便,而aop相比aspectJ没那么高效但是也能接受,所以工作多年我从未见到在工作中使用aspectJ

简介和spring aop的对比

这里摘抄自 https://www.jianshu.com/p/872d3dbdc2ca

Spring AOPAspectJ
在纯 Java 中实现使用 Java 编程语言的扩展实现
不需要单独的编译过程除非设置 LTW,否则需要 AspectJ 编译器 (ajc)
只能使用运行时织入运行时织入不可用。支持编译时、编译后和加载时织入
功能不强-仅支持方法级编织更强大 - 可以编织字段、方法、构造函数、静态初始值设定项、最终类/方法等…。
只能在由 Spring 容器管理的 bean 上实现可以在所有域对象上实现
仅支持方法执行切入点支持所有切入点
代理是由目标对象创建的, 并且切面应用在这些代理上在执行应用程序之前 (在运行时) 前, 各方面直接在代码中进行织入
比 AspectJ 慢多了更好的性能
易于学习和应用相对于 Spring AOP 来说更复杂

aspectj的HelloWorld

亲手写例子体验aspectj是很好的方法帮助理解的事情。

以下是 aspectJ 的 helloworld 新建步骤。我建的是springboot带web的项目,因为这个项目可以使用web比较方便。

测试的时候要注意:有时候不知道是不是缓存,运行测试main方法会发现拦截不生效,需要cmd+f9重新编译,才生效

步骤:

  1. 建测试用的project
    在 IDEA 里,点击 File->New->Project…->Spring Initialzer->一路进行创建包含spring web的project(详细步骤略,不用严格按照这操作,建一个project可以用maven即可)
  2. 修改 IDEA 的编译器
    仅修改当前project的编译器,不要影响到全局:在 IntelliJ IDEA->Preferences…->Build,Execution,Deployment->Compiler->Java Compiler(Windows是File->Settings->…)(这个修改仅影响当前project,放心修改!

将编译器Javac改成Ajc,并指定aspectjtools.jar的位置(获取方法见后

在这里插入图片描述

  • aspectjtools.jar的获取方法

    • 直接到maven中央仓库页面下载:搜索aspectjtools,并下载最新版即可。1.9.5版下载,然后保存在自己的某个目录,并引用这个路径

    • 或者,在project的pom.xml中引入GAV,下载在本地的maven仓库后找到这个jar包所在路径,引用它即可。project中为了下载这个jar包而引入的GAV可以删掉(不删也行,不过习惯把不需要的GAV删除)

      <!-- aspectjtools:下载好所需的jar包后可以删除这个GAV -->
      <dependency>
          <groupId>org.aspectj</groupId>
          <artifactId>aspectjtools</artifactId>
          <version>1.9.5</version>
      </dependency>
      
  1. 引入所需的jar包,pom.xml中增加如下GAV,这是必须的jar

    <!-- aspectj -->
    <dependency>
        <groupId>aspectj</groupId>
        <artifactId>aspectjrt</artifactId>
        <version>1.5.4</version>
    </dependency>
    

    缺少这个jar包,会在运行时抛出类找不到的异常

    Exception in thread "main" java.lang.NoClassDefFoundError: org/aspectj/lang/NoAspectBoundException
    	at com.wyf.test.aopaspectjspring.example01.HelloWorld.main(HelloWorld.java:20)
    Caused by: java.lang.ClassNotFoundException: org.aspectj.lang.NoAspectBoundException
    	at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
    	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
    	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    	... 1 more
    

    补充:可以发现 org.aspectj.lang.NoAspectBoundException 正是在我们引入的aspectj:aspectj:1.5.4的jar包中。

    PS:其实aspectjtools这个jar也是有这个类的,所以aspectjtools和aspectjrt两个GAV二选一也是可以的,或者连个都保留也是可以的。不过我们还是遵循留aspectjrt删除aspectjtools的GAV

  2. 新建Aspect,用于拦截;新建HelloWorld类,用于写被拦截的方法以及测试用的main方法

  • 这个aspect 类是一个.aj 扩展名的文件,如 LogAspectJ.aj,它需要Acj编译器来编译,编译后的文件也是clas文件,如 LogAspectJ.class (反编译这两个class发现还是跟普通的不一样)
package com.wyf.test.aopaspectjspring.example01;

/**
 *
 * @author Stone
 * @version V1.0.0
 * @date 2020/2/19
 */
public aspect LogAspectJ {
    void around(): call(void HelloWorld.sayHello()) {
        System.out.println("日志前...");
        proceed();
        System.out.println("日志后...");
    }
}

package com.wyf.test.aopaspectjspring.example01;

/**
 * @author Stone
 * @version V1.0.0
 * @date 2020/2/19
 */
public class HelloWorld {
    public void sayHello() {
        System.out.println("Hello, AspectJ!");
    }

    /**
     * 测试方法:运行时发现能够被拦截
     *
     * @param args
     */
    public static void main(String[] args) {
        HelloWorld helloWorld = new HelloWorld();
        helloWorld.sayHello();
    }
}
  1. 运行HelloWorld的main方法,发现打印

    日志前...
    Hello, AspectJ!
    日志后...
    

    (如果没有这么打印,2个方面排查。第一查看File->Settings查看下是不是Adj编译器,IDEA似乎有bug,只要稍微改一下pom.xml,就会打回原形用Javac编译器;第二可能是缓存,比如有时候改一下System.out.println("日志前..."); 的打印的字符串,比如改成 System.out.println("日志前222..."); 会发现不生效,这时使用cmd+f9触发一下编译再运行)

附录

反编译 HelloWorld 和 LogAspectJ,得到如下的源码(用IDEA反编译)’

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.wyf.test.aopaspectjspring.example01;

import org.aspectj.lang.NoAspectBoundException;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.runtime.internal.AroundClosure;

@Aspect
public class LogAspectJ {
    static {
        try {
            ajc$postClinit();
        } catch (Throwable var1) {
            ajc$initFailureCause = var1;
        }

    }

    public LogAspectJ() {
    }

    @Around(
            value = "call(void HelloWorld.sayHello())",
            argNames = "ajc$aroundClosure"
    )
    public void ajc$around$com_wyf_test_aopaspectjspring_example01_LogAspectJ$1$8852f95(AroundClosure ajc$aroundClosure) {
        System.out.println("日志前...");
        ajc$around$com_wyf_test_aopaspectjspring_example01_LogAspectJ$1$8852f95proceed(ajc$aroundClosure);
        System.out.println("日志后...");
    }

    public static LogAspectJ aspectOf() {
        if (ajc$perSingletonInstance == null) {
            throw new NoAspectBoundException("com_wyf_test_aopaspectjspring_example01_LogAspectJ", ajc$initFailureCause);
        } else {
            return ajc$perSingletonInstance;
        }
    }

    public static boolean hasAspect() {
        return ajc$perSingletonInstance != null;
    }
}

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.wyf.test.aopaspectjspring.example01;

import org.aspectj.lang.NoAspectBoundException;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.runtime.internal.AroundClosure;

@Aspect
public class LogAspectJ {
    static {
        try {
            ajc$postClinit();
        } catch (Throwable var1) {
            ajc$initFailureCause = var1;
        }

    }

    public LogAspectJ() {
    }

    @Around(
            value = "call(void HelloWorld.sayHello())",
            argNames = "ajc$aroundClosure"
    )
    public void ajc$around$com_wyf_test_aopaspectjspring_example01_LogAspectJ$1$8852f95(AroundClosure ajc$aroundClosure) {
        System.out.println("日志前...");
        ajc$around$com_wyf_test_aopaspectjspring_example01_LogAspectJ$1$8852f95proceed(ajc$aroundClosure);
        System.out.println("日志后...");
    }

    public static LogAspectJ aspectOf() {
        if (ajc$perSingletonInstance == null) {
            throw new NoAspectBoundException("com_wyf_test_aopaspectjspring_example01_LogAspectJ", ajc$initFailureCause);
        } else {
            return ajc$perSingletonInstance;
        }
    }

    public static boolean hasAspect() {
        return ajc$perSingletonInstance != null;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值