修改AspectJ实现包内通用切面及解决初始化异常方案
解决AspectJ通用切面改造及NoAspectBoundException异常
一、异常原因及解决步骤
NoAspectBoundException通常是切面未被正确织入JVM,或类加载时未将其识别为AspectJ切面导致的,解决步骤如下:
- 确认依赖配置:确保项目引入AspectJ核心依赖:
aspectjrt.jar(运行时库)和aspectjweaver.jar(织入器,运行时织入需用)。 - 正确织入切面:
- 编译时织入(CTW):用AspectJ编译器
ajc替代javac编译代码,确保切面被织入目标类。 - 运行时织入(LTW):启动JVM时添加参数
-javaagent:/path/to/aspectjweaver.jar,让织入器动态织入切面。
- 编译时织入(CTW):用AspectJ编译器
- 检查切面类配置:确保切面类上的
@Aspect注解未遗漏,且类在正确的类路径中。
二、改造为包内通用切面的代码修改
将所有针对Main类的切点替换为包级匹配,实现对org.mazouz.aop包下所有类的切面增强,修改后的完整切面代码如下:
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; private static int lineNumber = 1; // 匹配包及其子包内所有类的方法执行 @Pointcut("execution(* org.mazouz.aop..*.*(..))") public void anyMethodExecution() {} // 匹配包及其子包内所有类的构造方法执行 @Pointcut("execution(org.mazouz.aop..*.new(..))") public void constructorExecution() {} // 匹配包及其子包内所有类的字段修改 @Pointcut("set(* org.mazouz.aop..*.*) && within(org.mazouz.aop..*)") public void fieldUpdate() {} // 匹配包及其子包内所有类的main方法执行前 @Before("execution(public static void main(String[])) && within(org.mazouz.aop..*)") public void beforeMainMethod(JoinPoint joinPoint) { aspectStartTime = System.currentTimeMillis(); addLogEntry("System Start", "", joinPoint); addLogEntry("Thread Start", "main", joinPoint); addLogEntry("Type Load", "class=java.lang.Object", joinPoint); String className = joinPoint.getSignature().getDeclaringTypeName(); className = className.substring(className.lastIndexOf('.') + 1); addLogEntry("Type Load", " class=" + className, joinPoint); } @Before("constructorExecution()") public void beforeConstructorExecution(JoinPoint joinPoint) { String constructorName = joinPoint.getSignature().getName(); addLogEntry("Constructor Entry", constructorName, joinPoint); constructorExecuting = true; } @After("constructorExecution()") public void afterConstructorExecution(JoinPoint joinPoint) { String constructorName = joinPoint.getSignature().getName(); addLogEntry("Constructor Exit", constructorName, joinPoint); constructorExecuting = false; } @Before("anyMethodExecution()") public void beforeMethodExecution(JoinPoint joinPoint) { addLogEntry("Method Entry", joinPoint.getSignature().getName(), joinPoint); } @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); } } @After("anyMethodExecution()") public void afterMethodExecution(JoinPoint joinPoint) { addLogEntry("Method Exit", joinPoint.getSignature().getName(), joinPoint); } private synchronized void addLogEntry(String event, String additionalInfo, JoinPoint joinPoint) { String threadName = Thread.currentThread().getName(); int line = lineNumber++; String logEntry; String fileName; int sourceLineNumber; if (joinPoint.getSourceLocation() != null) { fileName = joinPoint.getSourceLocation().getFileName(); sourceLineNumber = joinPoint.getSourceLocation().getLine(); } else { fileName = "Unknown"; sourceLineNumber = -1; } if (line > 4) { logEntry = "\"" + threadName + "\" ,\"" + line + "\" ,\"" + fileName + ":" + sourceLineNumber + "\" ,\"" + event + "\" ,\"" + additionalInfo + "\""; } else { logEntry = "\"" + threadName + "\" ,\"" + line + "\" ,\"SYSTEM\" ,\"" + event + "\" ,\"" + additionalInfo + "\""; } logEntries.add(logEntry); } 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(); } } // 匹配包及其子包内所有类的main方法执行后 @After("execution(public static void main(String[])) && within(org.mazouz.aop..*)") public void afterMainMethod(JoinPoint joinPoint) { writeLogEntriesToFile(); long aspectEndTime = System.currentTimeMillis(); long aspectExecutionTime = aspectEndTime - aspectStartTime; System.out.println("Aspect execution time: " + aspectExecutionTime + " milliseconds"); } }
关键修改点说明
- 切点表达式通用化:
- 用
org.mazouz.aop..*匹配包及其子包下的所有类,替代原有的org.mazouz.aop.Main特定类。 execution(* org.mazouz.aop..*.*(..)):匹配包内所有类的所有方法执行。execution(org.mazouz.aop..*.new(..)):匹配包内所有类的构造方法执行。set(* org.mazouz.aop..*.*) && within(org.mazouz.aop..*):匹配包内所有类的字段修改操作。
- 用
- main方法切点扩展:将原针对
Main类的main方法切点,改为匹配包内所有类的main方法,增强通用性。 - 变量名优化:将局部变量
lineNumber改为sourceLineNumber,避免与类静态变量重名,消除潜在混淆。
三、验证方法
- 在
org.mazouz.aop包下新增测试类,添加方法、构造方法和字段修改逻辑。 - 使用
ajc编译所有代码(或启用运行时织入),运行测试类的main方法。 - 检查输出的CSV文件,确认所有类的方法、构造方法、字段操作都被正确记录。
内容的提问来源于stack exchange,提问作者sadhana gm
相关产品推荐
相关产品推荐

