首页 > 解决方案 > 通过 aspectj 拦截被拦截函数内的所有函数调用

问题描述

我是 AspectJ 的新手,我想实现的反射类似于在下面的示例中:

一个测试类:

    public class Sample {
    Home home = new Home();
    Account account = new Account();
    AccountAuthentication accountAuthentication = new AccountAuthentication();

    @Test
    public void loginInvalidCredentials(){
    home.clickOnAccount();
    account.login("Admin", "secret");
    accountAuthentication.waitForPage();
    Assert.assertTrue(true);
    }
}

我想记录输出是这样的:

packageName.Sample.loginInvalidCredentials 
    packageName.Home.clickOnAccount();
    packageName.Account.login(userName = "Admin", Password ="secret");
    packageName.AccountAuthentication.waitForPage();
    packageName.Assert.assertTrue(value= true);

我已经packageName.Sample.loginInvalidCredentials使用 AspectJ访问了函数的名称

@Aspect
public class AspectClass {
    @Around("execution(* *(..)) && @annotation(org.testng.annotations.Test)")
    public void around(ProceedingJoinPoint point) throws Throwable {
        Method method = MethodSignature.class.cast(point.getSignature()).getMethod();
        String methodName = method.getName();
        System.out.println("Aspect called for method "+ point.getSignature().getDeclaringType().name +"."+methodName);

        try { 
             //TODO intercept each function call inside the method without any custom anotation and get the value of parameters as well
              joinPoint.proceed();
             } 
    }
}

提前致谢。

标签: javareflectionaspectj

解决方案


我假设

  • 您使用完整的 AspectJ 而不是 Spring AOP 之类的东西(因为那样答案将不适用),
  • 不想递归地记录方法调用,而只想记录从您的@Test方法中直接调用的那些。例如,如果Account.login(..)会在内部调用Account.checkPassword(..),则不应记录。这也是可能的,但解决方案看起来会有所不同。
  • 您要做的就是记录测试执行而不做任何其他事情,因此@Around不需要建议,@Before就足够了。

虚拟应用程序类:

package de.scrum_master.app;

public class Account {
  public void login(String user, String password) {
    checkPassword(password);
  }

  public void checkPassword(String password) {}
}
package de.scrum_master.app;

public class AccountAuthentication {
  public void waitForPage() {}
}
package de.scrum_master.app;

public class Home {
  public void clickOnAccount() {
    doSomethingElse();
  }

  public void doSomethingElse() {}
}

标记注释:

我自己创建了这个,因为我懒得用我通常不使用的 TestNG 建立一个项目。

package org.testng.annotations;

import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

@Retention(RUNTIME)
@Target(METHOD)
public @interface Test {}

示例应用:

package de.scrum_master.app;

import org.testng.annotations.Test;

public class Sample {
  Home home = new Home();
  Account account = new Account();
  AccountAuthentication accountAuthentication = new AccountAuthentication();

  @Test
  public void loginInvalidCredentials() {
    home.clickOnAccount();
    account.login("Admin", "secret");
    accountAuthentication.waitForPage();
  }

  public static void main(String[] args) {
    new Sample().loginInvalidCredentials();
  }
}

方面:

package de.scrum_master.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class TestExecutionLogger {
  @Before("execution(* *(..)) && @annotation(org.testng.annotations.Test)")
  public void logTest(JoinPoint joinPoint) {
    System.out.println(joinPoint);
  }

  @Before("call(* *(..)) && withincode(@org.testng.annotations.Test * *(..))")
  public void logTestActions(JoinPoint joinPoint) {
    System.out.println("  " + joinPoint);
  }
}

控制台日志:

execution(void de.scrum_master.app.Sample.loginInvalidCredentials())
  call(void de.scrum_master.app.Home.clickOnAccount())
  call(void de.scrum_master.app.Account.login(String, String))
  call(void de.scrum_master.app.AccountAuthentication.waitForPage())

当然,如果你真的认为你需要从信息丰富的 AspectJ 日志输出中去除简单的方法名称,你可以改进你的方面:

  @Before("execution(* *(..)) && @annotation(org.testng.annotations.Test)")
  public void logTest(JoinPoint joinPoint) {
    System.out.println(joinPoint.getSignature());
  }

  @Before("call(* *(..)) && withincode(@org.testng.annotations.Test * *(..))")
  public void logTestActions(JoinPoint joinPoint) {
    System.out.println("  " + joinPoint.getSignature());
  }
void de.scrum_master.app.Sample.loginInvalidCredentials()
  void de.scrum_master.app.Home.clickOnAccount()
  void de.scrum_master.app.Account.login(String, String)
  void de.scrum_master.app.AccountAuthentication.waitForPage()

如果您有意删除方法的返回类型或包含示例中的实际参数值,您可以进一步完善它,这很容易通过 实现JoinPoint.getArgs(),但我将其留给您。您的问题是关于切入点,而不是关于如何从连接点中提取哪些信息。


推荐阅读