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;
}
}