【Redis使用系列】使用Redis做防止重复提交

前言

在平时的开发中我们都需要处理重复提交的问题,避免业务出错或者产生脏数据,虽然可以通过前端控制但这并不是可以完全避免,最好的方式还是前后端均进行控制,这样的话就可以更有效,尽可能全面的去减少错误的发生。

一、比如我们注册的时候需要发送验证码

如果用户频繁点击或者恶意攻击的话就会造成不断的请求对服务器产生很大的压力,为了避免这种情况我们需要做处理,传统的模式中是在数据库中记录手机号、验证码已经发送时间,再次请求的时候呢去数据库查询是否有该手机号记录,并校验是否超过间隔时间,如果超过则重新发送并更新时间,否组不予发送,这样有一个缺点就是如果同时又很多人在做相同的业务同时查询就会对数据库造成很大的压力。

根据此种情况我们可以使用Redis incrde 原子性递增,来解决这种高并发的秒杀或者分布式序列号生成等场景。鉴于本场景我们只用他来做计数实现间隔时间内只接收一次请求。

实现逻辑:在发送短信之后使用Redis的incr设置一个递增的KEY(根据自己的需要设定但是要保证每一个人的唯一),来判断该KEY的数值,如果等于1说明这是第一次请求,发送短信记录日志,并设置有效期,如果不等于的话说明是间隔时间内多次请求,就提示请求频繁,稍后重试。

 1 String redisKey = "SMS_SEND_" + smsPhone;
 2 long count = redisTemplate.opsForValue().increment(redisKey, 1);
 3 if (count == 1) {
 4 //设置有效期一分钟
 5   redisTemplate.expire(redisKey, 60, TimeUnit.SECONDS);
 6 }
 7 if (count > 1) {
 8    resultMap.put("retCode", "-1");     
 9    resultMap.put("retMsg", "每分钟只能发送一次短信");
10    outPrintJson(resultMap);
11    return;
12 }
13 /** 发送短信 */
14 ......
15 /** 记录发送日志 */
16 ......

二、上述方式可以解决特定的问题,当需要处理的情况多的话我们可以考虑使用切面来解决

代码如下:

 1 package com.slp.annotation;
 2 
 3 public class RedisLockBean {
 4     private String key;
 5     private int timeInSecond;
 6     private String codeName;
 7     private String msgName;
 8     private String code;
 9     private String msg;
10     private boolean isAtController;
11     private boolean isAtService;
12     private boolean isAtParameter;
13     private String returnType;
14     public String getKey() {
15         return key;
16     }
17     public void setKey(String key) {
18         this.key = key;
19     }
20     public int getTimeInSecond() {
21         return timeInSecond;
22     }
23     public void setTimeInSecond(int timeInSecond) {
24         this.timeInSecond = timeInSecond;
25     }
26     public String getCodeName() {
27         return codeName;
28     }
29     public void setCodeName(String codeName) {
30         this.codeName = codeName;
31     }
32     public String getMsgName() {
33         return msgName;
34     }
35     public void setMsgName(String msgName) {
36         this.msgName = msgName;
37     }
38     public String getCode() {
39         return code;
40     }
41     public void setCode(String code) {
42         this.code = code;
43     }
44     public String getMsg() {
45         return msg;
46     }
47     public void setMsg(String msg) {
48         this.msg = msg;
49     }
50     
51     
52     public boolean isAtController() {
53         return isAtController;
54     }
55     public void setAtController(boolean isAtController) {
56         this.isAtController = isAtController;
57     }
58     public boolean isAtService() {
59         return isAtService;
60     }
61     public void setAtService(boolean isAtService) {
62         this.isAtService = isAtService;
63     }
64     public boolean isAtParameter() {
65         return isAtParameter;
66     }
67     public void setAtParameter(boolean isAtParameter) {
68         this.isAtParameter = isAtParameter;
69     }
70     public String getReturnType() {
71         return returnType;
72     }
73     public void setReturnType(String returnType) {
74         this.returnType = returnType;
75     }
76     @Override
77     public String toString() {
78         return "RedisLockBean [key=" + key + ", timeInSecond=" + timeInSecond
79                 + ", codeName=" + codeName + ", msgName=" + msgName + ", code="
80                 + code + ", msg=" + msg + ", isAtController=" + isAtController
81                 + ", isAtService=" + isAtService + ", isAtParameter="
82                 + isAtParameter + ", returnType=" + returnType + "]";
83     }
84 }
 1 package com.slp.annotation;
 2 
 3 import java.lang.annotation.ElementType;
 4 import java.lang.annotation.Retention;
 5 import java.lang.annotation.RetentionPolicy;
 6 import java.lang.annotation.Target;
 7 /**
 8  * 解决的问题:<br>
 9  *         1.数据库加锁性能较差<br>
10  *          2.数据库加锁,若相应线程异常,所无法释放<br>
11  * 注意事项:<br>
12  *           方法的返回值对象必须包含错误码,错误信息属性及其的get方法
13  *
14  */
15 @Target({ElementType.PARAMETER,ElementType.METHOD})
16 @Retention(RetentionPolicy.RUNTIME)
17 public @interface RedisLock {
18     /**
19      * 若加注解的入参时基本数据类型(int,long)或String时,fieldName无效<br>
20      * 若注解的参数是自定义对象时,请注意一下几点:<br>
21      *         1.确保定义有相应属性public修饰的get方法<br>
22      *         2.get方法的返回参数是基本的数据类型或String<br>
23      *         3.get方法的返回值不为空<br>
24      * 否则,加锁失败.
25      * @return
26      */
27     String[] fieldName() default {};
28     /**
29      * 锁的有效时间,单位为秒,默认值为1
30      * @return
31      */
32     int timeInSecond() default 1;
33     /**
34      * 加锁,锁已被其它请求获取时,直接返回重复提交,codeName指定返回对象的返回码对应的属性,默认值'code'
35      * @return
36      */
37     String codeName() default "code";
38     /**
39      * 加锁,锁已被其它请求获取时,直接返回重复提交,msgName指定返回对象的返回信息对应的属性,默认值'msg'
40      * @return
41      */
42     String msgName() default "msg";
43     /**
44      * 加锁,锁已被其它请求获取时,直接返回重复提交,code指定返回对象的返回码对应的值,默认值'09'
45      * @return
46      */
47     String code() default "09";
48     /**
49      * 加锁,锁已被其它请求获取时,直接返回重复提交,msg指定返回对象的返回码对应的值,默认值'重复提交'
50      * @return
51      */
52     String msg()  default "重复提交";
53     /**
54      * 注解作用与方法时,指定参数在参数列表中的索引
55      */
56     int paramIndex() default 0;
57 }
  1 package com.slp.annotation;
  2 
  3 import java.lang.annotation.Annotation;
  4 import java.lang.reflect.InvocationTargetException;
  5 import java.lang.reflect.Method;
  6 import java.util.ArrayList;
  7 import java.util.Arrays;
  8 import java.util.Collections;
  9 import java.util.HashMap;
 10 import java.util.List;
 11 import java.util.Map;
 12 
 13 import javax.servlet.http.HttpServletRequest;
 14 
 15 import org.apache.commons.lang.StringUtils;
 16 import org.aspectj.lang.ProceedingJoinPoint;
 17 import org.aspectj.lang.Signature;
 18 import org.aspectj.lang.reflect.MethodSignature;
 19 import org.slf4j.Logger;
 20 import org.slf4j.LoggerFactory;
 21 import org.springframework.beans.factory.annotation.Autowired;
 22 import org.springframework.stereotype.Component;
 23 import org.springframework.stereotype.Controller;
 24 import org.springframework.web.bind.annotation.RequestMapping;
 25 
 26 import com.alibaba.fastjson.JSONObject;
 27 import com.cul.culsite.common.RedisKeyConstants;
 28 import com.cul.culsite.service.RedisService;
 29 import com.cul.culsite.util.DateUtil;
 30 import com.cul.culsite.util.OxmHelper;
 31 
 32 @Component
 33 public class RedisLockAspect {
 34     private final static Logger logger = LoggerFactory.getLogger(RedisLockAspect.class);
 35     
 36     protected static final String XML_TYPE = "xml";
 37     protected static final String JSON_TYPE = "json";
 38     protected static final String ILLEGAL_TYPE = "illegal type";
 39     @Autowired
 40     private RedisService redisService;
 41     
 42     public Object redisLockParse(ProceedingJoinPoint p) throws Throwable{
 43         Signature signature = p.getSignature();
 44         boolean isRepetition = false;
 45         RedisLockBean redisLockBean = null;
 46         String value = System.nanoTime()+"";
 47         if(signature instanceof MethodSignature){
 48             //获得接口中定义的方法的Method,但注解时加载实现类中方法的参数上
 49             MethodSignature methodSignature = (MethodSignature)signature;
 50             Method serviceMethod = methodSignature.getMethod();
 51             
 52             try {
 53                 Method serviceImpMethod = p.getTarget().getClass().getMethod(serviceMethod.getName(), serviceMethod.getParameterTypes());
 54                 //获取key值
 55                 redisLockBean = getRedisLockKey(p.getTarget(),serviceImpMethod,p.getArgs());
 56                 //成功获取key值,在redis中加锁
 57                 if(redisLockBean!=null){
 58                     logger.info("redis lock value is :{}",value);
 59                     boolean isPutSuccess =redisService.setIfAbsent(redisLockBean.getKey(), value, redisLockBean.getTimeInSecond());
 60                     //加锁失败,直接返回
 61                     if(!isPutSuccess){
 62                         logger.info("get redis lock fail for {}",redisLockBean.getKey());
 63                         if(redisLockBean.isAtParameter()||redisLockBean.isAtService()){
 64                             Class<?> returnType = serviceImpMethod.getReturnType();
 65                             //加锁方法有返回值
 66                             if(!returnType.getName().equals(java.lang.Void.class.getName())){
 67                                 //实例化返回值对象
 68                                 try {
 69                                     Object result = returnType.newInstance();
 70                                     //设置返回码
 71                                     returnType.getMethod(getSetMethodNameByFieldName(redisLockBean.getCodeName()), java.lang.String.class).invoke(result, redisLockBean.getCode());
 72                                     //设置返回信息
 73                                     returnType.getMethod(getSetMethodNameByFieldName(redisLockBean.getMsgName()), java.lang.String.class).invoke(result, redisLockBean.getMsg());
 74                                     return result;
 75                                 } catch (InstantiationException e) {
 76                                     e.printStackTrace();
 77                                 } catch (IllegalAccessException e) {
 78                                     e.printStackTrace();
 79                                 } catch (IllegalArgumentException e) {
 80                                     e.printStackTrace();
 81                                 } catch (InvocationTargetException e) {
 82                                     e.printStackTrace();
 83                                 }
 84                             }else{
 85                                 throw new RuntimeException("@RedisLock作用的方法没有返回参数");
 86                             }
 87                         }else if(redisLockBean.isAtController()){
 88                             Map<String,String> result = new HashMap<String,String>();
 89                             result.put(redisLockBean.getCodeName(), redisLockBean.getCode());
 90                             result.put(redisLockBean.getMsgName(), redisLockBean.getMsg());
 91                             return response(redisLockBean.getReturnType()==null?"json":redisLockBean.getReturnType(), result);
 92                         }
 93                     }else{
 94                         logger.info("get redis lock success for {}",redisLockBean.getKey());
 95                         isRepetition = true;
 96                     }
 97                 }
 98             } catch (NoSuchMethodException e) {
 99                 e.printStackTrace();
100             } catch (SecurityException e) {
101                 e.printStackTrace();
102             }
103         }
104         Object result = null;
105         try {
106             result = p.proceed();
107         } catch (Throwable e) {
108             throw e;
109         }finally{
110             if(redisLockBean!=null){
111                 if(isRepetition&&value.equals(redisService.get(redisLockBean.getKey()))){
112                     logger.info("lock has released :{}",redisLockBean.getKey());
113                     redisService.delete(redisLockBean.getKey());
114                 }
115                 
116             }
117         }
118         return result;
119     }
120     
121     private RedisLockBean getRedisLockKey(Object target,Method method,Object... object){
122         if(target == null){
123             throw new RuntimeException("get redis lock key error,target is null");
124         }
125         if(method==null){
126             throw new RuntimeException("get redis lock key error,method is null");
127         }
128         List<String> fieldValueList = new ArrayList<String>();
129         RedisLockBean redisLockBean = new RedisLockBean();
130         RedisLock redisLock = null;
131         //类上有@Controller说明@RedisLock是放在请求方法上,使用HttpServletRequest获取请求参数
132         if(method.isAnnotationPresent(RedisLock.class)&&target.getClass().isAnnotationPresent(Controller.class)){
133             //controller层方法时对外开放的接口
134             if(method.isAnnotationPresent(RequestMapping.class)){
135                 redisLock = method.getAnnotation(RedisLock.class);
136                 //获取方法中的HttpServletRequest类型的参数
137                 HttpServletRequest request = null;
138                 for(Object para:object){
139                     if(para instanceof HttpServletRequest){
140                         request = (HttpServletRequest)para;
141                         break;
142                     }
143                 }
144                 if(request==null){
145                     throw new RuntimeException("@RedisLock作用于controller层方法时,方法需要包含HttpServletRequest类型的参数");
146                 }
147                 //未定义加锁参数时,默认使用mac
148                 String[] paraName = redisLock.fieldName();
149                 if(paraName==null||paraName.length==0){
150                     paraName=new String[]{"mac"};
151                 }
152                 for(String para:paraName){
153                     fieldValueList.add(request.getParameter(para));
154                 }
155                 if(fieldValueList.isEmpty()){
156                     throw new RuntimeException("@RedisLock作用于controller层方法时,生成key失败,请求中没有mac签名");
157                 }
158                 //标示注解作用在controller成方法上
159                 redisLockBean.setAtController(true);
160             }else{
161                 throw new RuntimeException("@RedisLock作用于controller层的方法时,该方法上需要使用@RequestMapping注解");
162             }
163             //注解作用于非controller层方法上
164         }else if(method.isAnnotationPresent(RedisLock.class)){
165             redisLock = method.getAnnotation(RedisLock.class);
166             //参数的索引位置
167             int index = redisLock.paramIndex();
168             String[] fieldName = redisLock.fieldName();
169             String[] values = getFieldValue(object[index],fieldName);
170             //注解的参数时基本的数据类型或String,不需要传入属性名称,否则设置的属性,都必须获得该属性值
171             if(values==null || values.length!=fieldName.length && fieldName.length>0){
172                 return null;
173             }
174             fieldValueList.addAll(Arrays.asList(values));
175             redisLockBean.setAtService(true);
176         }else{
177             Annotation[][] annotations;
178             annotations = method.getParameterAnnotations();
179             for(int i=0;i<annotations.length;i++){
180                 for(Annotation annotation:annotations[i]){
181                     if(annotation instanceof RedisLock){
182                         RedisLock redisLockTmp = (RedisLock)annotation;
183                         if(redisLock==null){
184                             redisLock = redisLockTmp;
185                         }
186                         String[] fieldName = redisLockTmp.fieldName();
187                         String[] values = getFieldValue(object[i],fieldName);
188                         //注解的参数时基本的数据类型或String,不需要传入属性名称,否则设置的属性,都必须获得该属性值
189                         if(values==null || values.length!=fieldName.length && fieldName.length>0){
190                             return null;
191                         }
192                         fieldValueList.addAll(Arrays.asList(values));
193                         redisLockBean.setAtParameter(true);
194                     }
195                 }
196             }            
197         }
198         //未使用注解
199         if(fieldValueList.isEmpty()){
200             return null;
201         }
202         
203         //设置其它参数值
204         if(redisLockBean.getTimeInSecond()==0){
205             redisLockBean.setTimeInSecond(redisLock.timeInSecond());
206         }
207         if(StringUtils.isEmpty(redisLockBean.getCodeName())){
208             redisLockBean.setCodeName(redisLock.codeName());
209         }
210         if(StringUtils.isEmpty(redisLockBean.getCode())){
211             redisLockBean.setCode(redisLock.code());
212         }
213         if(StringUtils.isEmpty(redisLockBean.getMsgName())){
214             redisLockBean.setMsgName(redisLock.msgName());
215         }
216         if(StringUtils.isEmpty(redisLockBean.getMsg())){
217             redisLockBean.setMsg(redisLock.msg());
218         }
219         
220         Collections.sort(fieldValueList);
221         logger.info("all value of fieldName is {}",fieldValueList);
222         //生成key值
223         StringBuilder builder = new StringBuilder();
224         builder.append(target.getClass().getName())
225         .append("-")
226         .append(method.getName())
227         .append("-")
228         .append(Arrays.asList(method.getParameterTypes()))
229         .append("-")
230         .append(fieldValueList);
231         String lockKey = RedisKeyConstants.REDIS_LOCK + builder.toString();
232         logger.info("redis lock key is :{}",builder.toString());
233         redisLockBean.setKey(lockKey);
234         logger.info("redisLockBean :{}",redisLockBean.toString());
235         return redisLockBean;
236     }
237     private String[] getFieldValue(Object argObj,String...fieldName){
238         if(fieldName ==null || fieldName.length == 0){
239             return new String[]{getBaseClassValue(argObj)};
240         }
241         List<String> fieldsValue = new ArrayList<String>();
242         for(String field:fieldName){
243             String value = getFieldValue(argObj,field);
244             logger.info("value of fieldName '{}' is :{}",fieldName,value);
245             if(value!=null){
246                 fieldsValue.add(value);
247             }
248         }
249         return fieldsValue.toArray(new String[0]);
250     }
251     private String getFieldValue(Object argObj,String fieldName){
252         if(argObj==null){
253             throw new RuntimeException("argObj is null,cannot get field value of fieldName");
254         }
255         String value = getBaseClassValue(argObj);
256         if(!StringUtils.isEmpty(value)){
257             return value;
258         }
259         String methodName = getGetMethodValueByFieldName(fieldName);
260         Object result = null;
261         try {
262             Method method = argObj.getClass().getMethod(methodName);
263             result = method.invoke(argObj);
264         } catch (NoSuchMethodException e) {
265             logger.error("method {} without parameter is not exists!",methodName);
266             e.printStackTrace();
267         } catch (SecurityException e) {
268             e.printStackTrace();
269         } catch (IllegalAccessException e) {
270             logger.error("method {} without parameter is not public!",methodName);
271             e.printStackTrace();
272         } catch (IllegalArgumentException e) {
273             logger.error("method {} has parameter!",methodName);
274             e.printStackTrace();
275         } catch (InvocationTargetException e) {
276             e.printStackTrace();
277         }
278         if(result==null){
279             logger.warn("method {} does not have returnValue",methodName);
280             return null;
281         }
282         return getBaseClassValue(result);
283     }
284     private String getBaseClassValue(Object object){
285         if(object==null){
286             throw new RuntimeException("argObj is null,cannot get field value ");
287         }
288         if(object instanceof String){
289             return object.toString();
290         }
291         if(object instanceof Integer){
292             int i = (Integer)object;
293             //剔除成员变量的默认值
294             if(i!=0){
295                 return i+"";
296             }
297         }
298         if(object instanceof Long){
299             long i = (Long)object;
300             if(i!=0){
301                 return i+"";
302             }
303         }
304         return null;
305     }
306 
307     private String getGetMethodValueByFieldName(String fieldName){
308         return getMethodNameByFieldNameAndPrefix("get",fieldName);
309     }
310     private String getSetMethodNameByFieldName(String fieldName){
311         return getMethodNameByFieldNameAndPrefix("set",fieldName);
312     }
313     private String getMethodNameByFieldNameAndPrefix(String prefix,String fieldName){
314         if(StringUtils.isEmpty(fieldName)){
315             throw new RuntimeException("cannot get Get method by null or length is 0");
316         }
317         if(StringUtils.isEmpty(prefix)){
318             throw new RuntimeException("cannot get Get method by null without prefix");
319         }
320         String getMethodName = prefix+fieldName.substring(0, 1).toUpperCase();
321         //fieldName 的长度大于一时,索引大于一的字符不改变大小写
322         if(fieldName.length()>1){
323             getMethodName = getMethodName + fieldName.substring(1);
324         }
325         return getMethodName;
326     }
327     
328     private String response(String type, Object obj) {
329         if (XML_TYPE.equalsIgnoreCase(type)) {
330             String ret = OxmHelper.marshal(obj);
331             logger.info("response:{}",ret);
332             return ret;
333         }
334         if (JSON_TYPE.equalsIgnoreCase(type)) {
335             String ret = JSONObject.toJSONString(obj);
336             logger.info("response:{}",ret);
337             return ret;
338         }
339         return ILLEGAL_TYPE + ":" + type;
340     }
341 }
  1 package com.slp.service.impl;
  2 
  3 import java.util.ArrayList;
  4 import java.util.List;
  5 import java.util.Set;
  6 import java.util.concurrent.TimeUnit;
  7 
  8 import org.slf4j.Logger;
  9 import org.slf4j.LoggerFactory;
 10 import org.springframework.beans.factory.annotation.Autowired;
 11 import org.springframework.dao.DataAccessException;
 12 import org.springframework.data.redis.core.RedisOperations;
 13 import org.springframework.data.redis.core.RedisTemplate;
 14 import org.springframework.data.redis.core.SessionCallback;
 15 import org.springframework.data.redis.core.ValueOperations;
 16 import org.springframework.stereotype.Service;
 17 import org.springframework.util.Assert;
 18 
 19 import com.cul.culsite.service.RedisService;
 20 
 21 @Service
 22 public class RedisServiceImpl implements RedisService {
 23     private static final Logger logger = LoggerFactory.getLogger(RedisServiceImpl.class);
 24     @Autowired
 25     private RedisTemplate<String, String> redisTemplate;
 26     
 27     /**
 28      * 获取redis值
 29      * 
 30      * @param key
 31      *            键
 32      * @return 33      */
 34     public String get(String key) {
 35         Assert.hasText(key, "redis get key cannot null");
 36 
 37         return redisTemplate.opsForValue().get(key);
 38     }
 39     
 40     /**
 41      * 删除redis键
 42      * 
 43      * @param key
 44      *            键
 45      */
 46     @Override
 47     public  void delete(String key) {
 48         Assert.hasText(key, "redis delete key cannot null");
 49         redisTemplate.delete(key);
 50     }
 51 
 52     
 53 
 54     /**
 55      * 设置redis值
 56      * 
 57      * @param key
 58      *            键
 59      * @param value
 60      *            值
 61      * @param time
 62      *            时间(分钟)   如果小于0 默认为1分钟     
 63      */
 64     @Override
 65     public  boolean setIfAbsent(final String key,final String value,int time) {
 66         Assert.hasText(key, "redis set key cannot null");
 67         Assert.hasText(value, "redis set value cannot null");
 68 
 69         if(time<=0){
 70             time = 1;
 71         }
 72         final int timeInSecond = time;
 73         try{
 74             
 75             @SuppressWarnings("unchecked")
 76             Object isSetSuccess = redisTemplate.execute(new SessionCallback() {
 77     
 78                 @Override
 79                 public Object execute(RedisOperations arg0)
 80                         throws DataAccessException {
 81                     try{
 82                          //开始事务
 83                         List<Object> result=new ArrayList<Object>();
 84                         arg0.multi();
 85                         arg0.opsForValue().setIfAbsent(key, value);
 86 //                        arg0.expireAt(key,DateUtils.addSeconds(new Date(),timeInSecond));
 87                         arg0.expire(key, timeInSecond, TimeUnit.SECONDS);
 88                      //提交事务
 89                         result= arg0.exec();
 90                     
 91                         logger.info("redis mutil for get lock result is :{}",result);
 92                      //执行了两次redis操作,应该有两个返回值,否则防止key永久有效,执行删除操作
 93                         if(result == null||result.size()!=2){
 94                             redisTemplate.delete(key);
 95                             return false;
 96                         }
 97                      //获取加锁操作的返回结果
 98                         boolean setIfAbsentResult = false;
 99                         if(result.get(0) instanceof Boolean){
100                             setIfAbsentResult =(Boolean)result.get(0);
101                         }
102                      //获取设置key有效时间返回结果
103                         boolean expireAtResult = false;
104                         if(result.get(1) instanceof Boolean){
105                             expireAtResult = (Boolean)result.get(1);
106                         }
107                         if(setIfAbsentResult&&expireAtResult){
108                             logger.info("加锁成功.......");
109                             return true;
110                         }
111                     }catch(Exception e){
112                         e.printStackTrace();
113                     }
114                     return false;
115                 }
116     
117             });
118             if(isSetSuccess instanceof Boolean){
119                 return (Boolean) isSetSuccess;
120             }
121             return false;
122         }catch(Exception e){
123             e.printStackTrace();
124             return false;
125         }
126     }
127     
128     @Override
129     public Set<String> keys(String keyPattern) {
130         Assert.hasText(keyPattern, "keys pattern is null");
131         
132         return redisTemplate.keys(keyPattern);
133     }
134     
135     @Override
136     public long incr(String key) {
137         Assert.hasText(key, "key is null");
138         
139         return redisTemplate.opsForValue().increment(key, 1L);
140     }
141     @Override
142     public long decr(String key) {
143         Assert.hasText(key, "key is null");
144         
145         return redisTemplate.opsForValue().increment(key, -1L);
146     }
147     
148     @Override
149     public void set(String key, String value) {
150         Assert.hasText(key, "key is null");
151         Assert.hasText(value, "value is null");
152         redisTemplate.opsForValue().set(key, value);
153     }
154     
155     @Override
156     public boolean set(final String key, final long value, final int timeInSecond) {
157         Assert.hasText(key, "key is null");
158         Assert.hasText(value + "", "value is null");
159         Assert.hasText(timeInSecond + "", "timeInSecond is null");
160         
161         try{
162             
163             @SuppressWarnings("unchecked")
164             Object isSetSuccess = redisTemplate.execute(new SessionCallback() {
165     
166                 @Override
167                 public Object execute(RedisOperations arg0)
168                         throws DataAccessException {
169                     try{
170                          //开始事务
171                         List<Object> result=new ArrayList<Object>();
172                         arg0.multi();
173                         arg0.opsForValue().increment(key, value);
174                         arg0.expire(key, timeInSecond, TimeUnit.SECONDS);
175                      //提交事务
176                         result= arg0.exec();
177                     
178                         logger.info("result of redis set long value is :{}",result);
179                      //执行了两次redis操作,应该有两个返回值,否则防止key永久有效,执行删除操作
180                         if(result == null || result.size() != 2){
181                             redisTemplate.opsForValue().increment(key, (0 - value));
182                             return false;
183                         }
184                      //获取加锁操作的返回结果
185                         long incrementResult = 0;
186                         if(result.get(0) instanceof Long){
187                             incrementResult =(Long)result.get(0);
188                         }
189                      //获取设置key有效时间返回结果
190                         boolean expireAtResult = false;
191                         if(result.get(1) instanceof Boolean){
192                             expireAtResult = (Boolean)result.get(1);
193                         }
194                         if((incrementResult == value) && expireAtResult){
195                             return true;
196                         }
197                     }catch(Exception e){
198                         e.printStackTrace();
199                     }
200                     redisTemplate.opsForValue().increment(key, (0 - value));
201                     return false;
202                 }
203     
204             });
205             if(isSetSuccess instanceof Boolean){
206                 return (Boolean) isSetSuccess;
207             }
208             return false;
209         }catch(Exception e){
210             e.printStackTrace();
211             return false;
212         }
213         
214     }
215     
216     
217     public Long getLong(String key) {
218         try{
219          Set<String> keys = redisTemplate.keys(key);
220          //key指定的数据不存在
221          if (keys == null || keys.isEmpty()) {
222              return null;
223          }
224          return redisTemplate.opsForValue().increment(key, 0);
225         } catch (DataAccessException e) {
226             logger.info("error :{}", e);
227             logger.info("{}指定的数据不是数值类型", key);
228             throw new RuntimeException(key + "指定的数据不是数值类型");
229         }
230     }
231 
232     public Long getLongNoKeys(String key) {
233         try {
234             long keys = redisTemplate.opsForValue().increment(key, 0);
235             return keys;
236         } catch (DataAccessException e) {
237             logger.info("error :{}", e);
238             logger.info("{}指定的数据不是数值类型", key);
239             throw new RuntimeException(key + "指定的数据不是数值类型");
240         }
241     }
242 
243     
244     
245     
246     /** 
247      * 删除set集合中的对象 
248      * @param key 
249      * @param value 
250      */  
251     @Override
252     public void srem(String key, String value) {  
253         redisTemplate.boundSetOps(key).remove(value);  
254     }  
255 }

使用方式:

/**
     * 
     * @Description: 
     * @param @param request
     * @param @return
     * @param @throws Exception   
     * @return String  
     * @throws
     * @author liping.sang
     * @date 2017-8-8
     */
    @RedisLock(fieldName={"reqNo"},timeInSecond=3)
    @RequestMapping(method = { RequestMethod.GET, RequestMethod.POST }, value = "/test2")
    @ResponseBody
    public String test2(HttpServletRequest request)
            throws Exception {
1 <aop:pointcut expression="execution(* com.slp.controller.Test.test(..))" id="test"/>
2 
3             <aop:around method="redisLockParse" pointcut-ref="test"/>            
原文地址:https://www.cnblogs.com/dream-to-pku/p/7615139.html