如何使用AspectJ做切面,打印jar包中方法的执行日记

admin2024-05-15  0

最近在工作中遇到一个redis缓存中的hash key莫名其妙被删除的问题,我们用了J2Cache,二级缓存用的是redis。hash key莫名其妙被删除又没有日志,就想到做一个切面在调用redis删除hash key的方法的时候,打印日志,并且把调用链路打印出来。

但是这个类的对象不是spring ioc容器管辖的,所以没有办法使用spring AOP,只能用AspectJ。

AspectJ把切面代码织入目标代码有两种方式,一种是编译时织入,一种是类加载时织入

因为目标代码是第三方jar包的,所以这里只能选择类加载时编织(Load-time Weaving, LTW)

redis客服端使用的是lettuce,一开始我选择对io.lettuce.core.api.sync.RedisHashCommands#hdel这个方法打印日志,但是这是一个接口,而底层的具体的实现是一个代理类,切到这个代理类破费了一番功夫,最后执行还是报错:

java.lang.NoSuchMethodError:*.RedisDelCommandAop.aspectOf()L*/RedisDelCommandAop

实在没有办法放弃对RedisHashCommands进行切面,而改成对J2Cache中的二级缓存Level2Cache.evict方面进行切面。Level2Cache.evict切面成功后,对代理类切面也成功了(后面有详细叙述)。

普通类切面步骤有4步:

1 增加aspectJ依赖,这里以maven为例

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.9.19</version>
    </dependency>
</dependencies>

2. 配置aop.xml

src/main/resources/META-INF 目录下创建并配置 aop.xml 文件:

<aspectj>
    <weaver options="-verbose -showWeaveInfo">
        <!-- 目标类的包名,不确定直接用* -->
        <include within="com.example..*"/>
    </weaver>
    <aspects>
        <!-- 自定义切面类全路径 -->
        <aspect name="com.example.MyAspect"/>
    </aspects>
</aspectj>

3. 定义切面类

package com.example;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect
@Slf4j
public class MyAspect {

    @Before("execution(* com.example..*(..))")
    public void beforeMethod() {
        System.out.println("A method is about to be executed.");
    }
    
        // 切到某个接口下所有实现类的表达式
        @Around("execution(* net.oschina.j2cache.Level2Cache+.evict(..))")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        Object[] args = joinPoint.getArgs();
        log.debug("evict方法入参:{}", Arrays.toString(args));
        StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
        for (StackTraceElement element : stackTrace) {
            log.debug(element.getClassName() + "." + element.getMethodName());
        }

        return joinPoint.proceed();
    }
}

4. 运行时加入JVM参数,指向aspectjweaver的jar包,要确保路径正确

-javaagent:path/to/aspectjweaver.jar 

代理类切面需要增加的步骤

  1. 表达式

    @Around("execution(* com.sun.proxy.$Proxy*.hdel(..))")
    
  2. aop文件中增加开启切代理类

    <aspectj>
      <weaver options="-verbose -showWeaveInfo -Xset:weaveJavaxPackages=true -Xset:weaveAllIo=true -Xset:weaveAllProxies=true">
        <!-- Include the packages to be woven -->
        <include within="*"/>
      </weaver>
      <aspects>
        <!-- Specify the aspects to be woven -->
        <aspect name="*.RedisDelCommandAop"/>
      </aspects>
    </aspectj>
    
  3. 启动类上加上注解,后面测试这个步骤不加也可以成功

    @EnableAspectJAutoProxy
    

记录下我遇到的问题:

  1. idea运行的时候配置JVM参数,选错了地方。
    如何使用AspectJ做切面,打印jar包中方法的执行日记,在这里插入图片描述,第1张

  2. 打印日志定位aspectJ相关问题

    加入JVM参数,开启aspectJ日志打印:

    -Dorg.aspectj.weaver.showWeaveInfo=true -Dorg.aspectj.weaver.verbose=true
    

    如果配置正确,会打印如下日志:
    如何使用AspectJ做切面,打印jar包中方法的执行日记,在这里插入图片描述,第2张

    如果切面类不正确上面日志最后一行就是error Cannot register,原因是切面类上忘记加注解:@Aspect

    另外如果表达式不正确,没有切到切面,就不会打印下面的日志:
    如何使用AspectJ做切面,打印jar包中方法的执行日记,在这里插入图片描述,第3张

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明原文出处。如若内容造成侵权/违法违规/事实不符,请联系SD编程学习网:675289112@qq.com进行投诉反馈,一经查实,立即删除!