Skip to content

Commit 4cea59d

Browse files
committed
基于redis的分布式锁
1 parent 094d7cc commit 4cea59d

14 files changed

+596
-16
lines changed

pom.xml

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,21 @@
7272

7373
<dependency>
7474
<groupId>org.springframework.boot</groupId>
75-
<artifactId>spring-boot-starter-data-redis</artifactId>
76-
<version>1.5.9.RELEASE</version>
75+
<artifactId>spring-boot-starter-aop</artifactId>
76+
</dependency>
77+
<dependency>
78+
<groupId>org.springframework.boot</groupId>
79+
<artifactId>spring-boot-starter-web</artifactId>
80+
</dependency>
81+
<dependency>
82+
<groupId>org.redisson</groupId>
83+
<artifactId>redisson</artifactId>
84+
<version>2.10.3</version>
85+
</dependency>
86+
<dependency>
87+
<groupId>commons-beanutils</groupId>
88+
<artifactId>commons-beanutils</artifactId>
89+
<version>1.8.3</version>
7790
</dependency>
7891

7992

@@ -190,6 +203,15 @@
190203
<artifactId>spring-security-web</artifactId>
191204
<version>3.2.4.RELEASE</version>
192205
</dependency>
206+
<dependency>
207+
<groupId>org.springframework.data</groupId>
208+
<artifactId>spring-data-redis</artifactId>
209+
</dependency>
210+
<dependency>
211+
<groupId>commons-beanutils</groupId>
212+
<artifactId>commons-beanutils</artifactId>
213+
<version>1.8.3</version>
214+
</dependency>
193215
</dependencies>
194216

195217
<build>

src/main/java/com/victor/config/redis/RedisCacheConfig.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,8 @@
77
import org.springframework.beans.factory.annotation.Value;
88
import org.springframework.cache.CacheManager;
99
import org.springframework.cache.annotation.CachingConfigurerSupport;
10-
import org.springframework.cache.annotation.EnableCaching;
1110
import org.springframework.cache.interceptor.KeyGenerator;
1211
import org.springframework.context.annotation.Bean;
13-
import org.springframework.context.annotation.Configuration;
1412
import org.springframework.data.redis.cache.RedisCacheManager;
1513
import org.springframework.data.redis.connection.RedisConnectionFactory;
1614
import org.springframework.data.redis.core.RedisTemplate;
@@ -32,8 +30,8 @@
3230
* 普通使用普通类的方式的话,那么在使用@Cacheable的时候还需要指定KeyGenerator的名称;这样编码的时候比较麻烦。
3331
*/
3432

35-
@Configuration
36-
@EnableCaching //开启缓存
33+
//@Configuration
34+
//@EnableCaching //开启缓存
3735
public class RedisCacheConfig extends CachingConfigurerSupport {
3836

3937
@Value("${spring.redis.timeout}")

src/main/java/com/victor/config/shiro/ShiroConfiguration.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
7777
filterChainDefinitionMap.put("/admin/isEmail/**", "anon"); // 判断邮箱是否存在
7878
filterChainDefinitionMap.put("/coreServlet/**", "anon"); // 微信相关
7979
filterChainDefinitionMap.put("/wx/**", "anon"); // 微信相关
80+
filterChainDefinitionMap.put("/distributedLockTest/**", "anon"); // 微信相关
8081
filterChainDefinitionMap.put("/**", "authc");
8182
/**
8283
* anon:所有url都都可以匿名访问;
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package com.victor.redis;
2+
3+
import java.lang.annotation.Documented;
4+
import java.lang.annotation.ElementType;
5+
import java.lang.annotation.Retention;
6+
import java.lang.annotation.RetentionPolicy;
7+
import java.lang.annotation.Target;
8+
import java.util.concurrent.TimeUnit;
9+
10+
/**
11+
* @author Victor
12+
* @date 2018/01/04
13+
*/
14+
@Target({ElementType.METHOD})
15+
@Retention(RetentionPolicy.RUNTIME)
16+
@Documented
17+
public @interface DistributedLock {
18+
19+
String lockName() default ""; //如果lockName可以确定,直接设置该属性
20+
21+
String lockNamePre() default ""; //lockName后缀
22+
23+
String lockNamePost() default ""; //lockName后缀
24+
25+
String param() default ""; //获取注解的方法第一个参数对象的某个属性值来作为lockName。因为有时候lockName是不固定的。
26+
27+
int argNum() default 0; //将方法第argNum个参数作为锁
28+
29+
boolean fairLock() default false; //是否使用公平锁。
30+
31+
boolean tryLock() default false; //是否使用尝试锁。
32+
33+
long waitTime() default 30L;
34+
35+
long leaseTime() default 5L;
36+
37+
TimeUnit timeUnit() default TimeUnit.SECONDS;
38+
}
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
package com.victor.redis;
2+
3+
import org.apache.commons.beanutils.PropertyUtils;
4+
import org.apache.commons.lang.StringUtils;
5+
import org.aspectj.lang.ProceedingJoinPoint;
6+
import org.aspectj.lang.annotation.Around;
7+
import org.aspectj.lang.annotation.Aspect;
8+
import org.aspectj.lang.annotation.Pointcut;
9+
import org.springframework.beans.factory.annotation.Autowired;
10+
import org.springframework.stereotype.Component;
11+
12+
import java.lang.reflect.Method;
13+
import java.util.Arrays;
14+
import java.util.Optional;
15+
import java.util.concurrent.TimeUnit;
16+
17+
/**
18+
* @author Victor
19+
* @date 2018/01/04
20+
* 切面
21+
*/
22+
@Aspect
23+
@Component
24+
public class DistributedLockAspect {
25+
26+
@Autowired
27+
private DistributedLockTemplate lockTemplate;
28+
29+
@Pointcut("@annotation(DistributedLock)")
30+
public void DistributedLockAspect() {}
31+
32+
33+
34+
@Around(value = "DistributedLockAspect()")
35+
public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
36+
37+
//切点所在的类名
38+
String targetName = pjp.getTarget().getClass().getName();
39+
40+
//使用了注解的方法
41+
String methodName = pjp.getSignature().getName();
42+
43+
Class targetClass = Class.forName(targetName);
44+
45+
Method[] methods = targetClass.getMethods();
46+
47+
Object[] arguments = pjp.getArgs();
48+
49+
//得到使用注解的方法。可使用Method.getAnnotation(Class<T> annotationClass)获取指定的注解,然后可获得注解的属性
50+
Optional<Method> optional = Arrays.stream(methods)
51+
.parallel()
52+
.filter(method -> method.getName().equals(methodName))
53+
.findAny();
54+
55+
if (optional.isPresent()) {
56+
Method m = optional.get();
57+
final String lockName = getLockName(m, arguments);
58+
return lock(pjp, m, lockName);
59+
}
60+
61+
return null;
62+
}
63+
64+
public String getLockName(Method method, Object[] args) throws Throwable {
65+
66+
DistributedLock annotation = method.getAnnotation(DistributedLock.class);
67+
68+
String lockName = annotation.lockName(),
69+
param = annotation.param();
70+
71+
if (StringUtils.isEmpty(lockName)) {
72+
73+
if (args.length > 0) {
74+
if (annotation.argNum() > 0) {
75+
lockName = args[annotation.argNum() - 1].toString();
76+
}
77+
78+
if (!StringUtils.isEmpty(param)) {
79+
Object arg = args[0];
80+
lockName = String.valueOf(getParam(arg, param));
81+
}
82+
}
83+
}
84+
85+
if (! StringUtils.isEmpty(lockName)) {
86+
String preLockName = annotation.lockNamePre(),
87+
postLockName = annotation.lockNamePost();
88+
89+
lockName = preLockName + lockName + postLockName;
90+
91+
return lockName;
92+
}
93+
94+
throw new IllegalArgumentException("Can't get or generate lockName accurately!");
95+
}
96+
97+
/**
98+
* 从方法参数获取数据
99+
*
100+
* @param param
101+
* @param arg 方法的参数数组
102+
* @return
103+
*/
104+
public Object getParam(Object arg, String param) throws Throwable {
105+
106+
if (!StringUtils.isEmpty(param) && arg != null) {
107+
Object result = PropertyUtils.getProperty(arg, param);
108+
return result;
109+
}
110+
111+
return null;
112+
}
113+
114+
public Object lock(ProceedingJoinPoint pjp, Method method, final String lockName) {
115+
116+
DistributedLock annotation = method.getAnnotation(DistributedLock.class);
117+
118+
boolean fairLock = annotation.fairLock();
119+
120+
boolean tryLock = annotation.tryLock();
121+
122+
if (tryLock) {
123+
return tryLock(pjp, annotation, lockName, fairLock);
124+
} else {
125+
return lock(pjp,lockName, fairLock);
126+
}
127+
}
128+
129+
public Object lock(ProceedingJoinPoint pjp, final String lockName, boolean fairLock) {
130+
return lockTemplate.lock(new DistributedLockCallback<Object>() {
131+
@Override
132+
public Object process() {
133+
return proceed(pjp);
134+
}
135+
136+
@Override
137+
public String getLockName() {
138+
return lockName;
139+
}
140+
}, fairLock);
141+
}
142+
143+
public Object tryLock(ProceedingJoinPoint pjp, DistributedLock annotation, final String lockName, boolean fairLock) {
144+
145+
long waitTime = annotation.waitTime(),
146+
leaseTime = annotation.leaseTime();
147+
TimeUnit timeUnit = annotation.timeUnit();
148+
149+
return lockTemplate.tryLock(new DistributedLockCallback<Object>() {
150+
@Override
151+
public Object process() {
152+
return proceed(pjp);
153+
}
154+
155+
@Override
156+
public String getLockName() {
157+
return lockName;
158+
}
159+
}, waitTime, leaseTime, timeUnit, fairLock);
160+
}
161+
162+
public Object proceed(ProceedingJoinPoint pjp) {
163+
try {
164+
return pjp.proceed();
165+
} catch (Throwable throwable) {
166+
throwable.printStackTrace();
167+
}
168+
return null;
169+
}
170+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package com.victor.redis;
2+
3+
/**
4+
* @author Victor
5+
* @date 2018/01/04
6+
* 分布式锁回调接口
7+
*/
8+
public interface DistributedLockCallback<T> {
9+
10+
/**
11+
* 调用者必须在此方法中实现需要加分布式锁的业务逻辑
12+
*
13+
* @return
14+
*/
15+
public T process();
16+
17+
/**
18+
* 得到分布式锁名称
19+
*
20+
* @return
21+
*/
22+
public String getLockName();
23+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package com.victor.redis;
2+
3+
import java.util.concurrent.TimeUnit;
4+
5+
/**
6+
* @author Victor
7+
* @date 2018/01/04
8+
* 分布式锁操作模板
9+
*/
10+
public interface DistributedLockTemplate {
11+
12+
long DEFAULT_WAIT_TIME = 30;
13+
long DEFAULT_TIMEOUT = 5;
14+
TimeUnit DEFAULT_TIME_UNIT = TimeUnit.SECONDS;
15+
16+
/**
17+
* 使用分布式锁,使用锁默认超时时间。
18+
* @param callback
19+
* @param fairLock 是否使用公平锁
20+
* @return
21+
*/
22+
<T> T lock(DistributedLockCallback<T> callback, boolean fairLock);
23+
24+
/**
25+
* 使用分布式锁。自定义锁的超时时间
26+
*
27+
* @param callback
28+
* @param leaseTime 锁超时时间。超时后自动释放锁。
29+
* @param timeUnit
30+
* @param fairLock 是否使用公平锁
31+
* @param <T>
32+
* @return
33+
*/
34+
<T> T lock(DistributedLockCallback<T> callback, long leaseTime, TimeUnit timeUnit, boolean fairLock);
35+
36+
/**
37+
* 尝试分布式锁,使用锁默认等待时间、超时时间。
38+
* @param callback
39+
* @param fairLock 是否使用公平锁
40+
* @param <T>
41+
* @return
42+
*/
43+
<T> T tryLock(DistributedLockCallback<T> callback, boolean fairLock);
44+
45+
/**
46+
* 尝试分布式锁,自定义等待时间、超时时间。
47+
* @param callback
48+
* @param waitTime 获取锁最长等待时间
49+
* @param leaseTime 锁超时时间。超时后自动释放锁。
50+
* @param timeUnit
51+
* @param fairLock 是否使用公平锁
52+
* @param <T>
53+
* @return
54+
*/
55+
<T> T tryLock(DistributedLockCallback<T> callback, long waitTime, long leaseTime, TimeUnit timeUnit, boolean fairLock);
56+
}

0 commit comments

Comments
 (0)