SpringBoot--防止重复提交(锁机制---本地锁、分布式锁)
2020-12-13 20:21
标签:proc 方法参数 temp methods 否则 img tac declare redislock 防止重复提交,主要是使用锁的形式来处理,如果是单机部署,可以使用本地缓存锁(Guava)即可,如果是分布式部署,则需要使用分布式锁(可以使用zk分布式锁或者redis分布式锁),本文的分布式锁以redis分布式锁为例。 一、本地锁(Guava) 1、导入依赖 2、自定义本地锁注解 3、本地锁注解实现 4、控制层 5、测试 第一次请求: 未过期再次访问: 二、Redis分布式锁 1、导入依赖 导入aop依赖和redis依赖即可 2、配置 配置redis连接信息即可 3、自定义分布式锁注解 4、自定义key规则注解 由于redis的key可能是多层级结构,例如 redistest:demo1:token:kkk这种形式,因此需要自定义key的规则。 5、定义key生成策略接口 6、定义key生成策略实现类 7、分布式注解实现 8、主函数调整 主函数引入key生成策略 9、Controller 10、测试 (1)由于cacheLock方法的CacheLock注解没有加prefix前缀,因此会报错 (2)没有加CacheParam注解 第一次调用: 缓存信息: 可以发现key为prifix的值 第二次调用: (3)增加了CacheParam注解 第一次调用: 缓存信息: 可以发现缓存的内容为prefix+@CacheParam 第二次调用: SpringBoot--防止重复提交(锁机制---本地锁、分布式锁) 标签:proc 方法参数 temp methods 否则 img tac declare redislock 原文地址:https://www.cnblogs.com/liconglong/p/11728136.html
package com.example.demo.utils;
import java.lang.annotation.*;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface LocalLock {
String key() default "";
//过期时间,使用本地缓存可以忽略,如果使用redis做缓存就需要
int expire() default 5;
}
package com.example.demo.utils;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
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.reflect.MethodSignature;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;
@Aspect
@Configuration
public class LockMethodInterceptor {
//定义缓存,设置最大缓存数及过期日期
private static final Cache
@ResponseBody
@PostMapping(value ="/localLock")
@ApiOperation(value="重复提交验证测试--使用本地缓存锁")
@ApiImplicitParams( {@ApiImplicitParam(paramType="query", name = "token", value = "token", dataType = "String")})
@LocalLock(key = "localLock:test:arg[0]")
public String localLock(String token){
return "sucess====="+token;
}
package com.example.demo.utils;
import java.lang.annotation.*;
import java.util.concurrent.TimeUnit;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface CacheLock {
//redis锁前缀
String prefix() default "";
//redis锁过期时间
int expire() default 5;
//redis锁过期时间单位
TimeUnit timeUnit() default TimeUnit.SECONDS;
//redis key分隔符
String delimiter() default ":";
}
package com.example.demo.utils;
import java.lang.annotation.*;
@Target({ElementType.METHOD,ElementType.PARAMETER,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface CacheParam {
String name() default "";
}
package com.example.demo.service;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.stereotype.Service;
public interface CacheKeyGenerator {
//获取AOP参数,生成指定缓存Key
String getLockKey(ProceedingJoinPoint joinPoint);
}
package com.example.demo.service.impl;
import com.example.demo.service.CacheKeyGenerator;
import com.example.demo.utils.CacheLock;
import com.example.demo.utils.CacheParam;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
public class CacheKeyGeneratorImp implements CacheKeyGenerator {
@Override
public String getLockKey(ProceedingJoinPoint joinPoint) {
//获取连接点的方法签名对象
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
//Method对象
Method method = methodSignature.getMethod();
//获取Method对象上的注解对象
CacheLock cacheLock = method.getAnnotation(CacheLock.class);
//获取方法参数
final Object[] args = joinPoint.getArgs();
//获取Method对象上所有的注解
final Parameter[] parameters = method.getParameters();
StringBuilder sb = new StringBuilder();
for(int i=0;i
package com.example.demo.utils;
import com.example.demo.service.CacheKeyGenerator;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisStringCommands;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.types.Expiration;
import org.springframework.util.StringUtils;
import java.lang.reflect.Method;
@Aspect
@Configuration
public class CacheLockMethodInterceptor {
@Autowired
public CacheLockMethodInterceptor(StringRedisTemplate stringRedisTemplate, CacheKeyGenerator cacheKeyGenerator){
this.cacheKeyGenerator = cacheKeyGenerator;
this.stringRedisTemplate = stringRedisTemplate;
}
private final StringRedisTemplate stringRedisTemplate;
private final CacheKeyGenerator cacheKeyGenerator;
@Around("execution(public * * (..)) && @annotation(com.example.demo.utils.CacheLock)")
public Object interceptor(ProceedingJoinPoint joinPoint){
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Method method = methodSignature.getMethod();
CacheLock cacheLock = method.getAnnotation(CacheLock.class);
if(StringUtils.isEmpty(cacheLock.prefix())){
throw new RuntimeException("前缀不能为空");
}
//获取自定义key
final String lockkey = cacheKeyGenerator.getLockKey(joinPoint);
final Boolean success = stringRedisTemplate.execute(
(RedisCallback
@Bean
public CacheKeyGenerator cacheKeyGenerator(){
return new CacheKeyGeneratorImp();
}
@ResponseBody
@PostMapping(value ="/cacheLock")
@ApiOperation(value="重复提交验证测试--使用redis锁")
@ApiImplicitParams( {@ApiImplicitParam(paramType="query", name = "token", value = "token", dataType = "String")})
//@CacheLock
@CacheLock()
public String cacheLock(String token){
return "sucess====="+token;
}
@ResponseBody
@PostMapping(value ="/cacheLock1")
@ApiOperation(value="重复提交验证测试--使用redis锁")
@ApiImplicitParams( {@ApiImplicitParam(paramType="query", name = "token", value = "token", dataType = "String")})
//@CacheLock
@CacheLock(prefix = "redisLock.test",expire = 20)
public String cacheLock1(String token){
return "sucess====="+token;
}
@ResponseBody
@PostMapping(value ="/cacheLock2")
@ApiOperation(value="重复提交验证测试--使用redis锁")
@ApiImplicitParams( {@ApiImplicitParam(paramType="query", name = "token", value = "token", dataType = "String")})
//@CacheLock
@CacheLock(prefix = "redisLock.test",expire = 20)
public String cacheLock2(@CacheParam(name = "token") String token){
return "sucess====="+token;
}
文章标题:SpringBoot--防止重复提交(锁机制---本地锁、分布式锁)
文章链接:http://soscw.com/index.php/essay/36734.html