如何使用 Spring AOP 或 AspectJ 拦截给定方法中的每个方法调用

Cra*_*der 6 java aspectj spring-aop spring-aspects

 class Test {

@override
public String a(){
b();
d();
}


private String b() {
c();
}

private String c(){
d();
}
private String d(){}

}
Run Code Online (Sandbox Code Playgroud)

我想拦截从重写方法 A() 调用的 Test 类的每个方法,并想知道 b()、c() 等每个方法在单独处理某些业务逻辑时花费了多少时间。

如何使用 Spring AOP 或 Aspectj 实现它?

kri*_*aex 5

为了

  • 编织成私有方法,
  • 在一个类中处理自调用,
  • 动态确定控制流并将拦截限制为仅由您的接口方法直接或间接调用的方法

您需要使用 LTW(加载时编织)从 Spring AOP(基于代理,许多限制,缓慢)切换到AspectJ,如 Spring 手册中所述。

这是一个纯 AspectJ(没有 Spring,只有 Java SE)的例子,你可以很容易地适应你的需求:

示例界面

package de.scrum_master.app;

public interface TextTransformer {
  String transform(String text);
}
Run Code Online (Sandbox Code Playgroud)

类实现接口包括。main方法:

如您所见,我编写了一个像您这样的示例,并且还让方法花时间以便稍后在方面进行测量:

package de.scrum_master.app;

public class Application implements TextTransformer {
  @Override
  public String transform(String text) {
    String geekSpelling;
    try {
      geekSpelling = toGeekSpelling(text);
      return toUpperCase(geekSpelling);
    } catch (InterruptedException e) {
      throw new RuntimeException(e);
    }

  }

  private String toGeekSpelling(String text) throws InterruptedException {
    Thread.sleep(100);
    return replaceVovels(text).replaceAll("[lL]", "1");
  }

  private String replaceVovels(String text) throws InterruptedException {
    Thread.sleep(75);
    return text.replaceAll("[oO]", "0").replaceAll("[eE]", "?");
  }

  private String toUpperCase(String text) throws InterruptedException {
    Thread.sleep(50);
    return text.toUpperCase();
  }

  public static void main(String[] args) throws InterruptedException {
    System.out.println(new Application().transform("Hello world!"));
  }
}
Run Code Online (Sandbox Code Playgroud)

方面:

package de.scrum_master.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import static java.lang.System.currentTimeMillis;

@Aspect
public class TimingAspect {
  @Around("execution(* *(..)) && cflow(execution(* de.scrum_master.app.TextTransformer.*(..)))")
  public Object measureExecutionTime(ProceedingJoinPoint thisJoinPoint) throws Throwable {
    long startTime = currentTimeMillis();
    Object result = thisJoinPoint.proceed();
    System.out.println(thisJoinPoint + " -> " + (currentTimeMillis() - startTime) + " ms");
    return result;
  }
}
Run Code Online (Sandbox Code Playgroud)

控制台日志:

execution(String de.scrum_master.app.Application.replaceVovels(String)) -> 75 ms
execution(String de.scrum_master.app.Application.toGeekSpelling(String)) -> 189 ms
execution(String de.scrum_master.app.Application.toUpperCase(String)) -> 63 ms
execution(String de.scrum_master.app.Application.transform(String)) -> 252 ms
H?110 W0R1D!
Run Code Online (Sandbox Code Playgroud)

您还可以transform(..)通过将切入点从 更改为cflow()来排除该方法cflowbelow()

@Around("execution(* *(..)) && cflowbelow(execution(* de.scrum_master.app.TextTransformer.*(..)))")
Run Code Online (Sandbox Code Playgroud)

那么控制台日志就是:

execution(String de.scrum_master.app.Application.replaceVovels(String)) -> 77 ms
execution(String de.scrum_master.app.Application.toGeekSpelling(String)) -> 179 ms
execution(String de.scrum_master.app.Application.toUpperCase(String)) -> 62 ms
H?110 W0R1D!
Run Code Online (Sandbox Code Playgroud)

顺便说一句,请务必阅读 AspectJ 和/或 Spring AOP 手册。