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 AOP | AspectJ |
---|---|
在纯 Java 中实现 | 使用 Java 编程语言的扩展实现 |
不需要单独的编译过程 | 除非设置 LTW,否则需要 AspectJ 编译器 (ajc) |
只能使用运行时织入 | 运行时织入不可用。支持编译时、编译后和加载时织入 |
功能不强-仅支持方法级编织 | 更强大 - 可以编织字段、方法、构造函数、静态初始值设定项、最终类/方法等…。 |
只能在由 Spring 容器管理的 bean 上实现 | 可以在所有域对象上实现 |
仅支持方法执行切入点 | 支持所有切入点 |
代理是由目标对象创建的, 并且切面应用在这些代理上 | 在执行应用程序之前 (在运行时) 前, 各方面直接在代码中进行织入 |
比 AspectJ 慢多了 | 更好的性能 |
易于学习和应用 | 相对于 Spring AOP 来说更复杂 |
aspectj的HelloWorld
亲手写例子体验aspectj是很好的方法帮助理解的事情。
以下是 aspectJ 的 helloworld 新建步骤。我建的是springboot带web的项目,因为这个项目可以使用web比较方便。
测试的时候要注意:有时候不知道是不是缓存,运行测试main方法会发现拦截不生效,需要cmd+f9重新编译,才生效
步骤:
- 建测试用的project
在 IDEA 里,点击 File->New->Project…->Spring Initialzer->一路进行创建包含spring web的project(详细步骤略,不用严格按照这操作,建一个project可以用maven即可) - 修改 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>
引入所需的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
正是在我们引入的aspectj1.5.4的jar包中。
PS:其实aspectjtools这个jar也是有这个类的,所以aspectjtools和aspectjrt两个GAV二选一也是可以的,或者连个都保留也是可以的。不过我们还是遵循留aspectjrt删除aspectjtools的GAV
新建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();
}
}
- 这个aspect 类是一个.aj 扩展名的文件,如
运行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;
}
}
还没有评论,来说两句吧...