java - 通过 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();
}
}
}
提前致谢。
解决方案
我假设
- 您使用完整的 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()
,但我将其留给您。您的问题是关于切入点,而不是关于如何从连接点中提取哪些信息。
推荐阅读
- android - 不清楚 MotionLayout 参数
- php - 在购物车中添加我的订单时重复的项目
- spring-boot - Hibernate ColumnTransformer 和 DataJpaTest
- regex - 正则表达式从文件名中捕获页码
- javascript - 在单个 JSON 文件中循环多个数组(Vanilla JS)
- database - 如何让 Tableau 仅提取特定数据点
- android - 适用于 Android / iOS 的最佳图标大小
- reactjs - 接下来,Redux 和 Kepler.js 和 Deck.GL
- java - 更好的方式来组织 gradle pluginManagement 块
- node.js - 如何在 heroku 应用程序中配置 node-oracledb