基于redis和aop实现分布式锁

package com.awj.mall.core.aop;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
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.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import com.awj.mall.core.base.tips.SuccessTip;
import com.awj.mall.core.common.annotion.DistributedLock;

import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;


/**
 * @author ljj
 */
@Slf4j
@Aspect
@Component
public class RedisLockAop {
	
	private ExpressionParser parser = new SpelExpressionParser();

	@Autowired
	private StringRedisTemplate stringRedisTemplate;

    @Pointcut("@annotation(com.awj.mall.core.common.annotion.DistributedLock)")
    public void pointCut() {
    }

    @Around("pointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        String className = point.getTarget().getClass().getName();
        Object[] args = point.getArgs();
        String[] paramNames = signature.getParameterNames();
        //参数写入SpringEl域中
        EvaluationContext context = new StandardEvaluationContext();
        for (int i = 0; i < args.length; i++) {
            context.setVariable(paramNames[i], args[i]);
        }
        //获取切面注解
        DistributedLock lock = method.getAnnotation(DistributedLock.class);
        TimeUnit timeUnit = lock.timeUnit();
        //redis key过期时间
        long expire = lock.expire();
        //key为空时锁方法,否则按SpringEl表达式取值
        String param = StringUtils.isEmpty(lock.key()) ? method.getName() : parser.parseExpression(lock.key()).getValue
                (context, String.class);
        //作用域为空时取className
        String lockName = StringUtils.isEmpty(lock.lockName()) ? className : lock.lockName();
        //构造redisKey
        String redisKey = lockName + "#" + param;
		try {
			ValueOperations valueOperations = stringRedisTemplate.opsForValue();
			Boolean cacheRes = valueOperations.setIfAbsent(redisKey, param);
			if (!cacheRes) {
				return new SuccessTip("系统繁忙,请稍后重试!");
			}
			stringRedisTemplate.expire(redisKey, expire, timeUnit);
		} catch (Exception e) {
			this.log.error("获取分布式锁失败", e);
		} 
        log.info("获取分布式锁失败,class={},method={},key={}", className, method, redisKey);
        Object proceed = point.proceed();
        return proceed;
    }


}

package com.awj.mall.core.common.annotion;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.concurrent.TimeUnit;

/**
 * @author ljj
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DistributedLock {

	/**
	 * 过期时间 时间按时间单位换算
	 * 
	 * @return
	 */
	long expire() default 30L;

	/**
	 * 时间单位 默认为秒
	 *
	 * @return
	 */
	TimeUnit timeUnit() default TimeUnit.SECONDS;

	/**
	 * springEl表达式 为空时取方法名称锁方法
	 *
	 * @return
	 */
	String key() default "";

	/**
	 * 定义lock作用域,避免key重复 为空时取类完整包名
	 *
	 * @return
	 */
	String lockName() default "";

	 

}

/**
	 * 回访完成
	 * 
	 * @author zjk
	 * @version 创建时间:2018年9月14日 下午3:40:10
	 * @param
	 * @return
	 */
	@RequestMapping("/visit")
	@ResponseBody
	@Transactional
	@DistributedLock(key = "#id")
	public Object visit(Integer id, Integer serviceUserId, Model model) {
		Date date = new Date();
		Integer userId = ShiroKit.getUser().getId();
		// 判断是否完成了财务审核,如果invoice_info表里面没有status为3的记录就是没有完成审核
		WorkOrderInvoiceInfo have = WorkOrderInvoiceInfoServiceImpl.selectOne(new EntityWrapper<WorkOrderInvoiceInfo>()
				.eq("work_order_id", id).eq("is_deleted", 0).eq("is_enable", 1).eq("status", 3));
		if (have == null) {
			return new SuccessTip("此工单不能完成回访,请先交由财务审核处理!");
		}
		// TODO:借助Redis的原子操作实现分布式锁-对共享操作-资源进行控制
		// ValueOperations valueOperations = stringRedisTemplate.opsForValue();
		// final String key = new
		// StringBuffer().append(have.getWorkOrderNo()).append("-RedisLock").toString();
		// Boolean cacheRes = valueOperations.setIfAbsent(key, have.getWorkOrderNo());

		// if (cacheRes) {
		UserServiceWorkOrderInfo content = this.iUserServiceWorkOrderInfoService.selectById(id);
		if (content != null) {
			if (content.getWorkOrderStatus() != 11) {
				return new SuccessTip("只有已财务核对的工单才能回访!");
			}
			// stringRedisTemplate.expire(key, 30, TimeUnit.SECONDS);
			content.setId(id);
			content.setWorkOrderStatus(5);
			content.setModifier(userId);
			content.setModified(date);
			content.setVisitDate(date);
			iUserServiceWorkOrderInfoService.updateById(content);

			// 将服务工单账单明细表的状态改为1(可提现)
			UserServiceWorkOrderBill bill = userServiceWorkOrderBillServiceImpl.selectOne(
					new EntityWrapper<UserServiceWorkOrderBill>().eq("work_order_id", id).eq("is_deleted", 0));
			if (bill != null) {
				bill.setType(1);
				bill.setModified(date);
				bill.setModifier(userId);
				bill.setUserPrice(have.getUserPrice());
				userServiceWorkOrderBillServiceImpl.updateById(bill);

				// 增加可提现服务费,减去未结算服务费
				UserBaseInfo info = iUserBaseInfoService.selectById(bill.getUserId());
				if (info != null) {
					info.setCouldWithdrawcashMoney(add(info.getCouldWithdrawcashMoney(), have.getUserPrice(), 2));
					info.setUnliquidatedWithdrawcashMoney(
							subtract(info.getUnliquidatedWithdrawcashMoney(), have.getUserPrice(), 2));
					info.setModifier(userId);
					info.setModified(date);
					iUserBaseInfoService.updateById(info);
				}
			}

			// 逻辑删除服务商与工单关系表记录
			EntityWrapper<UserServiceWorkOrderRel> myWrapper1 = new EntityWrapper<>();
			Wrapper<UserServiceWorkOrderRel> wrapper1 = myWrapper1.eq("user_id", serviceUserId).eq("work_order_id", id)
					.eq("is_deleted", 0);
			UserServiceWorkOrderRel userServiceWorkOrderRel = userServiceWorkOrderRelServiceImpl.selectOne(wrapper1);
			if (userServiceWorkOrderRel != null) {
				userServiceWorkOrderRel.setIsDeleted(1);
				userServiceWorkOrderRel.setIsEnable(0);
				userServiceWorkOrderRel.setModified(date);
				userServiceWorkOrderRel.setModifier(userId);
				userServiceWorkOrderRelServiceImpl.updateById(userServiceWorkOrderRel);
			}

			// 插入日志表
			UserServiceWorkOrderLog log = new UserServiceWorkOrderLog();
			SysUser sysUser = sysUserServiceImpl.selectById(userId);
			log.setWorkOrderId(id);
			log.setServiceUserId(serviceUserId);
			log.setStatus(6);
			log.setDetail("回访完成,操作员是" + sysUser.getAccount());
			log.setCreator(userId);
			log.setCreated(date);
			log.setModifier(userId);
			log.setModified(date);
			iUserServiceWorkOrderLogService.insert(log);

			// if (have.getWorkOrderNo().equals(valueOperations.get(key).toString())) {
			// stringRedisTemplate.delete(key);
			// }

		}
		// } else {
		// return new SuccessTip("系统繁忙,请稍后重试!");
		// }

		return SUCCESS_TIP;
	}

package com.awj.mall.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
 * redis配置类
 * @author SHF
 * @version 创建时间:2019年1月15日  上午9:31:51
 */
@Configuration
@EnableCaching//开启注解
public class RedisConfig {
	
	@Autowired
    private RedisConnectionFactory redisConnectionFactory;
	
	/*@Bean
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);

        //使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值(默认使用JDK的序列化方式)
        Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer(Object.class);
        template.setValueSerializer(serializer);
        //使用StringRedisSerializer来序列化和反序列化redis的key值
        template.setKeySerializer(new StringRedisSerializer());
        template.afterPropertiesSet();
        return template;
    }*/
    @Bean
    public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){
        RedisTemplate<String,Object> template=new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer=new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper objectMapper=new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
        StringRedisSerializer stringRedisSerializer=new StringRedisSerializer();
        template.setKeySerializer(stringRedisSerializer);
        template.setHashKeySerializer(stringRedisSerializer);
        template.setValueSerializer(jackson2JsonRedisSerializer);
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }
    
    @Bean
    public StringRedisTemplate stringRedisTemplate(){
        StringRedisTemplate stringRedisTemplate=new StringRedisTemplate();
        stringRedisTemplate.setConnectionFactory(redisConnectionFactory);
        return stringRedisTemplate;
    }

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值