
本文详解如何通过 aspectj 的 `get()` 和 `set()` 切点表达式,结合自定义注解(如 `@monitor`),精准拦截被标注字段的直接读写访问,并提供可运行的完整示例与关键注意事项。
在基于 Spring 或纯 AspectJ 的 AOP 开发中,开发者常希望对特定字段的访问行为(如读取或赋值)进行统一监控、日志记录或权限校验。但需注意:Java 字节码层面不为字段访问生成独立方法,因此无法像拦截 getter/setter 那样用 execution() 直接匹配——必须使用 AspectJ 特有的 get() 与 set() 切点指示符。
✅ 正确做法:使用 get(@Annotation Type Field) 和 set(@Annotation Type Field)
以下是一个最小可验证示例(MCVE),展示如何实现对 @Monitor 标注字段的读写拦截:
1. 定义自定义注解
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface Monitor {}2. 标注目标字段的类
public class Point {
@Monitor
private int x;
public int getX() { return x; }
public void setX(int v) { x = v; }
public static void main(String[] args) {
Point p = new Point();
// ① 通过 getter/setter 间接访问 → 触发切点(因内部含字段读写字节码)
p.setX(11);
System.out.println(p.getX());
// ② 直接字段访问(包内/反射等)→ 同样触发切点
p.x = 22;
System.out.println(p.x);
}
}3. 编写切面逻辑(关键!)
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class MonitorAspect {
@Around("get(@Monitor * *)")
public Object onFieldRead(ProceedingJoinPoint jp) throws Throwable {
System.out.printf("[READ] %s%n", jp.getSignature());
return jp.proceed();
}
@Around("set(@Monitor * *)")
public Object onFieldWrite(ProceedingJoinPoint jp) throws Throwable {
System.out.printf("[WRITE] %s%n", jp.getSignature());
return jp.proceed();
}
}? 切点语法说明: get(@Monitor * *):匹配所有被 @Monitor 注解的任意类型、任意名称字段的 读取操作(即 field 读指令); set(@Monitor * *):匹配所有被 @Monitor 注解的字段的 写入操作(即 putfield/putstatic 指令)。 * * 分别代表“任意类型”和“任意字段名”,支持通配。
? 运行输出示例:
[WRITE] set(int Point.x) [READ] get(int Point.x) 11 [WRITE] set(int Point.x) [READ] get(int Point.x) 22
可见:无论通过 getter/setter 还是直接字段赋值,只要字节码中出现对应字段的 get/set 指令,均会被捕获。
⚠️ 重要注意事项
- 不拦截 getter/setter 方法本身:此方案拦截的是字段访问动作,而非 getX() 或 setX() 方法执行。若需拦截方法调用,请改用 execution(* *(..)) 并结合 @annotation + 方法签名匹配。
- 需启用编译时织入(CTW)或加载时织入(LTW):get()/set() 切点属于 AspectJ 特有语义,标准 Spring AOP(基于代理)不支持。必须使用 ajc 编译器或配置 LTW。
- 字段必须具有运行时保留策略(@Retention(RUNTIME)),否则织入时无法识别注解。
- 访问控制不影响织入:即使字段是 private,AspectJ 仍可在字节码层拦截其读写(无需 AccessibleObject.setAccessible(true))。
✅ 总结
通过 @Around("get(@Monitor * *)") 和 @Around("set(@Monitor * *)"),你可以优雅地实现对标注字段的底层读写行为进行横切处理。该方案轻量、精准,适用于审计日志、敏感字段访问控制、性能统计等场景。务必确保项目已正确集成 AspectJ 工具链,并理解其与 Spring AOP 的能力边界。










