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 小时前
Java-20 深入浅出 MyBatis - 手写ORM框架1 从原始 JDBC 暴露的 6 大问题开始
java·后端
雪隐1 小时前
AI股票小助手06-Backtrader 量化回测
人工智能·后端
qq_2518364571 小时前
2026计算机毕设选题|3000套高质量SpringBoot实战项目(含完整源码)(每人一套不收米)
java·spring boot·课程设计
设计师小聂!2 小时前
Java异常处理
java·开发语言·后端·编辑器·idea
ihuyigui2 小时前
国际商超零售短信接口
大数据·前端·后端·架构·零售
SimonKing2 小时前
实用,DynamicTP进阶之数据采集与告警
java·后端·程序员
用户298698530142 小时前
Java 进阶:基于模板生成 Word 文档的实践思路
java·后端
涛声依旧-底层原理研究所2 小时前
响应式编程:map与flatMap实战解析
java
枕星而眠2 小时前
C++ 面向对象核心机制深度解析:多态性、虚函数、虚继承与 final 类
运维·开发语言·c++·后端