AspectJ 详解

AspectJ 是 Java 生态中最强大、最成熟的 AOP 框架,Spring AOP 的底层就依赖 AspectJ 的注解和切点表达式语法。

Spring AOP vs AspectJ

对比维度 Spring AOP AspectJ
实现方式 动态代理(JDK/CGLIB) 字节码织入(编译期/类加载期/运行期)
织入时机 运行时 编译期、类加载期、运行期
性能 一般(代理调用开销) 更好(直接执行增强代码)
支持粒度 仅方法级别的连接点 字段、构造器、静态方法、异常处理等
目标对象限制 需要 Spring 容器管理 任意 Java 对象
private/protected 方法 ❌ 不支持 ✅ 支持(通过 Load-Time Weaving)
IDE 支持 一般 Eclipse/AJDT 插件支持

AspectJ 五种织入时机

织入类型 时机 说明
编译期织入 (CTW) 源代码编译时 需要 AspectJ 编译器(ajc)
编译后织入 (BTW) 编译后处理 class 文件 修改已有 class
加载期织入 (LTW) 类加载到 JVM 时 通过 Java Agent 实现,最灵活
运行时织入 运行时 Spring AOP 采用的方式(非原生 AspectJ)
混合织入 组合使用 部分代码编译期织入 + LTW

核心语法

1. 切点表达式完整语法
复制代码
execution([权限修饰符] 返回值类型 [类全限定名.]方法名(参数列表) [throws 异常类型])

示例:

bash 复制代码
// 任意 public 方法
execution(public * *(..))

// 指定包下所有方法
execution(* com.example.service..*.*(..))  // .. 表示子包

// 参数匹配
execution(* *.*(String, int))              // 两个特定参数
execution(* *.*(..))                       // 任意参数
execution(* *.*(String, ..))               // 第一个是 String,后面任意

// 返回值匹配
execution(* com.example.User+.*(..))       // User 及其子类的方法
2. 常用切点指示器
指示器 说明 示例
execution 匹配方法执行 execution(* com..*.*(..))
within 匹配类型内所有方法 within(com.example.service.*)
this 匹配代理对象类型 this(com.example.UserService)
target 匹配目标对象类型 target(com.example.UserService)
args 匹配参数类型 args(String, int)
@within 匹配有某注解的类型 @within(org.springframework.stereotype.Service)
@target 匹配目标对象有注解 @target(com.example.annotation.MyAnnotation)
@args 匹配参数有注解 @args(com.example.annotation.Valid)
@annotation 匹配方法有注解 @annotation(com.example.annotation.Log)
bean 匹配 Spring Bean 名称 bean(userService)

LTW(加载期织入)配置示例

1. 创建切面

java 复制代码
@Aspect
public class PerformanceAspect {
    
    @Around("execution(* com.example.service.*.*(..))")
    public Object measureTime(ProceedingJoinPoint pjp) throws Throwable {
        long start = System.nanoTime();
        Object result = pjp.proceed();
        long end = System.nanoTime();
        System.out.println(pjp.getSignature() + " 耗时: " + (end - start) + "ns");
        return result;
    }
    
    // 拦截字段访问
    @Before("get(String com.example.User.name)")
    public void beforeGetName() {
        System.out.println("即将读取 name 字段");
    }
}

2. META-INF/aop.xml

XML 复制代码
<!DOCTYPE aspectj PUBLIC "-//AspectJ//DTD//EN" "http://www.eclipse.org/aspectj/dtd/aspectj.dtd">
<aspectj>
    <weaver options="-verbose -showWeaveInfo">
        <!-- 指定需要织入的包 -->
        <include within="com.example.service..*"/>
        <include within="com.example.entity..*"/>
    </weaver>
    
    <aspects>
        <aspect name="com.example.aspect.PerformanceAspect"/>
    </aspects>
</aspectj>

3. JVM 启动参数

bash 复制代码
-javaagent:path/to/aspectjweaver.jar

Spring 集成 AspectJ

方式一:使用注解(最常用)
java 复制代码
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)  // 启用 AspectJ 注解支持
@ComponentScan("com.example")
public class AppConfig {
}
方式二:XML 配置
XML 复制代码
<aop:aspectj-autoproxy proxy-target-class="true"/>

<bean id="logAspect" class="com.example.aspect.LogAspect"/>

高级特性示例

1. 拦截字段赋值
java 复制代码
@Aspect
public class FieldMonitorAspect {
    
    @Before("set(* com.example.User.age)")
    public void beforeSetAge(JoinPoint jp) {
        int newAge = (int) jp.getArgs()[0];
        if (newAge < 0 || newAge > 150) {
            throw new IllegalArgumentException("年龄非法: " + newAge);
        }
    }
}
2. 拦截异常处理
java 复制代码
@Aspect
public class ExceptionHandlerAspect {
    
    @AfterThrowing(pointcut = "execution(* *.*(..))", throwing = "ex")
    public void handleException(Exception ex) {
        System.out.println("捕获异常: " + ex.getMessage());
        // 自定义异常处理逻辑
    }
    
    // 拦截 Handler 方法本身
    @Before("handler(java.lang.Exception+)")
    public void beforeExceptionHandler() {
        System.out.println("即将处理异常");
    }
}
3. 多重切点组合
java 复制代码
@Aspect
public class ComplexPointcutAspect {
    
    // 定义复用切点
    @Pointcut("execution(* com.example.service.*.*(..))")
    public void serviceLayer() {}
    
    @Pointcut("@annotation(com.example.annotation.Auditable)")
    public void auditableMethod() {}
    
    @Pointcut("bean(*Service)")
    public void serviceBean() {}
    
    // 组合使用
    @Before("serviceLayer() && auditableMethod() && !serviceBean()")
    public void beforeCombined() {
        System.out.println("满足多个条件的切点");
    }
}
4. 软升级(Softening)异常
java 复制代码
// 将受检异常转换为非受检异常
@DeclareSoft("execution(* *.*(..))")
public static SoftException softException;

性能优化建议

优化项 说明
窄化切点 切点表达式越精确,匹配速度越快
避免使用 * 通配符过多 减少模式匹配开销
使用 within 替代 execution within 匹配更快(基于类型)
优先使用编译期织入 运行时开销最小
减少 AspectJ 切面数量 每个切面增加匹配检查
相关推荐
咖啡八杯1 小时前
GoF设计模式——解释器模式
java·后端·spring·设计模式
优橙教育1 小时前
5G网优培训 vs Java开发:转行选哪个?
java·开发语言·5g
掘金码甲哥1 小时前
这块终端神器, 必须吹爆!
后端
糖果店的幽灵1 小时前
【DeepAgents 从入门到精通】Context Management 上下文管理
java·人工智能·后端·spring·中间件·langgraph·deepagents
Csvn2 小时前
📊 SQL 入门 Day 8:集合操作 — 用 SQL 做数学里的"并交差"
后端·sql
腻害兔3 小时前
【若依项目-产品经理视角】RuoYi-Vue-Pro 源码拆解:字典、短信、邮件、通知——后台系统的“基础设施四件套“!
java·前端·vue.js·产品经理·ai编程
碎光拾影3 小时前
ARM交叉工具链各工具作用及IMX6ULL平台LED+蜂鸣器裸机程序实现
java·开发语言·数据库
Miao121314 小时前
微服务 API 测试实践:海外某民宿平台如何构建模式驱动测试基础设施
java·开发语言
腻害兔4 小时前
【若依项目-产品经理视角】RuoYi-Vue-Pro 源码拆解:支付模块 yudao-module-pay,一个让产品经理都看懂的支付中台设计
java·前端·vue.js·产品经理·ai编程
码事漫谈5 小时前
告别数据孤岛与AI“水土不服”:金仓多模融合时序库如何让数据真正服务于业务
后端