You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

修改AspectJ实现包内通用切面及解决初始化异常方案

解决AspectJ通用切面改造及NoAspectBoundException异常

一、异常原因及解决步骤

NoAspectBoundException通常是切面未被正确织入JVM,或类加载时未将其识别为AspectJ切面导致的,解决步骤如下:

  • 确认依赖配置:确保项目引入AspectJ核心依赖:aspectjrt.jar(运行时库)和aspectjweaver.jar(织入器,运行时织入需用)。
  • 正确织入切面:
    • 编译时织入(CTW):用AspectJ编译器ajc替代javac编译代码,确保切面被织入目标类。
    • 运行时织入(LTW):启动JVM时添加参数-javaagent:/path/to/aspectjweaver.jar,让织入器动态织入切面。
  • 检查切面类配置:确保切面类上的@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");
    }
}

关键修改点说明

  1. 切点表达式通用化:
    • 用org.mazouz.aop..*匹配包及其子包下的所有类,替代原有的org.mazouz.aop.Main特定类。
    • execution(* org.mazouz.aop..*.*(..)):匹配包内所有类的所有方法执行。
    • execution(org.mazouz.aop..*.new(..)):匹配包内所有类的构造方法执行。
    • set(* org.mazouz.aop..*.*) && within(org.mazouz.aop..*):匹配包内所有类的字段修改操作。
  2. main方法切点扩展:将原针对Main类的main方法切点,改为匹配包内所有类的main方法,增强通用性。
  3. 变量名优化:将局部变量lineNumber改为sourceLineNumber,避免与类静态变量重名,消除潜在混淆。

三、验证方法

  1. 在org.mazouz.aop包下新增测试类,添加方法、构造方法和字段修改逻辑。
  2. 使用ajc编译所有代码(或启用运行时织入),运行测试类的main方法。
  3. 检查输出的CSV文件,确认所有类的方法、构造方法、字段操作都被正确记录。

内容的提问来源于stack exchange,提问作者sadhana gm

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 03:59:53