一、AOP 的核心思想:注解驱动
通过自定义注解标记需要增强的方法,切面逻辑根据注解动态增强代码。业务代码无需感知具体逻辑,只需关注自身职责。
二、电商通用场景示例
1. 接口防重提交(幂等性控制)
• 定义注解:标记需要幂等控制的接口。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Idempotent {
String key() default ""; // 支持动态生成幂等键
int expire() default 5; // 默认锁定时长5秒
}
• 切面逻辑:统一处理幂等键的生成与校验。
@Aspect
public class IdempotentAspect {
@Around("@annotation(idempotent)")
public Object checkIdempotent(ProceedingJoinPoint joinPoint, Idempotent idempotent) throws Throwable {
String key = generateKey(joinPoint, idempotent); // 动态生成幂等键
if (redis.setIfAbsent(key, "1", idempotent.expire(), TimeUnit.SECONDS)) {
return joinPoint.proceed(); // 执行业务逻辑
} else {
throw new BusinessException("请勿重复请求!");
}
}
}
• 业务代码:只需添加注解,无需编写防重逻辑。
@Idempotent(key = "#order.userId + '-' + #order.productId") // 动态生成键
public void createOrder(Order order) {
// 业务逻辑(无需关心防重)
}
2. 接口权限校验(动态鉴权)
• 定义注解:标记需要鉴权的接口。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RequirePermission {
String value(); // 权限标识(如 "order:create")
}
• 切面逻辑:统一从上下文获取用户权限并校验。
@Aspect
public class PermissionAspect {
@Before("@annotation(RequirePermission)")
public void checkPermission(JoinPoint joinPoint) {
RequirePermission annotation = getAnnotation(joinPoint);
if (!currentUser.hasPermission(annotation.value())) {
throw new BusinessException("无权限操作!");
}
}
}
• 业务代码:仅用注解声明所需权限。
@RequirePermission("order:create")
public void createOrder(Order order) {
// 业务逻辑(无需关心鉴权)
}
3. 热点数据缓存(自动缓存)
• 定义注解:标记需要缓存的方法。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface CacheResult {
String key(); // 缓存键(支持SpEL表达式)
int expire() default 3600; // 缓存时间(秒)
}
• 切面逻辑:统一处理缓存的读取与写入。
@Aspect
public class CacheAspect {
@Around("@annotation(cacheResult)")
public Object cacheResult(ProceedingJoinPoint joinPoint, CacheResult cacheResult) throws Throwable {
String key = parseKey(joinPoint, cacheResult); // 解析SpEL表达式生成键
Object value = redis.get(key);
if (value != null) return value;
value = joinPoint.proceed(); // 执行业务逻辑
redis.set(key, value, cacheResult.expire(), TimeUnit.SECONDS);
return value;
}
}
• 业务代码:只需关注数据查询逻辑。
@CacheResult(key = "'product:' + #productId") // 自动缓存结果
public Product getProductDetail(String productId) {
return productDao.findById(productId);
}
4. 接口限流(动态阈值)
• 定义注解:标记需要限流的接口。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RateLimit {
int permitsPerSecond() default 100; // 每秒允许请求数
}
• 切面逻辑:统一接入限流算法(如令牌桶)。
@Aspect
public class RateLimitAspect {
private final Map<String, RateLimiter> limiters = new ConcurrentHashMap<>();
@Around("@annotation(rateLimit)")
public Object limit(ProceedingJoinPoint joinPoint, RateLimit rateLimit) throws Throwable {
String key = generateKey(joinPoint); // 按方法名生成限流键
RateLimiter limiter = limiters.computeIfAbsent(key,
k -> RateLimiter.create(rateLimit.permitsPerSecond()));
if (limiter.tryAcquire()) {
return joinPoint.proceed();
} else {
throw new BusinessException("请求过于频繁!");
}
}
}
• 业务代码:仅用注解声明限流阈值。
@RateLimit(permitsPerSecond = 50) // 每秒最多50次请求
public void submitOrder(Order order) {
// 业务逻辑(无需关心限流)
}
三、AOP 的核心优势
- 解耦性:业务代码不耦合横切逻辑(如防重、鉴权)。
- 复用性:同一套切面逻辑可被多个注解复用。
- 动态性:通过 SpEL 表达式实现注解参数的动态计算。
- 可维护性:修改横切逻辑时只需调整切面,无需改动业务代码。
四、对比直接在业务代码中实现
场景 | AOP + 注解 | 业务代码硬编码 |
---|---|---|
防重提交 | 通过 @Idempotent 注解自动处理 | 每个方法中编写 Redis 操作与异常处理 |
权限校验 | 通过 @RequirePermission 注解自动拦截 | 在每个方法开头调用权限服务 |
缓存逻辑 | 通过 @CacheResult 注解自动缓存 | 手动查询缓存、处理未命中与回填逻辑 |
限流控制 | 通过 @RateLimit 注解自动限流 | 在每个方法中集成限流算法与异常处理 |
五、总结
通过 自定义注解 + 动态切面,AOP 在电商项目中能实现:
• 快速接入通用能力:新接口只需添加注解即可获得防重、鉴权等功能。
• 统一逻辑管理:所有缓存、限流逻辑集中在切面中,避免代码分散。
• 灵活调整策略:修改限流阈值或缓存时间只需调整注解参数,无需修改业务代码。
这种设计模式特别适合中大型项目,能显著提升开发效率与系统可维护性。