版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhou920786312/article/details/89187080
利用分布式共享锁实现防止方法重复调用
版本记录
| version:
| date: 20190410
| description:
| Author: 周飞
功能介绍
防止一个方法,在方法参数值相同的情况下,短时间频繁调用
流程
代码
package com.lolaage.common.annotations;
import java.lang.annotation.*;
/**
* @Author feizhou
* @Description 防止同一个方法被频繁执行(是否需要频繁执行看参数params是否不一样)
* @Date 19:35 2019/4/9
* @Param
* @return
**/
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SameMethodFrequentlyRun {
/**
* @Author feizhou
* @Description 当方法的参数是实体对象,对象必须对象重写equal和hashcode方法
**/
String params() default "";
String description() default "";
/**
* @Author feizhou
* @Description
**/
long milliseconds() default 30000L;
}
package com.lolaage.common.aop;
import com.lolaage.base.po.JsonModel;
import com.lolaage.common.annotations.SameMethodFrequentlyRun;
import com.lolaage.helper.util.RedisLockTemplate;
import com.lolaage.util.StringUtil;
import org.apache.log4j.Logger;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
/**
* @Author feizhou
* @Description 防止同一个方法被频繁执行AOP(是否需要频繁执行看参数params是否不一样)
**/
@Aspect
@Component
public class SameMethodFrequentlyRunAop {
private static Logger logger = Logger.getLogger(SameMethodFrequentlyRunAop.class);
// 配置接入点,即为所要记录的action操作目录
@Pointcut("execution(* com.lolaage.helper.web.controller..*.*(..))")
private void controllerAspect() {
}
@Around("controllerAspect()")
public Object around(ProceedingJoinPoint pjp) {
Object returnObj=null;
StringBuilder sb=new StringBuilder();
// 拦截的实体类,就是当前正在执行的controller
Object target = pjp.getTarget();
//获取全类名
String className=target.getClass().getName();
// 拦截的方法名称。当前正在执行的方法
String methodName = pjp.getSignature().getName();
// 拦截的方法参数
Object[] args = pjp.getArgs();
// 拦截的放参数类型
Signature sig = pjp.getSignature();
MethodSignature msig = (MethodSignature) sig ;
Class[] parameterTypes = msig.getMethod().getParameterTypes();
sb.append(className);
for (Object o : args) {
if(o==null){
continue;
}
int i = o.hashCode();
sb.append(":");
sb.append(i);
}
// 获得被拦截的方法
Method method = null;
try {
method = target.getClass().getMethod(methodName, parameterTypes);
SameMethodFrequentlyRun sameMethodFrequentlyRun = method.getAnnotation(SameMethodFrequentlyRun.class);
if (sameMethodFrequentlyRun != null) {
String description = sameMethodFrequentlyRun.description();
String params = sameMethodFrequentlyRun.params();
if(StringUtil.isEmpty(params)){
params=sb.toString();
}
long milliseconds = sameMethodFrequentlyRun.milliseconds();
Boolean isGetLock = RedisLockTemplate.distributedLock_v2(params, description, milliseconds, false);
if(!isGetLock){
//提示不要重复操作
JsonModel result = new JsonModel();
return result.setErrCode(5004);
}
}
} catch (NoSuchMethodException e) {
logger.error("分布式防重复操作异常:AOP只会拦截public方法,非public会报异常,如果你要将你的方法加入到aop拦截中,请修改方法的修饰符:"+e.getMessage());
}
try {
returnObj = pjp.proceed();
} catch (Throwable e) {
logger.error("分布式防重复操作异常Throwable:"+e.getMessage());
e.printStackTrace();
}
return returnObj;
}
}
/**
* 分布式锁压力测试,和防重复测试
* @return
*/
@SameMethodFrequentlyRun(description="查询操作日志",milliseconds = 10000L)
@RequestMapping("/pressureLock")
public void pressureLock(String key,QuitParam quitParam) {
System.out.println(this.hashCode()+"---"+Thread.currentThread().getName()+":测试开始");
System.out.println(this.hashCode()+"---"+Thread.currentThread().getName()+"测试结束");
}
方案优点
- 解决表单防重复提交
- 将防方法重复执行的功能和业务逻辑区分开发
- 可以解决计划任务中方法重复执行的问题。
方案缺点
对实现方案的缺点进行说明,以便后续改进
待改善点
对待完善点进行列举,以便后续改进