diff --git a/lock-api/src/main/java/com/github/houbb/lock/api/core/ILockSupportContext.java b/lock-api/src/main/java/com/github/houbb/lock/api/core/ILockSupportContext.java
index 9ded289..7cff82f 100644
--- a/lock-api/src/main/java/com/github/houbb/lock/api/core/ILockSupportContext.java
+++ b/lock-api/src/main/java/com/github/houbb/lock/api/core/ILockSupportContext.java
@@ -20,7 +20,7 @@ public interface ILockSupportContext {
* @return 缓存策略
* @since 0.0.4
*/
- ICommonCacheService commonCacheService();
+ ICommonCacheService cache();
/**
* 锁的过期时间
diff --git a/lock-core/src/main/java/com/github/houbb/lock/core/bs/LockBs.java b/lock-core/src/main/java/com/github/houbb/lock/core/bs/LockBs.java
index f174ca3..862aec6 100644
--- a/lock-core/src/main/java/com/github/houbb/lock/core/bs/LockBs.java
+++ b/lock-core/src/main/java/com/github/houbb/lock/core/bs/LockBs.java
@@ -44,7 +44,7 @@ public final class LockBs implements ILock{
* 缓存策略
* @since 0.0.4
*/
- private ICommonCacheService commonCacheService = JedisRedisServiceFactory.simple("127.0.0.1", 6379);
+ private ICommonCacheService cache = JedisRedisServiceFactory.pooled("127.0.0.1", 6379);
/**
* 锁支持策略
@@ -70,10 +70,10 @@ public final class LockBs implements ILock{
return this;
}
- public LockBs commonCacheService(ICommonCacheService commonCacheService) {
- ArgUtil.notNull(commonCacheService, "commonCacheService");
+ public LockBs cache(ICommonCacheService cache) {
+ ArgUtil.notNull(cache, "cache");
- this.commonCacheService = commonCacheService;
+ this.cache = cache;
return this;
}
@@ -91,7 +91,7 @@ public final class LockBs implements ILock{
public LockBs init() {
this.lockSupportContext = LockSupportContext.newInstance()
.id(id)
- .commonCacheService(commonCacheService)
+ .cache(cache)
.lockExpireMills(lockExpireMills);
return this;
diff --git a/lock-core/src/main/java/com/github/houbb/lock/core/support/lock/LockSupportContext.java b/lock-core/src/main/java/com/github/houbb/lock/core/support/lock/LockSupportContext.java
index c953e31..75145d3 100644
--- a/lock-core/src/main/java/com/github/houbb/lock/core/support/lock/LockSupportContext.java
+++ b/lock-core/src/main/java/com/github/houbb/lock/core/support/lock/LockSupportContext.java
@@ -44,11 +44,11 @@ public class LockSupportContext implements ILockSupportContext {
}
@Override
- public ICommonCacheService commonCacheService() {
+ public ICommonCacheService cache() {
return commonCacheService;
}
- public LockSupportContext commonCacheService(ICommonCacheService commonCacheService) {
+ public LockSupportContext cache(ICommonCacheService commonCacheService) {
this.commonCacheService = commonCacheService;
return this;
}
diff --git a/lock-core/src/main/java/com/github/houbb/lock/core/support/lock/RedisLockSupport.java b/lock-core/src/main/java/com/github/houbb/lock/core/support/lock/RedisLockSupport.java
index 5b6f0ac..89c4db8 100644
--- a/lock-core/src/main/java/com/github/houbb/lock/core/support/lock/RedisLockSupport.java
+++ b/lock-core/src/main/java/com/github/houbb/lock/core/support/lock/RedisLockSupport.java
@@ -65,7 +65,7 @@ public class RedisLockSupport implements ILockSupport {
IdThreadLocalHelper.put(requestId);
log.info("开始尝试获取锁 requestId: {}", requestId);
- final ICommonCacheService commonCacheService = context.commonCacheService();
+ final ICommonCacheService commonCacheService = context.cache();
final int lockExpireMills = context.lockExpireMills();
@@ -79,7 +79,7 @@ public class RedisLockSupport implements ILockSupport {
String requestId = IdThreadLocalHelper.get();
log.info("开始尝试释放锁 requestId: {}", requestId);
- final ICommonCacheService commonCacheService = context.commonCacheService();
+ final ICommonCacheService commonCacheService = context.cache();
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
Object result = commonCacheService.eval(script, Collections.singletonList(key), Collections.singletonList(requestId));
return JedisConst.RELEASE_SUCCESS.equals(result);
diff --git a/lock-spring/pom.xml b/lock-spring/pom.xml
new file mode 100644
index 0000000..b21886e
--- /dev/null
+++ b/lock-spring/pom.xml
@@ -0,0 +1,30 @@
+
+
+
+ lock
+ com.github.houbb
+ 1.0.0
+
+ 4.0.0
+
+ lock-spring
+
+
+
+ com.github.houbb
+ lock-core
+
+
+
+ com.github.houbb
+ aop-core
+
+
+ com.github.houbb
+ aop-spring
+
+
+
+
\ No newline at end of file
diff --git a/lock-spring/src/main/java/com/github/houbb/lock/spring/annotation/EnableLock.java b/lock-spring/src/main/java/com/github/houbb/lock/spring/annotation/EnableLock.java
new file mode 100644
index 0000000..82d8b3c
--- /dev/null
+++ b/lock-spring/src/main/java/com/github/houbb/lock/spring/annotation/EnableLock.java
@@ -0,0 +1,43 @@
+package com.github.houbb.lock.spring.annotation;
+
+import com.github.houbb.lock.spring.config.LockAopConfig;
+import org.springframework.context.annotation.EnableAspectJAutoProxy;
+import org.springframework.context.annotation.Import;
+
+import java.lang.annotation.*;
+
+/**
+ * 启用注解
+ * @author binbin.hou
+ * @since 0.0.2
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Import(LockAopConfig.class)
+@EnableAspectJAutoProxy
+public @interface EnableLock {
+
+ /**
+ * 加锁过期时间
+ * @return 时间
+ * @since 1.1.0
+ */
+ int lockExpireMills() default 60 * 1000;
+
+ /**
+ * 唯一标识生成策略
+ * @return 结果
+ * @since 1.1.0
+ */
+ String id() default "lockId";
+
+ /**
+ * 缓存实现策略 bean 名称
+ * @return 实现
+ * @since 1.1.0
+ */
+ String cache() default "lockCache";
+
+
+}
diff --git a/lock-spring/src/main/java/com/github/houbb/lock/spring/annotation/Lock.java b/lock-spring/src/main/java/com/github/houbb/lock/spring/annotation/Lock.java
new file mode 100644
index 0000000..ac00652
--- /dev/null
+++ b/lock-spring/src/main/java/com/github/houbb/lock/spring/annotation/Lock.java
@@ -0,0 +1,28 @@
+package com.github.houbb.lock.spring.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * 分布式加锁注解
+ * @author binbin.hou
+ * @since 0.0.2
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Inherited
+public @interface Lock {
+
+ /**
+ * 缓存的 key 策略,支持 SpEL
+ * @return 结果
+ */
+ String value() default "";
+
+ /**
+ * 尝试获取锁等待时间
+ * @return 结果
+ */
+ long tryLockMills() default 10 * 1000;
+
+}
diff --git a/lock-spring/src/main/java/com/github/houbb/lock/spring/aop/LockAspect.java b/lock-spring/src/main/java/com/github/houbb/lock/spring/aop/LockAspect.java
new file mode 100644
index 0000000..0ca7da6
--- /dev/null
+++ b/lock-spring/src/main/java/com/github/houbb/lock/spring/aop/LockAspect.java
@@ -0,0 +1,141 @@
+package com.github.houbb.lock.spring.aop;
+
+import com.github.houbb.aop.spring.util.SpringAopUtil;
+import com.github.houbb.heaven.util.lang.ObjectUtil;
+import com.github.houbb.heaven.util.lang.StringUtil;
+import com.github.houbb.heaven.util.lang.reflect.ReflectMethodUtil;
+import com.github.houbb.heaven.util.util.ArrayUtil;
+import com.github.houbb.lock.api.exception.LockException;
+import com.github.houbb.lock.core.bs.LockBs;
+import com.github.houbb.lock.spring.annotation.Lock;
+import com.github.houbb.log.integration.core.Log;
+import com.github.houbb.log.integration.core.LogFactory;
+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.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.expression.EvaluationContext;
+import org.springframework.expression.ExpressionParser;
+import org.springframework.expression.spel.standard.SpelExpressionParser;
+import org.springframework.expression.spel.support.StandardEvaluationContext;
+import org.springframework.stereotype.Component;
+
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @author binbin.hou
+ * @since 1.1.0
+ */
+@Aspect
+@Component
+public class LockAspect {
+
+ private final Log log = LogFactory.getLog(LockAspect.class);
+
+ @Autowired
+ @Qualifier("lockBs")
+ private LockBs lockBs;
+
+ @Pointcut("@annotation(com.github.houbb.lock.spring.annotation.Lock)")
+ public void lockPointcut() {
+ }
+
+ /**
+ * 执行核心方法
+ *
+ * 相当于 MethodInterceptor
+ * @param point 切点
+ * @return 结果
+ * @throws Throwable 异常信息
+ * @since 0.0.2
+ */
+ @Around("lockPointcut()")
+ public Object around(ProceedingJoinPoint point) throws Throwable {
+ Method method = SpringAopUtil.getCurrentMethod(point);
+
+ boolean isLockAnnotation = method.isAnnotationPresent(Lock.class);
+ if(isLockAnnotation) {
+ Lock lock = method.getAnnotation(Lock.class);
+
+ // 如果构建 key?
+ Object[] args = point.getArgs();
+ String lockKey = buildLockKey(lock, method, args);
+ try {
+ long tryLockMills = lock.tryLockMills();
+
+ boolean tryLockFlag = lockBs.tryLock(tryLockMills, TimeUnit.MILLISECONDS, lockKey);
+ if(!tryLockFlag) {
+ log.warn("尝试获取锁失败 {}", lockKey);
+ throw new LockException("尝试获取锁失败 " + lockKey);
+ }
+
+ // 执行业务
+ return point.proceed();
+ } catch (Throwable e) {
+ throw new RuntimeException(e);
+ } finally {
+ boolean unLockFlag = lockBs.unlock(lockKey);
+ if(!unLockFlag) {
+ log.warn("尝试释放锁失败 {}", lockKey);
+ // 这里释放异常,没有意义。
+ }
+ }
+ } else {
+ return point.proceed();
+ }
+ }
+
+ /**
+ * 构建加锁的 key
+ *
+ * https://www.cnblogs.com/fashflying/p/6908028.html spring cache
+ *
+ * https://www.cnblogs.com/best/p/5748105.html SpEL
+ * @param lock 注解信息
+ * @param args 参数
+ * @return 结果
+ */
+ private String buildLockKey(Lock lock,
+ Method method,
+ Object[] args) {
+ final String lockValue = lock.value();
+
+ //创建SpEL表达式的解析器
+ ExpressionParser parser = new SpelExpressionParser();
+ //1. 如果没有入参怎么办?
+ if(ArrayUtil.isEmpty(args)) {
+ log.warn("对应的数组信息为空,直接返回 key 的值 {}", lockValue);
+ return lockValue;
+ }
+
+ // 如果 lockValue 的值为空呢?
+ if(StringUtil.isEmpty(lockValue)) {
+ return Arrays.toString(args);
+ }
+
+ // 如何获取参数名称呢?
+
+ //解析表达式需要的上下文,解析时有一个默认的上下文
+ // jdk1.7 之前,直接使用 arg0, arg1...
+ EvaluationContext ctx = new StandardEvaluationContext();
+ List paramNameList = ReflectMethodUtil.getParamNames(method);
+
+ for(int i = 0; i < paramNameList.size(); i++) {
+ String paramName = paramNameList.get(i);
+ Object paramValue = args[i];
+
+ //在上下文中设置变量,变量名为user,内容为user对象
+ ctx.setVariable(paramName, paramValue);
+ }
+
+ // 直接 toString,比较简单。
+ Object value = parser.parseExpression(lockValue).getValue(ctx);
+ return ObjectUtil.objectToString(value, "null");
+ }
+
+}
diff --git a/lock-spring/src/main/java/com/github/houbb/lock/spring/config/LockAopConfig.java b/lock-spring/src/main/java/com/github/houbb/lock/spring/config/LockAopConfig.java
new file mode 100644
index 0000000..eea50d9
--- /dev/null
+++ b/lock-spring/src/main/java/com/github/houbb/lock/spring/config/LockAopConfig.java
@@ -0,0 +1,69 @@
+package com.github.houbb.lock.spring.config;
+
+import com.github.houbb.common.cache.api.service.ICommonCacheService;
+import com.github.houbb.heaven.util.lang.StringUtil;
+import com.github.houbb.id.api.Id;
+import com.github.houbb.id.core.core.Ids;
+import com.github.houbb.lock.core.bs.LockBs;
+import com.github.houbb.lock.spring.annotation.EnableLock;
+import com.github.houbb.redis.config.core.factory.JedisRedisServiceFactory;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.context.annotation.*;
+import org.springframework.core.annotation.AnnotationAttributes;
+import org.springframework.core.type.AnnotationMetadata;
+
+/**
+ * aop 配置
+ *
+ * @author binbin.hou
+ * @since 0.0.2
+ */
+@Configuration
+@ComponentScan(basePackages = "com.github.houbb.lock.spring")
+@Import(LockBeanConfig.class)
+public class LockAopConfig implements ImportAware, BeanFactoryPostProcessor {
+
+ @Bean("lockBs")
+ public LockBs lockBs() {
+ int lockExpireMills = (int) enableLockAttributes.get("lockExpireMills");
+ ICommonCacheService commonCacheService = beanFactory.getBean(enableLockAttributes.getString("cache"), ICommonCacheService.class);
+ Id id = beanFactory.getBean(enableLockAttributes.getString("id"), Id.class);
+
+ return LockBs.newInstance()
+ .cache(commonCacheService)
+ .id(id)
+ .lockExpireMills(lockExpireMills)
+ .init();
+ }
+
+ /**
+ * 属性信息
+ */
+ private AnnotationAttributes enableLockAttributes;
+
+ /**
+ * bean 工厂
+ *
+ * @since 0.0.5
+ */
+ private ConfigurableListableBeanFactory beanFactory;
+
+ @Override
+ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
+ this.beanFactory = beanFactory;
+ }
+
+ @Override
+ public void setImportMetadata(AnnotationMetadata annotationMetadata) {
+ enableLockAttributes = AnnotationAttributes.fromMap(
+ annotationMetadata.getAnnotationAttributes(EnableLock.class.getName(), false));
+ if (enableLockAttributes == null) {
+ throw new IllegalArgumentException(
+ "@EnableLock is not present on importing class " + annotationMetadata.getClassName());
+ }
+ }
+
+}
diff --git a/lock-spring/src/main/java/com/github/houbb/lock/spring/config/LockBeanConfig.java b/lock-spring/src/main/java/com/github/houbb/lock/spring/config/LockBeanConfig.java
new file mode 100644
index 0000000..b7b3e91
--- /dev/null
+++ b/lock-spring/src/main/java/com/github/houbb/lock/spring/config/LockBeanConfig.java
@@ -0,0 +1,46 @@
+package com.github.houbb.lock.spring.config;
+
+import com.github.houbb.common.cache.api.service.ICommonCacheService;
+import com.github.houbb.heaven.util.lang.StringUtil;
+import com.github.houbb.id.api.Id;
+import com.github.houbb.id.core.core.Ids;
+import com.github.houbb.redis.config.core.factory.JedisRedisServiceFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * bean 配置
+ *
+ * @author binbin.hou
+ * @since 0.0.2
+ */
+@Configuration
+@ComponentScan(basePackages = "com.github.houbb.lock.spring")
+public class LockBeanConfig {
+
+ @Value("${redis.address:127.0.0.1}")
+ private String redisAddress;
+
+ @Value("${redis.port:6379}")
+ private int redisPort;
+
+ @Value("${redis.password:}")
+ private String redisPassword;
+
+ @Bean("lockCache")
+ public ICommonCacheService lockCache() {
+ if(StringUtil.isNotEmpty(redisPassword)) {
+ return JedisRedisServiceFactory.pooled(redisAddress, redisPort, redisPassword);
+ }
+
+ return JedisRedisServiceFactory.simple(redisAddress, redisPort);
+ }
+
+ @Bean("lockId")
+ public Id lockId() {
+ return Ids.uuid32();
+ }
+
+}
diff --git a/lock-spring/src/main/java/com/github/houbb/lock/spring/package-info.java b/lock-spring/src/main/java/com/github/houbb/lock/spring/package-info.java
new file mode 100644
index 0000000..4bcc69d
--- /dev/null
+++ b/lock-spring/src/main/java/com/github/houbb/lock/spring/package-info.java
@@ -0,0 +1 @@
+package com.github.houbb.lock.spring;
\ No newline at end of file
diff --git a/lock-test/pom.xml b/lock-test/pom.xml
index 49d9eec..f4ad931 100644
--- a/lock-test/pom.xml
+++ b/lock-test/pom.xml
@@ -16,11 +16,20 @@
com.github.houbb
lock-core
+
+ com.github.houbb
+ lock-spring
+
junit
junit
+
+
+ com.github.houbb
+ test-spring
+
diff --git a/lock-test/src/main/java/com/github/houbb/lock/test/config/SpringConfig.java b/lock-test/src/main/java/com/github/houbb/lock/test/config/SpringConfig.java
new file mode 100644
index 0000000..5001678
--- /dev/null
+++ b/lock-test/src/main/java/com/github/houbb/lock/test/config/SpringConfig.java
@@ -0,0 +1,16 @@
+package com.github.houbb.lock.test.config;
+
+
+import com.github.houbb.lock.spring.annotation.EnableLock;
+import org.springframework.beans.factory.annotation.Configurable;
+import org.springframework.context.annotation.ComponentScan;
+
+/**
+ * @author binbin.hou
+ * @since 1.0.0
+ */
+@Configurable
+@ComponentScan(basePackages = "com.github.houbb.lock.test.service")
+@EnableLock
+public class SpringConfig {
+}
diff --git a/lock-test/src/main/java/com/github/houbb/lock/test/model/User.java b/lock-test/src/main/java/com/github/houbb/lock/test/model/User.java
new file mode 100644
index 0000000..5092d57
--- /dev/null
+++ b/lock-test/src/main/java/com/github/houbb/lock/test/model/User.java
@@ -0,0 +1,33 @@
+package com.github.houbb.lock.test.model;
+
+public class User {
+
+ private String name;
+
+ private String id;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ @Override
+ public String toString() {
+ return "User{" +
+ "name='" + name + '\'' +
+ ", id='" + id + '\'' +
+ '}';
+ }
+
+}
diff --git a/lock-test/src/main/java/com/github/houbb/lock/test/service/UserService.java b/lock-test/src/main/java/com/github/houbb/lock/test/service/UserService.java
new file mode 100644
index 0000000..8cd3225
--- /dev/null
+++ b/lock-test/src/main/java/com/github/houbb/lock/test/service/UserService.java
@@ -0,0 +1,23 @@
+package com.github.houbb.lock.test.service;
+
+import com.github.houbb.lock.spring.annotation.Lock;
+import com.github.houbb.lock.test.model.User;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author binbin.hou
+ * @since 1.0.0
+ */
+@Service
+public class UserService {
+
+ @Lock
+ public String queryUserName(Long userId) {
+ return userId+"-name";
+ }
+
+ @Lock(value = "#arg0.name")
+ public void queryUserName2(User user) {
+ System.out.println("user: " + user.toString());
+ }
+}
diff --git a/lock-test/src/test/java/com/github/houbb/lock/test/spring/SpringServiceTest.java b/lock-test/src/test/java/com/github/houbb/lock/test/spring/SpringServiceTest.java
new file mode 100644
index 0000000..53b80bd
--- /dev/null
+++ b/lock-test/src/test/java/com/github/houbb/lock/test/spring/SpringServiceTest.java
@@ -0,0 +1,38 @@
+package com.github.houbb.lock.test.spring;
+
+
+import com.github.houbb.lock.test.config.SpringConfig;
+import com.github.houbb.lock.test.model.User;
+import com.github.houbb.lock.test.service.UserService;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+/**
+ * @author binbin.hou
+ * @since 1.0.0
+ */
+@ContextConfiguration(classes = SpringConfig.class)
+@RunWith(SpringJUnit4ClassRunner.class)
+public class SpringServiceTest {
+
+ @Autowired
+ private UserService userService;
+
+ @Test
+ public void queryLogTest() {
+ final String key = "name";
+ final String value = userService.queryUserName(1L);
+ }
+
+ @Test
+ public void queryUserNameTest() {
+ User user = new User();
+ user.setId("001");
+ user.setName("u-001");
+ userService.queryUserName2(user);
+ }
+
+}
diff --git a/pom.xml b/pom.xml
index 8cbe6f6..19178ad 100644
--- a/pom.xml
+++ b/pom.xml
@@ -10,6 +10,7 @@
lock-api
lock-core
lock-test
+ lock-spring
@@ -40,6 +41,8 @@
1.1.8
0.0.5
1.4.0
+ 0.0.2
+ 0.0.1
4.12
@@ -59,6 +62,12 @@
lock-core
${project.version}
+
+ com.github.houbb
+ lock-spring
+ ${project.version}
+
+
com.github.houbb
@@ -117,6 +126,24 @@
jedis
${jedis.version}
+
+
+ com.github.houbb
+ aop-core
+ ${aop.version}
+
+
+ com.github.houbb
+ aop-spring
+ ${aop.version}
+
+
+
+ com.github.houbb
+ test-spring
+ ${test.version}
+
+