如何修改 AspectJ 代码以创建可应用于包中任何类的通用方面,而不是仅限于“Main”等特定类名?
package org.mazouz.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@Aspect
public class AspectJAspect {
private static final String CSV_FILE_PATH = "/home/hadoop/Documents/AspectJ/output1.csv";
private static final List < String > logEntries = new ArrayList < > ();
private static boolean constructorExecuting = false;
private static long aspectStartTime; // Variable to store the start time of the aspect
private static int lineNumber = 1; // Start line number from 1
// Pointcut to capture any method execution within the Main class
@Pointcut("execution(* org.mazouz.aop.Main.*(..))")
public void anyMethodExecution() {}
// Pointcut to capture constructor execution within the Main class
@Pointcut("execution(org.mazouz.aop.Main.new(..))") //Matches any constructor signature
public void constructorExecution() {}
// Pointcut to capture any field update within methods of the Main class
@Pointcut("set(* org.mazouz.aop.Main.*) && within(org.mazouz.aop.Main)")
public void fieldUpdate() {}
// Advice triggered before entering the main method
@Before("execution(public static void org.mazouz.aop.Main.main(String[]))")
public void beforeMainMethod(JoinPoint joinPoint) {
aspectStartTime = System.currentTimeMillis(); // Capture start time of the aspect
addLogEntry("System Start", "", joinPoint); // Provided log line before entering the main method
addLogEntry("Thread Start", "main", joinPoint); // Provided log line for thread start after "System Start"
addLogEntry("Type Load", "class=java.lang.Object", joinPoint);
// Retrieve class name dynamically and pass it to the addLogEntry method
String className = joinPoint.getSignature().getDeclaringTypeName();
className = className.substring(className.lastIndexOf('.') + 1); // Extract only the class name
addLogEntry("Type Load", " class=" + className, joinPoint);
}
// Advice triggered before entering the constructor
@Before("constructorExecution()")
public void beforeConstructorExecution(JoinPoint joinPoint) {
String constructorName = joinPoint.getSignature().getName();
addLogEntry("Constructor Entry", constructorName, joinPoint);
constructorExecuting = true;
}
// Advice triggered after exiting the constructor
@After("constructorExecution()")
public void afterConstructorExecution(JoinPoint joinPoint) {
String constructorName = joinPoint.getSignature().getName();
addLogEntry("Constructor Exit", constructorName, joinPoint);
constructorExecuting = false;
}
// Advice triggered before any method execution within the Main class
@Before("anyMethodExecution()")
public void beforeMethodExecution(JoinPoint joinPoint) {
addLogEntry("Method Entry", joinPoint.getSignature().getName(), joinPoint);
}
// Advice triggered after any field update within methods of the Main class
@After("fieldUpdate()")
public void afterFieldUpdate(JoinPoint joinPoint) {
String fieldName = joinPoint.getSignature().getName();
Object newValue = joinPoint.getArgs()[0];
if (constructorExecuting) {
addLogEntry("Field Write in Constructor", fieldName + " updated to " + newValue, joinPoint);
} else {
addLogEntry("Field Write", fieldName + " updated to " + newValue, joinPoint);
}
}
// Advice triggered after any method execution within the Main class
@After("anyMethodExecution()")
public void afterMethodExecution(JoinPoint joinPoint) {
addLogEntry("Method Exit", joinPoint.getSignature().getName(), joinPoint);
}
// Helper method to add log entries to the list
private synchronized void addLogEntry(String event, String additionalInfo, JoinPoint joinPoint) {
String threadName = Thread.currentThread().getName();
int line = lineNumber++;
String logEntry;
String fileName;
int lineNumber;
if (joinPoint.getSourceLocation() != null) {
fileName = joinPoint.getSourceLocation().getFileName();
lineNumber = joinPoint.getSourceLocation().getLine();
} else {
// Handle null source location
fileName = "Unknown";
lineNumber = -1;
}
if (line > 4) {
logEntry = "\"" + threadName + "\",\"" + line + "\",\"" + fileName + ":" + lineNumber + "\",\"" + event + "\",\"" + additionalInfo + "\"";
} else {
logEntry = "\"" + threadName + "\",\"" + line + "\",\"SYSTEM\",\"" + event + "\",\"" + additionalInfo + "\"";
}
logEntries.add(logEntry);
}
// Helper method to write all log entries to the file
private synchronized void writeLogEntriesToFile() {
try (FileWriter writer = new FileWriter(CSV_FILE_PATH)) {
for (String logEntry: logEntries) {
writer.append(logEntry);
writer.append("\n");
}
} catch (IOException e) {
e.printStackTrace();
}
}
// Advice triggered after the main method exits
@After("execution(public static void org.mazouz.aop.Main.main(String[]))")
public void afterMainMethod(JoinPoint joinPoint) {
writeLogEntriesToFile();
long aspectEndTime = System.currentTimeMillis(); // Capture end time of the aspect
long aspectExecutionTime = aspectEndTime - aspectStartTime; // Calculate aspect execution time
System.out.println("Aspect execution time: " + aspectExecutionTime + " milliseconds");
}
}
Main.java
package jvmti;
public class Main {
public static int staticVariable;
public int a;
public int b;
public int c;
public int d;
public int e;
public Main(int d, int e) {
this.d = d;
this.e = e;
}
public static void main(String[] args) {
Main aop = new Main(5, 10);
aop.methodA(5);
aop.methodB(6);
aop.methodC(7);
Main.updateStaticVariable(8);
}
public void methodA(int value) {
a = value;
a = 20;
}
public void methodB(int value) {
b = value;
c = 15;
}
public void methodC(int value) {
d = value;
e = 25;
}
public static void updateStaticVariable(int value) {
staticVariable = value;
}
}
如何修改我的方面代码,使其足够通用,可以适用于包中的任何类,而不是仅限于“Main”等特定类名称?此外,当尝试进行此修改(试图使方面通用)时,我遇到了以下错误。
我该如何解决它才能成功实现通用方面
Exception in thread "main" java.lang.NoClassDefFoundError: Could not initialize class org.mazouz.aop.AspectJAspect
at org.mazouz.aop.Main.main(Main.java:23)
Caused by: java.lang.ExceptionInInitializerError: Exception org.aspectj.lang.NoAspectBoundException: org.mazouz.aop.AspectJAspect [in thread "main"]
at org.mazouz.aop.AspectJAspect.aspectOf(AspectJAspect.aj:1)
at org.mazouz.aop.AspectJAspect.<clinit>(AspectJAspect.aj:17)
at org.mazouz.aop.Main.main(Main.java:18)
关于问题1,你检查过AspectJ文档吗?例如:
至于
NoClassDefFoundError
,我无法确定,但也许你尝试过这样的事情:
execution(* *(..)) && within(org.mazouz..*)
execution(new(..)) && within(org.mazouz..*)
这会重现您的错误。因为你的方面位于同一个基础包中,所以你需要排除它以避免 iut 尝试编织自身,这在某些情况下很好,anbd 使 AspectJ 如此强大,但你想在这里避免:
execution(* *(..)) && within(org.mazouz..*) && !within(*..*Aspect)
execution(new(..)) && within(org.mazouz..*) && !within(*..*Aspect)