注解方式基于Redisson实现分布式锁

一、定义注解类

package com.example.demo.lock.annotation;

import com.example.demo.lock.model.LockType;

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

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface DistributedLock {
    /**
     * 锁名
     *
     * @return 默认空值
     */
    String name() default "";

    /**
     * 键值
     * 支持 SpEL 表达式,如:#param.id
     *
     * @return 解析 SpEl 的值或原文
     */
    String key() default "";

    /**
     * 尝试加锁,最多等待时间,时间单位以 unit 为准
     * 默认值(-1):不等待
     *
     * @return 默认-1
     */
    long waitTime() default -1;

    /**
     * 加锁以后多久自动解锁,时间单位以 unit 为准
     * 默认值(-1):默认30秒,Redisson自动续期
     * 其它值:按填写的值到期自动解锁,Redisson不会自动续期
     *
     * @return 默认-1
     */
    long leaseTime() default -1;

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

    /**
     * 锁类型,
     * 如:可重入锁、公平锁、读锁、写锁
     *
     * @return 默认可重入锁
     */
    LockType lockType() default LockType.REENTRANT;

    /**
     * 是否联锁(多把锁合为一个锁操作),
     * 可以在操作List类型锁使用,把List里的值合为一个锁。
     *
     * @return true-是联锁,false-非联锁
     */
    boolean isMultiLock() default false;

    /**
     * 是否尝试加锁,
     * true-尝试加锁,等待 waitTime 时间,无论成功还是失败都会返回;
     * false-阻塞加锁,直到完成加锁才返回。
     *
     * @return true-尝试加锁,false-阻塞加锁
     */
    boolean isTryLock() default true;
}

二、定义切面拦截 DistributedLock 注解

2.1 AOP处理

package com.example.demo.lock.aspect;

import com.alibaba.fastjson.JSON;
import com.example.demo.lock.annotation.DistributedLock;
import com.example.demo.lock.exception.TryLockFailException;
import com.example.demo.lock.provider.Lock;
import com.example.demo.lock.provider.LockFactory;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

@Slf4j
@Aspect
@Component
@Order(0)
public class DistributedLockAspect {
    @Autowired
    private LockFactory lockFactory;

    @Around("@annotation(distributedLock)")
    public Object around(ProceedingJoinPoint joinPoint, DistributedLock distributedLock) throws Throwable {
        Lock lock = lockFactory.getLock(joinPoint, distributedLock);
        log.info("↓↓↓ LockInfo ↓↓↓{}{}", System.lineSeparator(), JSON.toJSONString(lock.getLockInfo(), true));
        if (!lock.lock()) {
            throw new TryLockFailException(String.format("加锁失败!加锁[%s]失败", lock.getLockInfo().getName()));
        }
        log.info("加锁成功!加锁[{}]成功", lock.getLockInfo().getName());
        try {
            return joinPoint.proceed();
        } finally {
            try {
                lock.unlock();
                log.info("解锁成功!解锁[{}]成功", lock.getLockInfo().getName());
            } catch (Exception e) {
                log.error("解锁失败!解锁[{}]失败", lock.getLockInfo().getName(), e);
            }
        }
    }
}

2.2 解析SpEL表达式

package com.example.demo.lock.provider;

import com.example.demo.lock.annotation.DistributedLock;
import com.example.demo.lock.model.LockInfo;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.expression.MethodBasedEvaluationContext;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

@Component
public class LockKeyProvider {
    private static final String KEY_PREFIX = "lock";
    private static final String KEY_SEPARATOR = ":";
    private static final String ERROR_NO_VALUE_MESSAGE = "表达式[%s]未获取到值";
    private final ExpressionParser parser = new SpelExpressionParser();
    private final ParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();

    public Method getMethod(JoinPoint joinPoint) {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        return methodSignature.getMethod();
    }

    private String concat(DistributedLock distributedLock, String value) {
        Assert.hasText(value, String.format(ERROR_NO_VALUE_MESSAGE, distributedLock.key()));
        return StringUtils.joinWith(KEY_SEPARATOR, KEY_PREFIX, distributedLock.name(), value);
    }

    public LockInfo getInfo(JoinPoint joinPoint, DistributedLock distributedLock) {
        Object rootObject = joinPoint.getTarget();
        Method method = getMethod(joinPoint);
        Object[] args = joinPoint.getArgs();
        EvaluationContext context = new MethodBasedEvaluationContext(rootObject, method, args, discoverer);
        Expression expression = parser.parseExpression(distributedLock.key());

        LockInfo lockInfo = new LockInfo();
        lockInfo.setName(concat(distributedLock, expression.getValue(context, String.class)));
        if (distributedLock.isMultiLock()) {
            List<?> values = expression.getValue(context, List.class);
            Assert.notNull(values, String.format(ERROR_NO_VALUE_MESSAGE, distributedLock.key()));
            List<String> keys = new ArrayList<>();
            values.forEach(value -> keys.add(concat(distributedLock, value.toString())));
            lockInfo.setMultiLockNames(keys);
        }
        return lockInfo;
    }
}

三、锁的实现

3.1 锁接口

package com.example.demo.lock.provider;

import com.example.demo.lock.model.LockInfo;

public interface Lock {
    /**
     * 加锁
     *
     * @return true-加锁成功;false-加锁失败
     */
    boolean lock();

    /**
     * 解锁
     */
    void unlock();

    /**
     * 获取锁信息
     *
     * @return 锁信息
     */
    LockInfo getLockInfo();
}

3.2 抽象锁

package com.example.demo.lock.provider;

import com.example.demo.lock.model.LockInfo;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.util.Assert;

@Slf4j
public abstract class AbstractLock implements Lock {
    private final RedissonClient redissonClient;
    private final LockInfo lockInfo;
    private RLock rLock;

    protected AbstractLock(RedissonClient redissonClient, LockInfo lockInfo) {
        this.redissonClient = redissonClient;
        this.lockInfo = lockInfo;
    }

    protected RedissonClient getRedissonClient() {
        return redissonClient;
    }

    @Override
    public LockInfo getLockInfo() {
        return lockInfo;
    }

    /**
     * 获取 Redisson Lock
     *
     * @return RLock
     */
    protected abstract RLock getRedissonLock();

    @Override
    public boolean lock() {
        try {
            rLock = getRedissonLock();
            if (getLockInfo().isTryLock()) {
                return rLock.tryLock(getLockInfo().getWaitTime(), getLockInfo().getLeaseTime(), getLockInfo().getUnit());
            } else {
                rLock.lock(getLockInfo().getLeaseTime(), getLockInfo().getUnit());
                return true;
            }
        } catch (Exception e) {
            log.error("加锁失败", e);
            return false;
        }
    }

    @Override
    public void unlock() {
        Assert.notNull(rLock, "锁为空,未能执行解锁!");
        rLock.unlock();
    }
}

3.3 可重入锁

package com.example.demo.lock.provider.concrete;

import com.example.demo.lock.model.LockInfo;
import com.example.demo.lock.provider.AbstractLock;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;

@Slf4j
public class ReentrantLock extends AbstractLock {
    public ReentrantLock(RedissonClient redissonClient, LockInfo lockInfo) {
        super(redissonClient, lockInfo);
    }

    @Override
    protected RLock getRedissonLock() {
        if (getLockInfo().isMultiLock()) {
            RLock[] rLocks = new RLock[getLockInfo().getMultiLockNames().size()];
            for (int i = 0; i < getLockInfo().getMultiLockNames().size(); i++) {
                rLocks[i] = getRedissonClient().getLock(getLockInfo().getMultiLockNames().get(i));
            }
            return getRedissonClient().getMultiLock(rLocks);
        } else {
            return getRedissonClient().getLock(getLockInfo().getName());
        }
    }
}

3.4 公平锁

package com.example.demo.lock.provider.concrete;

import com.example.demo.lock.model.LockInfo;
import com.example.demo.lock.provider.AbstractLock;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;

@Slf4j
public class FairLock extends AbstractLock {
    public FairLock(RedissonClient redissonClient, LockInfo lockInfo) {
        super(redissonClient, lockInfo);
    }

    @Override
    protected RLock getRedissonLock() {
        if (getLockInfo().isMultiLock()) {
            RLock[] rLocks = new RLock[getLockInfo().getMultiLockNames().size()];
            for (int i = 0; i < getLockInfo().getMultiLockNames().size(); i++) {
                rLocks[i] = getRedissonClient().getFairLock(getLockInfo().getMultiLockNames().get(i));
            }
            return getRedissonClient().getMultiLock(rLocks);
        } else {
            return getRedissonClient().getFairLock(getLockInfo().getName());
        }
    }
}

3.5 读锁

package com.example.demo.lock.provider.concrete;

import com.example.demo.lock.model.LockInfo;
import com.example.demo.lock.provider.AbstractLock;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;

@Slf4j
public class ReadLock extends AbstractLock {
    public ReadLock(RedissonClient redissonClient, LockInfo lockInfo) {
        super(redissonClient, lockInfo);
    }

    @Override
    protected RLock getRedissonLock() {
        if (getLockInfo().isMultiLock()) {
            RLock[] rLocks = new RLock[getLockInfo().getMultiLockNames().size()];
            for (int i = 0; i < getLockInfo().getMultiLockNames().size(); i++) {
                rLocks[i] = getRedissonClient().getReadWriteLock(getLockInfo().getMultiLockNames().get(i)).readLock();
            }
            return getRedissonClient().getMultiLock(rLocks);
        } else {
            return getRedissonClient().getReadWriteLock(getLockInfo().getName()).readLock();
        }
    }
}

3.6 写锁

package com.example.demo.lock.provider.concrete;

import com.example.demo.lock.model.LockInfo;
import com.example.demo.lock.provider.AbstractLock;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;

@Slf4j
public class WriteLock extends AbstractLock {
    public WriteLock(RedissonClient redissonClient, LockInfo lockInfo) {
        super(redissonClient, lockInfo);
    }

    @Override
    protected RLock getRedissonLock() {
        if (getLockInfo().isMultiLock()) {
            RLock[] rLocks = new RLock[getLockInfo().getMultiLockNames().size()];
            for (int i = 0; i < getLockInfo().getMultiLockNames().size(); i++) {
                rLocks[i] = getRedissonClient().getReadWriteLock(getLockInfo().getMultiLockNames().get(i)).writeLock();
            }
            return getRedissonClient().getMultiLock(rLocks);
        } else {
            return getRedissonClient().getReadWriteLock(getLockInfo().getName()).writeLock();
        }
    }
}

3.7 锁工厂

package com.example.demo.lock.provider;

import com.example.demo.lock.annotation.DistributedLock;
import com.example.demo.lock.model.LockInfo;
import com.example.demo.lock.provider.concrete.FairLock;
import com.example.demo.lock.provider.concrete.ReadLock;
import com.example.demo.lock.provider.concrete.ReentrantLock;
import com.example.demo.lock.provider.concrete.WriteLock;
import org.aspectj.lang.JoinPoint;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class LockFactory {
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private LockInfoProvider lockInfoProvider;

    public Lock getLock(JoinPoint joinPoint, DistributedLock distributedLock) {
        LockInfo lockInfo = lockInfoProvider.get(joinPoint, distributedLock);
        switch (lockInfo.getType()) {
            case REENTRANT:
                return new ReentrantLock(redissonClient, lockInfo);
            case FAIR:
                return new FairLock(redissonClient, lockInfo);
            case READ:
                return new ReadLock(redissonClient, lockInfo);
            case WRITE:
                return new WriteLock(redissonClient, lockInfo);
            default:
                throw new UnsupportedOperationException("还未实现该类型的锁");
        }
    }
}

四、使用方法

在需要用分布式锁的方法上面加 @DistributedLock 注解即可。

@PostMapping("/single")
@DistributedLock(name = "demo:product", key = "#param.id")
public Object single(@RequestBody @Valid LockDTO param) throws InterruptedException {
    TimeUnit.SECONDS.sleep(20);
    return "OK";
}

你可能感兴趣的:(系统架构,Java,代码实例,分布式锁,Redisson,注解,切面,Java)