一、基于AOP实现的自定义注解
eg:分布式锁
定义注解属性:
/**
* 分布式锁注解
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DistributeLock {
/**
* 锁的场景
*/
public String scene();
/**
* 锁的key
*/
public String key() default "NONE";
/**
* SPEL表达式:
*/
public String keyExpression() default "NONE";
/**
* 超时时间,毫秒
* 用Redisson 不设置ttl有看门狗机制 为-1 则调rLock.lock();
*/
public int expireTime() default -1;
/**
* 加锁等待时长,毫秒
*/
public int waitTime() default Integer.MAX_VALUE;
}
SPEL表达式:
SpEL (Spring Expression Language) 是 Spring 框架内置的表达式语言。
好处:
根据方法参数实时生成锁 key,不同订单/用户互不阻塞
支持 #参数名、属性访问、方法调用、三元表达式等
业务方法上只写注解,不侵入代码
Spring 原生,无需引入额外依赖,Spring 自带
这里的执行流程:
用户调用 → AOP拦截 → 读取注解的 keyExpression → 解析SpEL → 绑定参数 → 生成动态key → 加锁
触发:
环绕通知
/**
@annotation()里跟的是注解的全限定类名(包名 + 类名),用来指定切入点
如果注解和切面在同一个包下,也可以省略包名直接写类名,但写全路径更清晰
*/
@Around("@annotation(xxxxxxxx)")
public Object around(ProceedingJoinPoint pjp, DistributeLock annotation) {
//Spring 自动把方法上的 @DistributeLock 实例传给 annotation
// ...
}
@Around("@annotation(xxxxxxxx)")
public Object around(ProceedingJoinPoint pjp) throws Exception {
// 手动通过反射获取注解
Method method = ((MethodSignature) pjp.getSignature()).getMethod();
DistributeLock distributeLock = method.getAnnotation(DistributeLock.class);
// ...
}
| 注解 | 说明 | 执行时机 |
| @Before | 前置通知 | 目标方法执行前 |
| @After | 后置通知 | 目标方法执行后(无论是否异常) |
| @AfterReturning | 返回通知 | 目标方法正常返回后 |
| @AfterThrowing | 异常通知 | 目标方法抛出异常后 |
| @Around | 环绕通知 | 包裹整个目标方法,最灵活 |
二、基于拦截器实现自定义注解
eg:安全点击
定义注解属性
/**
* 安全点击时间拦截自定义注解
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
public @interface SafeClick {
/***
* 事件KEY
*/
ClickEvent event();
/***
* 默认500ml内只能允许一次点击操作,单位:毫秒
*/
long time() default 500L;
/***
* 异常文案提示
*/
String message() default "点击太频繁~~~~";
}
@AllArgsConstructor
@Getter
public enum ClickEvent {
RESOURCE_SUPPORT("点赞"),
COMMENT("评论"),
SUBMIT("提交"),
REGISTRY("注册"),
CREATE_GROUP("创建"),
;
private String actionName;
}
触发
/**
* 异常点击事件拦截
*/
@Slf4j
public class SafeClickInterceptor extends HandlerInterceptorAdapter {
@Autowired
private RedisService redisService;
/**
* 在调用方法之前执行拦截
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
SafeClick safeClick = method.getAnnotation(SafeClick.class);
if (safeClick == null) {
return true;
}
...
}
}
使用:
@SafeClick(event = ClickEvent.SUBMIT, time = 2000, message = "2s内只能提交一次~~")
@PostMapping("/submit")
public APIResponseBean submit(@RequestBody MomentSaveOrUpdateVO vo) {
.......
}
三、区别
| 维度 | AOP | 拦截器 |
| 代理机制 | 动态代理(JDK/CGLIB) | Servlet容器 (DispatcherServlet) |
| 作用范围 | 任意Bean 方法 | 仅Controller请求方法 |
| 切面粒度 | 方法级别 | HTTP请求级别 |
| 实现注解 | @Around("@annotation(xxx)") | preHandle()里反射读取注解 |
| 场景 | 选择 |
| Controller层:登录校验、权限检查、参数校验 | 拦截器 |
| Service层:分布式锁、事务、志、缓存 | AOP |
| 需要访问HttpServletRequest/Response | 拦截器 |
| 需要精确控制到某个方法 | AOP |
| 全局统一处理(所有请求) | 拦截器或AOP均可 |
&spm=1001.2101.3001.5002&articleId=161559548&d=1&t=3&u=301805674fcf4e2593b63a64c75cd728)
3766

被折叠的 条评论
为什么被折叠?



