现在java生态中spring大行其道,一般使用aspectj进行切面编程使用注解方式实现,比较少使用原生的aspectj编程,网上的资料也比较少。最近工作中需要封装redisson客户端提供统一的redis组件,主要就是实现耗时日志打印以及上报,压测支持等功能。为了避免组件依赖spring的情况,使用了aspectj原生库编程。工程基于jdk1.8。
1.aspectj介绍
aspectj定义(来自wiki):AspectJ是在PARC为Java编程语言创建的面向方面的编程扩展。它可以在Eclipse Foundation开源项目中使用,既可以单独使用,也可以集成到Eclipse中。
关于面向切面编程,这里不再赘述,一般用于实现通用的功能,比如日志打印,权限控制等。aspectj通过定义切面,将
2.配置以及使用
2.1配置
aspectj依赖两个核心库(aspectjrt,aspectjweaver),所以在需要在工程pom文件中添加一下依赖
<dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.8.9</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.9</version> </dependency>
要使aspectj织入功能生效还需要添加aspectj-maven-plugin插件,配置如下。
<build> <plugins> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>aspectj-maven-plugin</artifactId> <version>1.7</version> <configuration> <complianceLevel>1.8</complianceLevel> <source>1.8</source> <target>1.8</target> <showWeaveInfo>true</showWeaveInfo> <verbose>true</verbose> <Xlint>ignore</Xlint> <encoding>UTF-8</encoding> <weaveDependencies> <weaveDependency> <groupId>org.redisson</groupId> <artifactId>redisson</artifactId> </weaveDependency> </weaveDependencies> </configuration> <executions> <execution> <goals> <!-- use this goal to weave all your main classes --> <goal>compile</goal> <!-- use this goal to weave all your test classes --> <!--<goal>test-compile</goal>--> </goals> </execution> </executions> </plugin> </plugins> </build>
aspectj可以选择织入的时期,主要分为三种:编译时,编译后,运行期。编译时织入只对织入代码在本工程的情况,编译后织入除了本工程代码还包括针对织入代码在第三方jar包情况,运行时织入只有在代码运行时才做动态切面。上面这个例子是编译后的配置,其中weaveDependencies配置了第三方jar包信息,只需要编译是织入可以不用配置。运行时织入参考(https://www.baeldung.com/aspectj)
2.2 aspect编写
切面类代码如下
/** * @date 2018/12/22 */ public aspect CommandAspect { /** * redis命令方法切面,所有redis命令都会经过 */ public pointcut callSendCommand(): execution (* org.redisson.command.CommandAsyncService.sendCommand(..)); /** * RedissonExpirable的所有子类方法切面,基本包含了redisson内置的常用数据结构 */ public pointcut callExpireSubClassMethod(): execution (* org.redisson.RedissonExpirable+.*(..)); Object around(): callSendCommand() { //long startNs = System.nanoTime(); Object result = proceed(); //AspectUtil.sendMetrics(startNs, thisJoinPoint); return result; } Object around(): callExpireSubClassMethod(){ Object result = proceed(); //AspectUtil.resetExpireTime(thisJoinPoint); return result; } }
对于熟悉注解方式实现的同学可以发现,这种方式与注解方式十分相像,这里是实现了around,同样也支持before,after方式。在idea intellij调试时,无法在apsect类中断点,但是可以在其依赖的class类中断点,这对于我们第一次编码的时候存在一些困扰。
编码完成后,使用mvn compile命令,实现代码织入,之后就可以进行调试了
参考文档:
http://opoo.org/aspectj-compile-time-weaving/
https://www.baeldung.com/aspectj