aspectJ最简单的HelloWorld例子

灰太狼 2023-07-06 13:28 57阅读 0赞

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 AOP AspectJ
在纯 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删除)

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

    1. <!-- aspectj -->
    2. <dependency>
    3. <groupId>aspectj</groupId>
    4. <artifactId>aspectjrt</artifactId>
    5. <version>1.5.4</version>
    6. </dependency>

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

    1. Exception in thread "main" java.lang.NoClassDefFoundError: org/aspectj/lang/NoAspectBoundException
    2. at com.wyf.test.aopaspectjspring.example01.HelloWorld.main(HelloWorld.java:20)
    3. Caused by: java.lang.ClassNotFoundException: org.aspectj.lang.NoAspectBoundException
    4. at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
    5. at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    6. at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
    7. at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    8. ... 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 {

    1. void around(): call(void HelloWorld.sayHello()) {
    2. System.out.println("日志前...");
    3. proceed();
    4. System.out.println("日志后...");
    5. }

    }

    package com.wyf.test.aopaspectjspring.example01;

    /* @author Stone @version V1.0.0 @date 2020/2/19 */
    public class HelloWorld {

    1. public void sayHello() {
    2. System.out.println("Hello, AspectJ!");
    3. }
    4. /** * 测试方法:运行时发现能够被拦截 * * @param args */
    5. public static void main(String[] args) {
    6. HelloWorld helloWorld = new HelloWorld();
    7. helloWorld.sayHello();
    8. }

    }

  3. 运行HelloWorld的main方法,发现打印

    1. 日志前...
    2. Hello, AspectJ!
    3. 日志后...

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

附录

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

  1. //
  2. // Source code recreated from a .class file by IntelliJ IDEA
  3. // (powered by Fernflower decompiler)
  4. //
  5. package com.wyf.test.aopaspectjspring.example01;
  6. import org.aspectj.lang.NoAspectBoundException;
  7. import org.aspectj.lang.annotation.Around;
  8. import org.aspectj.lang.annotation.Aspect;
  9. import org.aspectj.runtime.internal.AroundClosure;
  10. @Aspect
  11. public class LogAspectJ {
  12. static {
  13. try {
  14. ajc$postClinit();
  15. } catch (Throwable var1) {
  16. ajc$initFailureCause = var1;
  17. }
  18. }
  19. public LogAspectJ() {
  20. }
  21. @Around(
  22. value = "call(void HelloWorld.sayHello())",
  23. argNames = "ajc$aroundClosure"
  24. )
  25. public void ajc$around$com_wyf_test_aopaspectjspring_example01_LogAspectJ$1$8852f95(AroundClosure ajc$aroundClosure) {
  26. System.out.println("日志前...");
  27. ajc$around$com_wyf_test_aopaspectjspring_example01_LogAspectJ$1$8852f95proceed(ajc$aroundClosure);
  28. System.out.println("日志后...");
  29. }
  30. public static LogAspectJ aspectOf() {
  31. if (ajc$perSingletonInstance == null) {
  32. throw new NoAspectBoundException("com_wyf_test_aopaspectjspring_example01_LogAspectJ", ajc$initFailureCause);
  33. } else {
  34. return ajc$perSingletonInstance;
  35. }
  36. }
  37. public static boolean hasAspect() {
  38. return ajc$perSingletonInstance != null;
  39. }
  40. }
  41. //
  42. // Source code recreated from a .class file by IntelliJ IDEA
  43. // (powered by Fernflower decompiler)
  44. //
  45. package com.wyf.test.aopaspectjspring.example01;
  46. import org.aspectj.lang.NoAspectBoundException;
  47. import org.aspectj.lang.annotation.Around;
  48. import org.aspectj.lang.annotation.Aspect;
  49. import org.aspectj.runtime.internal.AroundClosure;
  50. @Aspect
  51. public class LogAspectJ {
  52. static {
  53. try {
  54. ajc$postClinit();
  55. } catch (Throwable var1) {
  56. ajc$initFailureCause = var1;
  57. }
  58. }
  59. public LogAspectJ() {
  60. }
  61. @Around(
  62. value = "call(void HelloWorld.sayHello())",
  63. argNames = "ajc$aroundClosure"
  64. )
  65. public void ajc$around$com_wyf_test_aopaspectjspring_example01_LogAspectJ$1$8852f95(AroundClosure ajc$aroundClosure) {
  66. System.out.println("日志前...");
  67. ajc$around$com_wyf_test_aopaspectjspring_example01_LogAspectJ$1$8852f95proceed(ajc$aroundClosure);
  68. System.out.println("日志后...");
  69. }
  70. public static LogAspectJ aspectOf() {
  71. if (ajc$perSingletonInstance == null) {
  72. throw new NoAspectBoundException("com_wyf_test_aopaspectjspring_example01_LogAspectJ", ajc$initFailureCause);
  73. } else {
  74. return ajc$perSingletonInstance;
  75. }
  76. }
  77. public static boolean hasAspect() {
  78. return ajc$perSingletonInstance != null;
  79. }
  80. }

发表评论

表情:
评论列表 (有 0 条评论,57人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Java简单死锁例子

    死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。 简单来说: 1. 线程1和线程2的执行逻