分布式锁

package com.zkj.mall.cart.common.lock;

import com.zkj.mall.cart.common.lock.annotation.DistributedLock;
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.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.expression.MethodBasedEvaluationContext;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.Ordered;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * 分布式锁切入点,会切入被 DistributedLock 注解的方法
 * Created by bin.li01 on 2017/9/21.
 */
@Aspect
@Component("distributedLock")
@EnableConfigurationProperties(DistributedLockProperties.class)
public class DistributedLockAspect implements InitializingBean,Ordered{

    private final SpelExpressionParser parser = new SpelExpressionParser();
    private final ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();

    @Autowired
    private DistributedLockComponent locker;

    @Autowired
    private DistributedLockProperties properties;

    @Pointcut("@annotation(com.zkj.mall.cart.common.lock.annotation.DistributedLock)")
    private void pointcut(){
    }

    @Around(value="pointcut() && @annotation(distributedLock)")
    public Object invoke(ProceedingJoinPoint point, DistributedLock distributedLock) throws Throwable {
        String[] bizKeys = distributedLock.bizKey();
        MethodSignature methodSignature = (MethodSignature) point.getSignature();
        Method method = methodSignature.getMethod();

        MethodBasedEvaluationContext context = new MethodBasedEvaluationContext(point.getTarget(), method, point.getArgs(), parameterNameDiscoverer);

        StringBuffer lockKeySb = new StringBuffer();
        lockKeySb.append(properties.getLockKeyPrefix()).append(methodSignature.toString());
        for (String bizKey:bizKeys){
            lockKeySb.append("#").append(parser.parseExpression(bizKey).getValue(context));
        }

        String lockKey = lockKeySb.toString().replaceAll(" ","");
        // 默认锁在redis中失效时间,生效顺序:注解中重新定义的 > DistributedLockProperties配置文件中重新定义 > 默认值5
        long expire = distributedLock.lockSeconds();
        expire = expire == 5L ? properties.getDefaultLockSeconds() : expire;
        try {
            locker.lock(lockKey.toString(), expire);
            return point.proceed();
        }finally {
            locker.unlock(lockKey.toString());
        }
    }

    @Override
    public int getOrder() {
        return properties.getDefaultAspectOrder();
    }

    @Override
    public void afterPropertiesSet() throws Exception {

    }
}

package com.zkj.mall.cart.common.lock;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.Random;
import java.util.concurrent.TimeUnit;

/**
 * 用redis支持的分布式锁组件,可以单独用,DistributeLock注解会使用这个组件
 * @author bin.li01
 */
@Component
@EnableConfigurationProperties(DistributedLockProperties.class)
public class DistributedLockComponent<K> {
    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private DistributedLockProperties properties;

    /**
     * 加锁
     * @param key
     * @throws InterruptedException
     */
    public void lock(K key)  throws InterruptedException{
        long expire = properties.getDefaultLockSeconds().longValue();
        this.lock(key, expire);
    }

    /**
     * 加锁
     * @param key
     * @param expire
     * @throws InterruptedException
     */
    public void lock(K key, long expire) throws InterruptedException {
        long nowTime = System.nanoTime();
        Random r = new Random();

        while(System.nanoTime() - nowTime < properties.getDefaultLockTimeout()) {
            byte[]  keyByte = redisTemplate.getKeySerializer().serialize(key);
            boolean flag = redisTemplate.getConnectionFactory().getConnection().setNX(keyByte, keyByte).booleanValue();
            if(flag) {
                redisTemplate.expire(key, expire, TimeUnit.SECONDS);
                break;
            }
            // 防止程序异常退出引起的死锁
            if (redisTemplate.getExpire(key) == -1){
                redisTemplate.expire(key, expire, TimeUnit.SECONDS);
            }
            Thread.sleep(300L, r.nextInt(500));
        }
    }

    public void unlock(K key) {
        redisTemplate.delete(key);
    }
}

package com.zkj.mall.cart.common.lock;

import org.springframework.boot.context.properties.ConfigurationProperties;

/**
 * Created by bin.li01 on 2017/9/21.
 */
@ConfigurationProperties(prefix = DistributedLockProperties.PREFIX)
public class DistributedLockProperties {
    public static final String PREFIX = "distributed.lock";

    // 分布式锁的key的前缀
    private String lockKeyPrefix = "groupon:lock:";

    // 默认锁在redis中失效时间,生效顺序:注解中重新定义的 > DistributedLockProperties配置文件中重新定义 > 默认值5
    private Long defaultLockSeconds = 5L;

    // 获取锁最长等待时间
    private Long defaultLockTimeout = Long.MAX_VALUE;

    // 切面的order,如果工程中有多个切面需要执行切面顺序时配置
    private Integer defaultAspectOrder = 0;

    public String getLockKeyPrefix() {
        return lockKeyPrefix;
    }

    public void setLockKeyPrefix(String lockKeyPrefix) {
        this.lockKeyPrefix = lockKeyPrefix;
    }

    public Long getDefaultLockSeconds() {
        return defaultLockSeconds;
    }

    public void setDefaultLockSeconds(Long defaultLockSeconds) {
        this.defaultLockSeconds = defaultLockSeconds;
    }

    public Long getDefaultLockTimeout() {
        return defaultLockTimeout;
    }

    public void setDefaultLockTimeout(Long defaultLockTimeout) {
        this.defaultLockTimeout = defaultLockTimeout;
    }

    public Integer getDefaultAspectOrder() {
        return defaultAspectOrder;
    }

    public void setDefaultAspectOrder(Integer defaultAspectOrder) {
        this.defaultAspectOrder = defaultAspectOrder;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值