本次用Springboot1.5.9版本
手动注入Bean
@Configuration
public class MonitorAutoConfiguration extends CachingConfigurerSupport {
/**
* @desc
*
* @param factory
* @return
*/
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
// 创建ObjectMapper
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
// 创建jackson序列化对象
Jackson2JsonRedisSerializer<Object> jsonSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
// 设置ObjectMapper
jsonSerializer.setObjectMapper(om);
// 字符串序列化
StringRedisSerializer stringSerializer = new StringRedisSerializer();
// 构建RedisTemplate,设置序列化
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
//开启事务
template.setEnableTransactionSupport(true);
// 设置相关序列化
template.setKeySerializer(stringSerializer);
template.setValueSerializer(jsonSerializer);
template.setHashKeySerializer(stringSerializer);
template.setHashValueSerializer(jsonSerializer);
template.afterPropertiesSet();
return template;
}
}
创建一个注解
/**
* Created by xulinglin on 2019/1/24
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface RedisLock {
/**
* 上锁key值
* @return
*/
String value() default "";
/**
* 上锁超时时间
* @return
*/
long time() default 3000;
/**
* 是否循环等待获取锁进行执行方法
* @return
*/
boolean required() default false;
/**
* 循环等待多少时间 时间单位秒
* @return
*/
long timeLock() default 0;
/**
* 是否开启事务
* 默认开启
* @return
*/
boolean transaction() default true;
}
使用Spring Aop 拦截器
/**
* Created by xulinglin on 2019/1/24
*/
@Aspect
@Order(-1)//保证该AOP在@Transactional之前执行
@Component
@Log
public class RedisLockInterceptor {
@Resource
private RedisTemplate<String, Object> redisTemplate;
@Pointcut("@annotation(redisLock)")
public void pointCut(RedisLock redisLock) {}
/**
* 环绕通知
* @param proceedingJoinPoint
* @param redisLock
* @return
* @throws Throwable
*/
@Around("pointCut(redisLock)")
public Object around(ProceedingJoinPoint proceedingJoinPoint, RedisLock redisLock) throws Throwable {
//方法执行前时间
long currentTimeMillis= System.currentTimeMillis();
//获取当前值
String value = redisLock.value();
//获取上锁超时时间
long time = redisLock.time();
//获取循环等待多少时间
long timeLock = redisLock.timeLock();
//控制当前循环
boolean required = redisLock.required();
required = timeLock > 0;
//获取事务是否开启
boolean transaction = redisLock.transaction();
//获取随机id
String name = String.valueOf(IdGenerator.next());
do {
if(null == redisTemplate.opsForValue().get(value)){
if(transaction){
try {
//事务 锁住 key
redisTemplate.watch(value);
//事务 multi 开始
redisTemplate.multi();
}catch (Exception e){
log.info("RedisLock Key: "+value+" 其他事务正在执行,停止本次方法执行");
return null;
}
}
//设置缓存值
redisTemplate.opsForValue().set(value,name,time,TimeUnit.MILLISECONDS);
if(transaction){
//事务提交
redisTemplate.exec();
return proceedingJoinPoint.proceed();
}else{
if(((String.valueOf(redisTemplate.opsForValue().get(value))).equals(name)))
//执行方法
return proceedingJoinPoint.proceed();
}
}
//计算需要循环时间
if(timeLock > 0 && ((System.currentTimeMillis() - currentTimeMillis)/1000d) > timeLock)
break;
}while (required);
//取消事务
if(transaction)
redisTemplate.discard();
return null;
}
}
@RedisLock(value = "xxx",timeLock = 20 ,transaction = false)
@RedisLock(value = "xxxx",time = 1100000)
可以在方法上使用了
注意:本次使用的是Spring Aop 实现的,在方法使用 @RedisLock 的时候 该类必须spring扫描过的