利用redis实现redisson分布式锁,自定义注解形式

我要实现的业务是根据年月生成自增的编码

添加所需包


        <!-- redis 缓存操作 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson-spring-boot-starter</artifactId>
        </dependency>

redis配置及redisson配置

1.yml

    # redis 配置
    redis:
        # 地址
        host: ip地址
        # 端口,默认为6379
        port: 6379
        # 密码
        password:
        # 连接超时时间
        timeout: 10s
        lettuce:
            pool:
                # 连接池中的最小空闲连接
                min-idle: 0
                # 连接池中的最大空闲连接
                max-idle: 8
                # 连接池的最大数据库连接数
                max-active: 8
                # #连接池最大阻塞等待时间(使用负值表示没有限制)
                max-wait: -1ms
#编码头
 LeaveCodeTop: PD

2.config

package com.ruoyi.framework.config;

import org.redisson.Redisson;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.CachingConfigurerSupport;
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.serializer.StringRedisSerializer;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;

/**
 * redis配置
 * 
 * @author ruoyi
 */
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport
{

    @Value("${spring.redis.host}")
    private String host ;

    @Value("${spring.redis.port}")
    private String port;

    @Bean
    @SuppressWarnings(value = { "unchecked", "rawtypes" })
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory)
    {
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);

        FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class);

        ObjectMapper mapper = new ObjectMapper();
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        serializer.setObjectMapper(mapper);

        template.setValueSerializer(serializer);
        // 使用StringRedisSerializer来序列化和反序列化redis的key值
        template.setKeySerializer(new StringRedisSerializer());
        template.afterPropertiesSet();
        return template;
    }

    @Bean
    public Redisson redisson() {
        Config config = new Config();
        System.err.println("redis://" + host + ":"+port);
        config.useSingleServer().setAddress("redis://" + host + ":"+port);
        return (Redisson) Redisson.create(config);
    }
}

自定义注解

package com.ruoyi.framework.aspectj;

import java.lang.annotation.*;
import java.util.concurrent.TimeUnit;

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface Rlock {

    /**
     * 分布式锁的key
     */
    String localKey() default "redisLockAnnotation::";

    /**
     * 等待时间 默认五秒
     *
     * @return
     */
    long waitTime() default 5;

    /**
     * 锁释放时间 默认十秒
     *
     * @return
     */
    long leaseTime() default 10;

    /**
     * 时间格式 默认:秒
     *
     * @return
     */
    TimeUnit timeUnit() default TimeUnit.SECONDS;
}

建立aop切面

package com.ruoyi.framework.aspectj;

import lombok.extern.slf4j.Slf4j;
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.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Aspect
@Component
@Slf4j
public class RlockAspect {

    @Autowired
    private RedissonClient redissonClient;

    @Pointcut("@annotation(com.ruoyi.framework.aspectj.Rlock)")
    public void RlockAspect() { }

    @Around("RlockAspect()")
    public Object arround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        Object object = null;
        RLock lock = null;
        try {
        	// 获取注解信息
            Rlock rlockInfo = getRlockInfo(proceedingJoinPoint);
            // 根据名字获取锁实例
            lock = redissonClient.getLock(getLocalKey(proceedingJoinPoint, rlockInfo));
            System.err.println("获取锁");
            if (lock != null) {
                final boolean status = lock.tryLock(rlockInfo.waitTime(), rlockInfo.leaseTime(), rlockInfo.timeUnit());
                if (status) {
                    System.err.println("执行业务代码");
                    object = proceedingJoinPoint.proceed();

                }else{
                    throw new RuntimeException("获取锁失败");
                }
                System.err.println("业务代码执行完毕");
            }
        } finally {
            if (lock != null && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
            System.err.println("释放锁");
        }
        return object;
    }

    public Rlock getRlockInfo(ProceedingJoinPoint proceedingJoinPoint) {
        MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature();
        return methodSignature.getMethod().getAnnotation(Rlock.class);
    }

    /**
     * @param proceedingJoinPoint
     * @return
     */
    public String getLocalKey(ProceedingJoinPoint proceedingJoinPoint, Rlock rlockInfo) {
        StringBuilder localKey = new StringBuilder();
        final Object[] args = proceedingJoinPoint.getArgs();

        MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature();
        String methodName = methodSignature.getMethod().getName();
        localKey.append(rlockInfo.localKey()).append(methodName);

        return localKey.toString();
    }
}

使用


    public static String LEAVE_CODE_TOP = "";

    @Value("${LeaveCodeTop}")
    private void setLeaveCodeTop(String LeaveCodeTop) {
    	//当前类赋值static
        PlaLeaveApplyServiceImpl.LEAVE_CODE_TOP = LeaveCodeTop;
    }


@Transactional
    @Rlock(localKey = "redisLockAnnotation", waitTime = 10, leaseTime = 10, timeUnit = TimeUnit.SECONDS)
    @Override
    public int getsss(PlaLeaveApply plaLeaveApply) {

        QueryWrapper<PlaLeaveApply> queryWrapper = new QueryWrapper<>();
        Calendar cal = Calendar.getInstance();
        //获取当前年
        int years = cal.get(Calendar.YEAR);
        //获取当前月
        int month = cal.get(Calendar.MONTH) + 1;
        String codeStr;
        //判断月份是否需要拼接0
        if (month < 10) {
            //拼接编码能确定的前一部分
            codeStr = LEAVE_CODE_TOP + years + "0" + month;
        } else {
            //拼接编码能确定的前一部分
            codeStr = LEAVE_CODE_TOP + years + month;
        }
        //模糊查询
        queryWrapper.like("apply_no", codeStr);
         //根据自增的id排序,不能使用创建时间降序排列,因为如果同时生成很多条数据,创建时间有可能会出现
        //相同时间,这样排序的话每次可能都会不同,生成的编码可能会出现重复
        queryWrapper.orderByDesc("id");
        //根据排列后的数据取第一条数据
        queryWrapper.last("LIMIT 1");
        System.err.println("查询");
        PlaLeaveApply plaLeaveApplypr = plaLeaveApplyMapper.selectOne(queryWrapper);
        String applyNo;
        //判断是否获取到收据
        if (StringUtils.isNotNull(plaLeaveApplypr)) {
            //获取数据的编码
            String code = plaLeaveApplypr.getApplyNo();
            System.err.println("code:" + code);
            //分割获取数字,将string型转换成 BigInteger  注意:不能转int或者Integer,因为数过于大,会报错
            BigInteger codeInteger = new BigInteger(code.split(LEAVE_CODE_TOP)[1]);
            //将编码加1后转拼接前缀,生成string型编码
            applyNo = LEAVE_CODE_TOP + codeInteger.add(new BigInteger("1"));
        } else {
            //如果没数据说名当前是第一条编码,所以拼接00001即可
            applyNo = codeStr + "00001";
        }
        String personnelType = SecurityUtils.getLoginUser().getUser().getPersonnelType();
        QueryWrapper<PlaLeaveFlow> qry = new QueryWrapper<>();
        qry.eq("personnel_type", personnelType);
        PlaLeaveFlow plaLeaveFlow = plaLeaveFlowMapper.selectOne(qry);
        plaLeaveApply.setApplyNo(applyNo);
        plaLeaveApply.setApplicantId(SecurityUtils.getLoginUser().getUser().getUserId());
        plaLeaveApply.setDeptId(SecurityUtils.getLoginUser().getUser().getDeptId());
        plaLeaveApply.setApplyTime(new Date());
        plaLeaveApply.setState(1);
        plaLeaveApply.setLeaveFlowId(plaLeaveFlow.getId());
        plaLeaveApply.setCreateBy(String.valueOf(SecurityUtils.getLoginUser().getUser().getUserId()));
        plaLeaveApply.setCreateTime(new Date());
        plaLeaveApplyMapper.insert(plaLeaveApply);
        PlaLeaveApplyDetail plaLeaveApplyDetail = new PlaLeaveApplyDetail();
        plaLeaveApplyDetail.setApplyId(plaLeaveApply.getId());
        plaLeaveApplyDetail.setStepId(0L);
        return plaLeaveApplyDetailMapper.insert(plaLeaveApplyDetail);

    }

效果截图

apply_no为编码字段

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值