From fa23a4415972d5fcc1589c91bbf0d37a47d8dd40 Mon Sep 17 00:00:00 2001 From: huangchengxing <841396397@qq.com> Date: Mon, 22 Apr 2024 23:55:35 +0800 Subject: [PATCH] =?UTF-8?q?doc:=20=E4=BC=98=E5=8C=96=E6=B3=A8=E9=87=8A?= =?UTF-8?q?=EF=BC=8C=E8=B0=83=E6=95=B4=E5=8F=82=E6=95=B0=E5=90=8D=E7=A7=B0?= =?UTF-8?q?=E4=B8=8E=E6=96=B9=E6=B3=95=E5=90=8D=E7=A7=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../baomidou/lock/LockFailureStrategy.java | 1 + .../com/baomidou/lock/LockKeyBuilder.java | 4 +- .../baomidou/lock/annotation/LocalLock.java | 98 +++++++--- .../com/baomidou/lock/annotation/Lock4j.java | 178 +++++++++++++++--- .../lock/annotation/LockWithDefault.java | 91 ++++++--- .../baomidou/lock/aop/LockOpsInterceptor.java | 1 + .../lock/executor/LocalLockExecutor.java | 2 + .../baomidou/lock/executor/LockExecutor.java | 21 ++- .../baomidou/lock/annotation/RedisLock.java | 99 +++++++--- .../executor/RedisTemplateLockExecutor.java | 4 +- .../lock/annotation/RedissonLock.java | 98 +++++++--- .../lock/executor/RedissonLockExecutor.java | 4 +- .../lock/annotation/ZookeeperLock.java | 108 ++++++++--- .../lock/executor/ZookeeperLockExecutor.java | 2 + 14 files changed, 549 insertions(+), 162 deletions(-) diff --git a/lock4j-core/src/main/java/com/baomidou/lock/LockFailureStrategy.java b/lock4j-core/src/main/java/com/baomidou/lock/LockFailureStrategy.java index e2e5d21..9b15241 100644 --- a/lock4j-core/src/main/java/com/baomidou/lock/LockFailureStrategy.java +++ b/lock4j-core/src/main/java/com/baomidou/lock/LockFailureStrategy.java @@ -33,6 +33,7 @@ public interface LockFailureStrategy { * @param arguments 方法参数 * @throws Exception 处理过程中可能抛出的异常,如果抛出异常则会终止方法执行 */ + @SuppressWarnings("java:S112") void onLockFailure(String key, Method method, Object[] arguments) throws Exception; // TODO 释放锁失败时也应该进行处理,具体参见:https://gitee.com/baomidou/lock4j/issues/I4LG1U diff --git a/lock4j-core/src/main/java/com/baomidou/lock/LockKeyBuilder.java b/lock4j-core/src/main/java/com/baomidou/lock/LockKeyBuilder.java index 031e069..6299b5c 100644 --- a/lock4j-core/src/main/java/com/baomidou/lock/LockKeyBuilder.java +++ b/lock4j-core/src/main/java/com/baomidou/lock/LockKeyBuilder.java @@ -19,6 +19,8 @@ package com.baomidou.lock; import org.aopalliance.intercept.MethodInvocation; /** + * 用于加锁的key生成器 + * * @author zengzhihong */ public interface LockKeyBuilder { @@ -26,7 +28,7 @@ public interface LockKeyBuilder { /** * 构建key * - * @param invocation invocation + * @param invocation 方法调用 * @param definitionKeys 定义 * @return key */ diff --git a/lock4j-core/src/main/java/com/baomidou/lock/annotation/LocalLock.java b/lock4j-core/src/main/java/com/baomidou/lock/annotation/LocalLock.java index 734cc98..02e3298 100644 --- a/lock4j-core/src/main/java/com/baomidou/lock/annotation/LocalLock.java +++ b/lock4j-core/src/main/java/com/baomidou/lock/annotation/LocalLock.java @@ -3,8 +3,10 @@ package com.baomidou.lock.annotation; import com.baomidou.lock.LockFailureStrategy; import com.baomidou.lock.LockKeyBuilder; import com.baomidou.lock.executor.LocalLockExecutor; +import com.baomidou.lock.executor.LockExecutor; import com.baomidou.lock.spring.boot.autoconfigure.Lock4jProperties; import org.springframework.core.Ordered; +import org.springframework.core.annotation.AliasFor; import java.lang.annotation.*; @@ -20,65 +22,111 @@ import java.lang.annotation.*; public @interface LocalLock { /** - * 应用条件表达式,当执行结果为{@code true}或{@code 'true'}时,才会执行锁操作 + *

应用条件表达式,当执行结果为{@code true}或{@code 'true'}时,才会执行锁操作。 * - * @return 名称 + *

你可以在表达式中引用上下文参数: + *

+ * 并且,与{@link org.springframework.beans.factory.annotation.Value @Value}注解一样, + * 支持通过{@code "${}"}引用配置文件中的属性。 + * + * @return 条件表达式 + * @see com.baomidou.lock.MethodBasedExpressionEvaluator + * @see com.baomidou.lock.aop.AbstractConditionalLockInterceptor */ String condition() default ""; /** - * 用于多个方法锁同一把锁 可以理解为锁资源名称 为空则会使用 包名+类名+方法名 + *

key前缀,它将会被拼接到根据{@link #keys()}属性生成的key前面, + * 当为空时,默认为当前加锁方法的{@code "声明类全限定名#方法名称"}。 + * + * @return key前缀 + */ + @AliasFor("name") + String prefix() default ""; + + /** + *

key前缀,它将会被拼接到根据{@link #keys()}属性生成的key前面, + * 当为空时,默认为当前加锁方法的{@code "声明类全限定名#方法名称"}。 * - * @return 名称 + * @return key前缀 */ + @AliasFor("prefix") String name() default ""; /** - * support SPEL expresion 锁的key = name + keys + * 要用于加锁的key,执行时, + * 将通过{@link #keyBuilderStrategy()}指定的策略生成最终的key。 * - * @return KEY + * @return 要加锁的key + * @see #keyBuilderStrategy() + * @see LockKeyBuilder */ String[] keys() default ""; /** - * @return 过期时间 单位:毫秒 - *

-     *     过期时间一定是要长于业务的执行时间. 未设置则为默认时间30秒 默认值:{@link Lock4jProperties#expire}
-     * 
+ *

要使用的{@link LockKeyBuilder key生成器}类型, + * 默认优先使用{@link Lock4jProperties#getPrimaryKeyBuilder()} Lck4jProperties.primaryKeyBuilder}所指定的执行器。 + * + * @return 生成器类型 + * @see LockKeyBuilder */ - long expire() default -1; + Class keyBuilderStrategy() default LockKeyBuilder.class; /** - * @return 获取锁超时时间 单位:毫秒 - *

-     *     结合业务,建议该时间不宜设置过长,特别在并发高的情况下. 未设置则为默认时间3秒 默认值:{@link Lock4jProperties#acquireTimeout}
-     * 
+ *

获取锁超时时间,单位为毫秒。 + *

+ * 当获取锁失败时,将会根据{@link #failStrategy()}指定的策略进行处理。 + * + *

注意:并发较高的情况下该值不易设置过大,请结合业务设置一个合理的等待时间。 + * + * @return 获取锁超时时间 + * @see #failStrategy() */ long acquireTimeout() default -1; /** - * 业务方法执行完后(方法内抛异常也算执行完)自动释放锁,如果为false,锁将不会自动释放直至到达过期时间才释放 {@link com.baomidou.lock.annotation.Lock4j#expire()} + *

锁的过期时间,单位为毫秒。 * - * @return 是否自动释放锁 + *

当该值小于等于0时,若所使用的{@link #executor() 执行器}支持自动续期, + * 则在业务执行期间将会自动续期,直至业务执行完毕后才会释放锁。
+ * 否则会使用{@link Lock4jProperties#getExpire() Lck4jProperties.expire}指定的过期时间。 + * + *

注意:过期时间请务必保证长于业务的执行时间,未在配置文件中指定超时时间时,则默认为30s。 + * + * @return 锁的过期时间 + * @see LockExecutor#supportRenewal() + * @see #autoRelease() */ - boolean autoRelease() default true; + long expire() default -1; /** - * 失败策略 + *

在方法执行完后是否自动释放锁, + * 当设置为{@code false}时,锁将不会自动释放,直至到达过期时间才会释放。 * - * @return LockFailureStrategy + * @return 是否自动释放锁 */ - Class failStrategy() default LockFailureStrategy.class; + boolean autoRelease() default true; /** - * key生成器策略 + *

要使用的{@link LockFailureStrategy 当获取锁失败时的处理策略}类型, + * 默认优先使用{@link Lock4jProperties#getPrimaryFailureStrategy()} Lck4jProperties.primaryFailureStrategy}所指定的执行器。 * - * @return LockKeyBuilder + * @return 失败策略类型 + * @see LockFailureStrategy */ - Class keyBuilderStrategy() default LockKeyBuilder.class; + Class failStrategy() default LockFailureStrategy.class; /** - * 获取顺序,值越小越先执行 + *

当存在多个{@link Lock4j}注解时, + * 将会根据该值进行排序,顺序值越小越先执行。 * * @return 顺序值 */ diff --git a/lock4j-core/src/main/java/com/baomidou/lock/annotation/Lock4j.java b/lock4j-core/src/main/java/com/baomidou/lock/annotation/Lock4j.java index dfca5fc..b3648bc 100644 --- a/lock4j-core/src/main/java/com/baomidou/lock/annotation/Lock4j.java +++ b/lock4j-core/src/main/java/com/baomidou/lock/annotation/Lock4j.java @@ -21,13 +21,80 @@ import com.baomidou.lock.LockKeyBuilder; import com.baomidou.lock.executor.LockExecutor; import com.baomidou.lock.spring.boot.autoconfigure.Lock4jProperties; import org.springframework.core.Ordered; +import org.springframework.core.annotation.AliasFor; +import org.springframework.core.annotation.MergedAnnotation; import java.lang.annotation.*; /** - * 分布式锁注解 + *

AOP注解,标明注解方法在执行前需要获取锁。 * - * @author zengzhihong TaoYu + *

使用

+ * 你可以在需要加锁的方法上添加注解,并指定等待时间与超时时间: + *
{@code
+ *  @Lock4j(prefix = "example", key = "lock:global", acquireTimeout = 10L, expire = 20L)
+ *  void runWithLock();
+ * }
+ * 在上述例子中,将会获取一个名为{@code "example:lock:global"}的锁,等待时间为10ms,超时时间为20ms。 + * + *

条件

+ * 你可以通过在{@link #condition()}指定SpEL表达式,等到执行时再动态判断是否确实要获取锁: + *
{@code
+ *  @Lock4j(key = "lock:global", condition = "#ids =! null && !#ids.isEmpty()")
+ *  void runWithLock(Collection ids);
+ * }
+ * 在上述例子中,仅当入参参数{@code ids}不为空时才会加锁。 + * + *

多级锁

+ * 你可以通过在类或方法上同时添加多个组件,以实现多级锁的效果: + *
{@code
+ * @Lock4j(key = {"#id", "':three'"}, order = 3)
+ * @Lock4j(key = {"#id", "':two'"}, order = 2)
+ * @Lock4j(key = {"#id", "':one'"}, order = 1)
+ * void runWithLock(Integer id);
+ * }
+ * 在上述例子中,将会依次获取三把锁,等到执行完毕后,将会再依次释放。 + * + *

组件

+ * 在注解中,可以调整三个关键组件: + * + * 你可以指定组件以便获得不同的加锁效果,比如: + *
{@code
+ *  @Lock4j(
+ *   key = "'lock:' + #{id}",
+ *   keyBuilderStrategy = DefaultKeyBuilderStrategy.class, // 基于 SpEL 表达式生成 key
+ *   executor = RedissonLockExecutor.clas, // 基于 Redisson 加锁
+ *   failStrategy = AbortLockFailureStrategy.class, // 加锁失败后,直接抛出AppException
+ *   acquireTimeout = 10L, expire = 20L
+ *  )
+ *  void runWithLock(Integer id);
+ * }
+ * 若要实现自己的组件,则实现接口后将实现类托管到Spring容器后,即可在注解中引用。 + * + *

扩展注解

+ * 基于Spring的{@link MergedAnnotation},你可以基于{@link Lock4j}注解定义扩展注解,以便简化配置,比如: + *
{@code
+ *  @Lock4j(keys ="global:lock") // 将@Lock4j作为元注解
+ *  @Documented
+ *  @Target(ElementType.METHOD)
+ *  @Retention(RetentionPolicy.RUNTIME)
+ *  public @interface GlobalLocked {}
+ * }
+ * 当使用扩展注解{@code @GlobalLocked} 时,其等效于{@code @Lock4j(keys ="global:lock")}。
+ * 具体可以参考{@link LockWithDefault},它就是一个扩展注解。 + * + * @author zengzhihong + * @author TaoYu + * @author huangchengxing + * @see LockKeyBuilder + * @see LockExecutor + * @see LockFailureStrategy + * @see com.baomidou.lock.aop.LockAnnotationAdvisor + * @see com.baomidou.lock.aop.LockOpsInterceptor */ @Repeatable(Lock4j.List.class) @Target(value = {ElementType.METHOD, ElementType.ANNOTATION_TYPE}) @@ -37,75 +104,130 @@ import java.lang.annotation.*; public @interface Lock4j { /** - * 应用条件表达式,当执行结果为{@code true}或{@code 'true'}时,才会执行锁操作 + *

应用条件表达式,当执行结果为{@code true}或{@code 'true'}时,才会执行锁操作。 + * + *

你可以在表达式中引用上下文参数: + *

+ * 并且,与{@link org.springframework.beans.factory.annotation.Value @Value}注解一样, + * 支持通过{@code "${}"}引用配置文件中的属性。 * - * @return 名称 + * @return 条件表达式 + * @see com.baomidou.lock.MethodBasedExpressionEvaluator + * @see com.baomidou.lock.aop.AbstractConditionalLockInterceptor */ String condition() default ""; /** - * 用于多个方法锁同一把锁 可以理解为锁资源名称 为空则会使用 包名+类名+方法名 + *

key前缀,它将会被拼接到根据{@link #keys()}属性生成的key前面, + * 当为空时,默认为当前加锁方法的{@code "声明类全限定名#方法名称"}。 * - * @return 名称 + * @return key前缀 */ + @AliasFor("name") + String prefix() default ""; + + /** + *

key前缀,它将会被拼接到根据{@link #keys()}属性生成的key前面, + * 当为空时,默认为当前加锁方法的{@code "声明类全限定名#方法名称"}。 + * + * @return key前缀 + */ + @AliasFor("prefix") String name() default ""; /** - * @return lock 执行器 + *

要使用的{@link LockExecutor 锁执行器}类型, + * 默认优先使用{@link Lock4jProperties#getPrimaryExecutor() Lck4jProperties.primaryExecutor}所指定的执行器, + * + * @return 执行器类型 + * @see LockExecutor */ Class executor() default LockExecutor.class; /** - * support SPEL expresion 锁的key = name + keys + * 要用于加锁的key,执行时, + * 将通过{@link #keyBuilderStrategy()}指定的策略生成最终的key。 * - * @return KEY + * @return 要加锁的key + * @see #keyBuilderStrategy() + * @see LockKeyBuilder */ String[] keys() default ""; /** - * @return 过期时间 单位:毫秒 - *

-     *     过期时间一定是要长于业务的执行时间. 未设置则为默认时间30秒 默认值:{@link Lock4jProperties#expire}
-     * 
+ *

要使用的{@link LockKeyBuilder key生成器}类型, + * 默认优先使用{@link Lock4jProperties#getPrimaryKeyBuilder()} Lck4jProperties.primaryKeyBuilder}所指定的执行器。 + * + * @return 生成器类型 + * @see LockKeyBuilder */ - long expire() default -1; + Class keyBuilderStrategy() default LockKeyBuilder.class; /** - * @return 获取锁超时时间 单位:毫秒 - *

-     *     结合业务,建议该时间不宜设置过长,特别在并发高的情况下. 未设置则为默认时间3秒 默认值:{@link Lock4jProperties#acquireTimeout}
-     * 
+ *

获取锁超时时间,单位为毫秒。 + *

+ * 当获取锁失败时,将会根据{@link #failStrategy()}指定的策略进行处理。 + * + *

注意:并发较高的情况下该值不易设置过大,请结合业务设置一个合理的等待时间。 + * + * @return 获取锁超时时间 + * @see #failStrategy() */ long acquireTimeout() default -1; /** - * 业务方法执行完后(方法内抛异常也算执行完)自动释放锁,如果为false,锁将不会自动释放直至到达过期时间才释放 {@link com.baomidou.lock.annotation.Lock4j#expire()} + *

锁的过期时间,单位为毫秒。 * - * @return 是否自动释放锁 + *

当该值小于等于0时,若所使用的{@link #executor() 执行器}支持自动续期, + * 则在业务执行期间将会自动续期,直至业务执行完毕后才会释放锁。
+ * 否则会使用{@link Lock4jProperties#getExpire() Lck4jProperties.expire}指定的过期时间。 + * + *

注意:过期时间请务必保证长于业务的执行时间,未在配置文件中指定超时时间时,则默认为30s。 + * + * @return 锁的过期时间 + * @see LockExecutor#supportRenewal() + * @see #autoRelease() */ - boolean autoRelease() default true; + long expire() default -1; /** - * 失败策略 + *

在方法执行完后是否自动释放锁, + * 当设置为{@code false}时,锁将不会自动释放,直至到达过期时间才会释放。 * - * @return LockFailureStrategy + * @return 是否自动释放锁 */ - Class failStrategy() default LockFailureStrategy.class; + boolean autoRelease() default true; /** - * key生成器策略 + *

要使用的{@link LockFailureStrategy 当获取锁失败时的处理策略}类型, + * 默认优先使用{@link Lock4jProperties#getPrimaryFailureStrategy()} Lck4jProperties.primaryFailureStrategy}所指定的执行器。 * - * @return LockKeyBuilder + * @return 失败策略类型 + * @see LockFailureStrategy */ - Class keyBuilderStrategy() default LockKeyBuilder.class; + Class failStrategy() default LockFailureStrategy.class; /** - * 获取顺序,值越小越先执行 + *

当存在多个{@link Lock4j}注解时, + * 将会根据该值进行排序,顺序值越小越先执行。 * * @return 顺序值 */ int order() default Ordered.LOWEST_PRECEDENCE; + /** + * 多级注解 + * + * @author huangchengxing + */ @Target(value = {ElementType.METHOD, ElementType.ANNOTATION_TYPE}) @Retention(value = RetentionPolicy.RUNTIME) @Inherited diff --git a/lock4j-core/src/main/java/com/baomidou/lock/annotation/LockWithDefault.java b/lock4j-core/src/main/java/com/baomidou/lock/annotation/LockWithDefault.java index 3ea368e..6ddacdb 100644 --- a/lock4j-core/src/main/java/com/baomidou/lock/annotation/LockWithDefault.java +++ b/lock4j-core/src/main/java/com/baomidou/lock/annotation/LockWithDefault.java @@ -27,63 +27,108 @@ import java.lang.annotation.*; public @interface LockWithDefault { /** - * 应用条件表达式,当执行结果为{@code true}或{@code 'true'}时,才会执行锁操作 + *

应用条件表达式,当执行结果为{@code true}或{@code 'true'}时,才会执行锁操作。 * - * @return 名称 + *

你可以在表达式中引用上下文参数: + *

+ * 并且,与{@link org.springframework.beans.factory.annotation.Value @Value}注解一样, + * 支持通过{@code "${}"}引用配置文件中的属性。 + * + * @return 条件表达式 + * @see com.baomidou.lock.MethodBasedExpressionEvaluator + * @see com.baomidou.lock.aop.AbstractConditionalLockInterceptor */ String condition() default ""; /** - * 用于多个方法锁同一把锁 可以理解为锁资源名称 为空则会使用 包名+类名+方法名 + *

key前缀,它将会被拼接到根据{@link #keys()}属性生成的key前面, + * 当为空时,默认为当前加锁方法的{@code "声明类全限定名#方法名称"}。 * - * @return 名称 + * @return key前缀 */ + @AliasFor("name") + String prefix() default ""; + + /** + *

key前缀,它将会被拼接到根据{@link #keys()}属性生成的key前面, + * 当为空时,默认为当前加锁方法的{@code "声明类全限定名#方法名称"}。 + * + * @return key前缀 + */ + @AliasFor("prefix") String name() default ""; /** - * @return lock 执行器 + *

要使用的{@link LockExecutor 锁执行器}类型, + * 默认优先使用{@link Lock4jProperties#getPrimaryExecutor() Lck4jProperties.primaryExecutor}所指定的执行器, + * + * @return 执行器类型 + * @see LockExecutor */ Class executor() default LockExecutor.class; /** - * support SPEL expresion 锁的key = name + keys + * 要用于加锁的key,执行时, + * 将通过{@link #keyBuilderStrategy()}指定的策略生成最终的key。 * - * @return KEY + * @return 要加锁的key + * @see #keyBuilderStrategy() + * @see LockKeyBuilder */ String[] keys() default ""; /** - * @return 过期时间 单位:毫秒 - *

-     *     过期时间一定是要长于业务的执行时间. 未设置则为默认时间30秒 默认值:{@link Lock4jProperties#expire}
-     * 
+ *

要使用的{@link LockKeyBuilder key生成器}类型, + * 默认优先使用{@link DefaultLockKeyBuilder}。 + * + * @return 生成器类型 */ - long expire() default -1; + Class keyBuilderStrategy() default DefaultLockKeyBuilder.class; /** - * @return 获取锁超时时间 单位:毫秒 - *

-     *     结合业务,建议该时间不宜设置过长,特别在并发高的情况下. 未设置则为默认时间3秒 默认值:{@link Lock4jProperties#acquireTimeout}
-     * 
+ *

获取锁超时时间,单位为毫秒。 + *

+ * + *

注意:并发较高的情况下该值不易设置过大,请结合业务设置一个合理的等待时间。 + * + * @return 获取锁超时时间 */ long acquireTimeout() default -1; /** - * 业务方法执行完后(方法内抛异常也算执行完)自动释放锁,如果为false,锁将不会自动释放直至到达过期时间才释放 {@link com.baomidou.lock.annotation.Lock4j#expire()} + *

锁的过期时间,单位为毫秒。 * - * @return 是否自动释放锁 + *

当该值小于等于0时,若所使用的{@link #executor() 执行器}支持自动续期, + * 则在业务执行期间将会自动续期,直至业务执行完毕后才会释放锁。
+ * 否则会使用{@link Lock4jProperties#getExpire() Lck4jProperties.expire}指定的过期时间。 + * + *

注意:过期时间请务必保证长于业务的执行时间,未在配置文件中指定超时时间时,则默认为30s。 + * + * @return 锁的过期时间 + * @see LockExecutor#supportRenewal() + * @see #autoRelease() */ - boolean autoRelease() default true; + long expire() default -1; /** - * key生成器策略,默认使用{@link DefaultLockKeyBuilder} + *

在方法执行完后是否自动释放锁, + * 当设置为{@code false}时,锁将不会自动释放,直至到达过期时间才会释放。 * - * @return LockKeyBuilder + * @return 是否自动释放锁 */ - Class keyBuilderStrategy() default DefaultLockKeyBuilder.class; + boolean autoRelease() default true; /** - * 获取顺序,值越小越先执行 + *

当存在多个{@link Lock4j}注解时, + * 将会根据该值进行排序,顺序值越小越先执行。 * * @return 顺序值 */ diff --git a/lock4j-core/src/main/java/com/baomidou/lock/aop/LockOpsInterceptor.java b/lock4j-core/src/main/java/com/baomidou/lock/aop/LockOpsInterceptor.java index f26e027..e8d9e7e 100644 --- a/lock4j-core/src/main/java/com/baomidou/lock/aop/LockOpsInterceptor.java +++ b/lock4j-core/src/main/java/com/baomidou/lock/aop/LockOpsInterceptor.java @@ -105,6 +105,7 @@ public class LockOpsInterceptor extends AbstractConditionalLockChainInterceptor String prefix = lock4jProperties.getLockKeyPrefix() + ":"; Method method = invocation.getMethod(); Lock4j annotation = lockOps.getAnnotation(); + // FIXME 此处无法区分重载的方法,应直接替换为method.toString() prefix += StringUtils.hasText(annotation.name()) ? annotation.name() : method.getDeclaringClass().getName() + method.getName(); String key = prefix + "#" + lockOps.getLockKeyBuilder().buildKey(invocation, annotation.keys()); diff --git a/lock4j-core/src/main/java/com/baomidou/lock/executor/LocalLockExecutor.java b/lock4j-core/src/main/java/com/baomidou/lock/executor/LocalLockExecutor.java index c64660c..c074278 100644 --- a/lock4j-core/src/main/java/com/baomidou/lock/executor/LocalLockExecutor.java +++ b/lock4j-core/src/main/java/com/baomidou/lock/executor/LocalLockExecutor.java @@ -4,6 +4,7 @@ import com.baomidou.lock.exception.LockException; import lombok.AllArgsConstructor; import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.lang.Nullable; import org.springframework.util.ConcurrentReferenceHashMap; import java.util.Objects; @@ -57,6 +58,7 @@ public class LocalLockExecutor extends AbstractLockExecutor 锁实例类型 + * @author zengzhihong + * @author TaoYu */ public interface LockExecutor { /** - * 续期,目前只有redisson支持,且expire参数为-1才会续期 + * 是否支持对锁进行续期 * - * @return 是否续期 + * @return 是否支持续期 */ - default boolean renewal() { + default boolean supportRenewal() { return false; } @@ -40,8 +44,9 @@ public interface LockExecutor { * @param lockValue 锁值 * @param expire 锁有效时间 * @param acquireTimeout 获取锁超时时间 - * @return 锁信息 + * @return 锁信息,当获取锁失败时返回{@code null} */ + @Nullable T acquire(String lockKey, String lockValue, long expire, long acquireTimeout); /** @@ -53,11 +58,11 @@ public interface LockExecutor { * 此时客户端B尝试加锁成功,然后客户端A再执行releaseLock方法,则将客户端B的锁给解除了。 * * - * @param key 加锁key + * @param lockKey 加锁key * @param value 加锁value * @param lockInstance 锁实例 * @return 是否释放成功 */ - boolean releaseLock(String key, String value, T lockInstance); + boolean releaseLock(String lockKey, String value, T lockInstance); } diff --git a/lock4j-redis-template-spring-boot-starter/src/main/java/com/baomidou/lock/annotation/RedisLock.java b/lock4j-redis-template-spring-boot-starter/src/main/java/com/baomidou/lock/annotation/RedisLock.java index af3a095..5a4547c 100644 --- a/lock4j-redis-template-spring-boot-starter/src/main/java/com/baomidou/lock/annotation/RedisLock.java +++ b/lock4j-redis-template-spring-boot-starter/src/main/java/com/baomidou/lock/annotation/RedisLock.java @@ -2,9 +2,11 @@ package com.baomidou.lock.annotation; import com.baomidou.lock.LockFailureStrategy; import com.baomidou.lock.LockKeyBuilder; +import com.baomidou.lock.executor.LockExecutor; import com.baomidou.lock.executor.RedisTemplateLockExecutor; import com.baomidou.lock.spring.boot.autoconfigure.Lock4jProperties; import org.springframework.core.Ordered; +import org.springframework.core.annotation.AliasFor; import java.lang.annotation.*; @@ -12,6 +14,7 @@ import java.lang.annotation.*; * 基于{@link org.springframework.data.redis.core.RedisTemplate}实现的分布式锁 * * @author huangchengxing + * @see Lock4j */ @Lock4j(executor = RedisTemplateLockExecutor.class) @Target(value = {ElementType.METHOD, ElementType.ANNOTATION_TYPE}) @@ -20,65 +23,111 @@ import java.lang.annotation.*; public @interface RedisLock { /** - * 应用条件表达式,当执行结果为{@code true}或{@code 'true'}时,才会执行锁操作 + *

应用条件表达式,当执行结果为{@code true}或{@code 'true'}时,才会执行锁操作。 * - * @return 名称 + *

你可以在表达式中引用上下文参数: + *

+ * 并且,与{@link org.springframework.beans.factory.annotation.Value @Value}注解一样, + * 支持通过{@code "${}"}引用配置文件中的属性。 + * + * @return 条件表达式 + * @see com.baomidou.lock.MethodBasedExpressionEvaluator + * @see com.baomidou.lock.aop.AbstractConditionalLockInterceptor */ String condition() default ""; /** - * 用于多个方法锁同一把锁 可以理解为锁资源名称 为空则会使用 包名+类名+方法名 + *

key前缀,它将会被拼接到根据{@link #keys()}属性生成的key前面, + * 当为空时,默认为当前加锁方法的{@code "声明类全限定名#方法名称"}。 + * + * @return key前缀 + */ + @AliasFor("name") + String prefix() default ""; + + /** + *

key前缀,它将会被拼接到根据{@link #keys()}属性生成的key前面, + * 当为空时,默认为当前加锁方法的{@code "声明类全限定名#方法名称"}。 * - * @return 名称 + * @return key前缀 */ + @AliasFor("prefix") String name() default ""; /** - * support SPEL expresion 锁的key = name + keys + * 要用于加锁的key,执行时, + * 将通过{@link #keyBuilderStrategy()}指定的策略生成最终的key。 * - * @return KEY + * @return 要加锁的key + * @see #keyBuilderStrategy() + * @see LockKeyBuilder */ String[] keys() default ""; /** - * @return 过期时间 单位:毫秒 - *

-     *     过期时间一定是要长于业务的执行时间. 未设置则为默认时间30秒 默认值:{@link Lock4jProperties#expire}
-     * 
+ *

要使用的{@link LockKeyBuilder key生成器}类型, + * 默认优先使用{@link Lock4jProperties#getPrimaryKeyBuilder()} Lck4jProperties.primaryKeyBuilder}所指定的执行器。 + * + * @return 生成器类型 + * @see LockKeyBuilder */ - long expire() default -1; + Class keyBuilderStrategy() default LockKeyBuilder.class; /** - * @return 获取锁超时时间 单位:毫秒 - *

-     *     结合业务,建议该时间不宜设置过长,特别在并发高的情况下. 未设置则为默认时间3秒 默认值:{@link Lock4jProperties#acquireTimeout}
-     * 
+ *

获取锁超时时间,单位为毫秒。 + *

+ * 当获取锁失败时,将会根据{@link #failStrategy()}指定的策略进行处理。 + * + *

注意:并发较高的情况下该值不易设置过大,请结合业务设置一个合理的等待时间。 + * + * @return 获取锁超时时间 + * @see #failStrategy() */ long acquireTimeout() default -1; /** - * 业务方法执行完后(方法内抛异常也算执行完)自动释放锁,如果为false,锁将不会自动释放直至到达过期时间才释放 {@link com.baomidou.lock.annotation.Lock4j#expire()} + *

锁的过期时间,单位为毫秒。 * - * @return 是否自动释放锁 + *

当该值小于等于0时,若所使用的{@link #executor() 执行器}支持自动续期, + * 则在业务执行期间将会自动续期,直至业务执行完毕后才会释放锁。
+ * 否则会使用{@link Lock4jProperties#getExpire() Lck4jProperties.expire}指定的过期时间。 + * + *

注意:过期时间请务必保证长于业务的执行时间,未在配置文件中指定超时时间时,则默认为30s。 + * + * @return 锁的过期时间 + * @see LockExecutor#supportRenewal() + * @see #autoRelease() */ - boolean autoRelease() default true; + long expire() default -1; /** - * 失败策略 + *

在方法执行完后是否自动释放锁, + * 当设置为{@code false}时,锁将不会自动释放,直至到达过期时间才会释放。 * - * @return LockFailureStrategy + * @return 是否自动释放锁 */ - Class failStrategy() default LockFailureStrategy.class; + boolean autoRelease() default true; /** - * key生成器策略 + *

要使用的{@link LockFailureStrategy 当获取锁失败时的处理策略}类型, + * 默认优先使用{@link Lock4jProperties#getPrimaryFailureStrategy()} Lck4jProperties.primaryFailureStrategy}所指定的执行器。 * - * @return LockKeyBuilder + * @return 失败策略类型 + * @see LockFailureStrategy */ - Class keyBuilderStrategy() default LockKeyBuilder.class; + Class failStrategy() default LockFailureStrategy.class; /** - * 获取顺序,值越小越先执行 + *

当存在多个{@link Lock4j}注解时, + * 将会根据该值进行排序,顺序值越小越先执行。 * * @return 顺序值 */ diff --git a/lock4j-redis-template-spring-boot-starter/src/main/java/com/baomidou/lock/executor/RedisTemplateLockExecutor.java b/lock4j-redis-template-spring-boot-starter/src/main/java/com/baomidou/lock/executor/RedisTemplateLockExecutor.java index 25a6c52..b1d9b02 100644 --- a/lock4j-redis-template-spring-boot-starter/src/main/java/com/baomidou/lock/executor/RedisTemplateLockExecutor.java +++ b/lock4j-redis-template-spring-boot-starter/src/main/java/com/baomidou/lock/executor/RedisTemplateLockExecutor.java @@ -23,6 +23,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.script.DefaultRedisScript; import org.springframework.data.redis.core.script.RedisScript; +import org.springframework.lang.Nullable; import java.util.Collections; import java.util.Timer; @@ -50,10 +51,11 @@ public class RedisTemplateLockExecutor extends AbstractLockExecutor { private final Lock4jProperties lock4jProperties; @Override - public boolean renewal() { + public boolean supportRenewal() { return true; } + @Nullable @Override public String acquire(String lockKey, String lockValue, long expire, long acquireTimeout) { diff --git a/lock4j-redisson-spring-boot-starter/src/main/java/com/baomidou/lock/annotation/RedissonLock.java b/lock4j-redisson-spring-boot-starter/src/main/java/com/baomidou/lock/annotation/RedissonLock.java index 89a95d7..9b490f4 100644 --- a/lock4j-redisson-spring-boot-starter/src/main/java/com/baomidou/lock/annotation/RedissonLock.java +++ b/lock4j-redisson-spring-boot-starter/src/main/java/com/baomidou/lock/annotation/RedissonLock.java @@ -2,9 +2,11 @@ package com.baomidou.lock.annotation; import com.baomidou.lock.LockFailureStrategy; import com.baomidou.lock.LockKeyBuilder; +import com.baomidou.lock.executor.LockExecutor; import com.baomidou.lock.executor.RedissonLockExecutor; import com.baomidou.lock.spring.boot.autoconfigure.Lock4jProperties; import org.springframework.core.Ordered; +import org.springframework.core.annotation.AliasFor; import java.lang.annotation.*; @@ -22,65 +24,111 @@ import java.lang.annotation.*; public @interface RedissonLock { /** - * 应用条件表达式,当执行结果为{@code true}或{@code 'true'}时,才会执行锁操作 + *

应用条件表达式,当执行结果为{@code true}或{@code 'true'}时,才会执行锁操作。 * - * @return 名称 + *

你可以在表达式中引用上下文参数: + *

+ * 并且,与{@link org.springframework.beans.factory.annotation.Value @Value}注解一样, + * 支持通过{@code "${}"}引用配置文件中的属性。 + * + * @return 条件表达式 + * @see com.baomidou.lock.MethodBasedExpressionEvaluator + * @see com.baomidou.lock.aop.AbstractConditionalLockInterceptor */ String condition() default ""; /** - * 用于多个方法锁同一把锁 可以理解为锁资源名称 为空则会使用 包名+类名+方法名 + *

key前缀,它将会被拼接到根据{@link #keys()}属性生成的key前面, + * 当为空时,默认为当前加锁方法的{@code "声明类全限定名#方法名称"}。 + * + * @return key前缀 + */ + @AliasFor("name") + String prefix() default ""; + + /** + *

key前缀,它将会被拼接到根据{@link #keys()}属性生成的key前面, + * 当为空时,默认为当前加锁方法的{@code "声明类全限定名#方法名称"}。 * - * @return 名称 + * @return key前缀 */ + @AliasFor("prefix") String name() default ""; /** - * support SPEL expresion 锁的key = name + keys + * 要用于加锁的key,执行时, + * 将通过{@link #keyBuilderStrategy()}指定的策略生成最终的key。 * - * @return KEY + * @return 要加锁的key + * @see #keyBuilderStrategy() + * @see LockKeyBuilder */ String[] keys() default ""; /** - * @return 过期时间 单位:毫秒 - *

-     *     过期时间一定是要长于业务的执行时间. 未设置则为默认时间30秒 默认值:{@link Lock4jProperties#expire}
-     * 
+ *

要使用的{@link LockKeyBuilder key生成器}类型, + * 默认优先使用{@link Lock4jProperties#getPrimaryKeyBuilder()} Lck4jProperties.primaryKeyBuilder}所指定的执行器。 + * + * @return 生成器类型 + * @see LockKeyBuilder */ - long expire() default -1; + Class keyBuilderStrategy() default LockKeyBuilder.class; /** - * @return 获取锁超时时间 单位:毫秒 - *

-     *     结合业务,建议该时间不宜设置过长,特别在并发高的情况下. 未设置则为默认时间3秒 默认值:{@link Lock4jProperties#acquireTimeout}
-     * 
+ *

获取锁超时时间,单位为毫秒。 + *

+ * 当获取锁失败时,将会根据{@link #failStrategy()}指定的策略进行处理。 + * + *

注意:并发较高的情况下该值不易设置过大,请结合业务设置一个合理的等待时间。 + * + * @return 获取锁超时时间 + * @see #failStrategy() */ long acquireTimeout() default -1; /** - * 业务方法执行完后(方法内抛异常也算执行完)自动释放锁,如果为false,锁将不会自动释放直至到达过期时间才释放 {@link com.baomidou.lock.annotation.Lock4j#expire()} + *

锁的过期时间,单位为毫秒。 * - * @return 是否自动释放锁 + *

当该值小于等于0时,若所使用的{@link #executor() 执行器}支持自动续期, + * 则在业务执行期间将会自动续期,直至业务执行完毕后才会释放锁。
+ * 否则会使用{@link Lock4jProperties#getExpire() Lck4jProperties.expire}指定的过期时间。 + * + *

注意:过期时间请务必保证长于业务的执行时间,未在配置文件中指定超时时间时,则默认为30s。 + * + * @return 锁的过期时间 + * @see LockExecutor#supportRenewal() + * @see #autoRelease() */ - boolean autoRelease() default true; + long expire() default -1; /** - * 失败策略 + *

在方法执行完后是否自动释放锁, + * 当设置为{@code false}时,锁将不会自动释放,直至到达过期时间才会释放。 * - * @return LockFailureStrategy + * @return 是否自动释放锁 */ - Class failStrategy() default LockFailureStrategy.class; + boolean autoRelease() default true; /** - * key生成器策略 + *

要使用的{@link LockFailureStrategy 当获取锁失败时的处理策略}类型, + * 默认优先使用{@link Lock4jProperties#getPrimaryFailureStrategy()} Lck4jProperties.primaryFailureStrategy}所指定的执行器。 * - * @return LockKeyBuilder + * @return 失败策略类型 + * @see LockFailureStrategy */ - Class keyBuilderStrategy() default LockKeyBuilder.class; + Class failStrategy() default LockFailureStrategy.class; /** - * 获取顺序,值越小越先执行 + *

当存在多个{@link Lock4j}注解时, + * 将会根据该值进行排序,顺序值越小越先执行。 * * @return 顺序值 */ diff --git a/lock4j-redisson-spring-boot-starter/src/main/java/com/baomidou/lock/executor/RedissonLockExecutor.java b/lock4j-redisson-spring-boot-starter/src/main/java/com/baomidou/lock/executor/RedissonLockExecutor.java index b7453cd..8e48328 100644 --- a/lock4j-redisson-spring-boot-starter/src/main/java/com/baomidou/lock/executor/RedissonLockExecutor.java +++ b/lock4j-redisson-spring-boot-starter/src/main/java/com/baomidou/lock/executor/RedissonLockExecutor.java @@ -20,6 +20,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.redisson.api.RLock; import org.redisson.api.RedissonClient; +import org.springframework.lang.Nullable; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; @@ -36,10 +37,11 @@ public class RedissonLockExecutor extends AbstractLockExecutor { private final RedissonClient redissonClient; @Override - public boolean renewal() { + public boolean supportRenewal() { return true; } + @Nullable @Override public RLock acquire(String lockKey, String lockValue, long expire, long acquireTimeout) { try { diff --git a/lock4j-zookeeper-spring-boot-starter/src/main/java/com/baomidou/lock/annotation/ZookeeperLock.java b/lock4j-zookeeper-spring-boot-starter/src/main/java/com/baomidou/lock/annotation/ZookeeperLock.java index 3e17fff..aef6594 100644 --- a/lock4j-zookeeper-spring-boot-starter/src/main/java/com/baomidou/lock/annotation/ZookeeperLock.java +++ b/lock4j-zookeeper-spring-boot-starter/src/main/java/com/baomidou/lock/annotation/ZookeeperLock.java @@ -2,9 +2,11 @@ package com.baomidou.lock.annotation; import com.baomidou.lock.LockFailureStrategy; import com.baomidou.lock.LockKeyBuilder; +import com.baomidou.lock.executor.LockExecutor; import com.baomidou.lock.executor.ZookeeperLockExecutor; import com.baomidou.lock.spring.boot.autoconfigure.Lock4jProperties; import org.springframework.core.Ordered; +import org.springframework.core.annotation.AliasFor; import java.lang.annotation.*; @@ -12,6 +14,7 @@ import java.lang.annotation.*; * 基于{@link com.baomidou.lock.executor.ZookeeperLockExecutor}实现的分布式锁 * * @author huangchengxing + * @see Lock4j */ @Lock4j(executor = ZookeeperLockExecutor.class) @Target(value = {ElementType.METHOD, ElementType.ANNOTATION_TYPE}) @@ -20,65 +23,120 @@ import java.lang.annotation.*; public @interface ZookeeperLock { /** - * 应用条件表达式,当执行结果为{@code true}或{@code 'true'}时,才会执行锁操作 + *

应用条件表达式,当执行结果为{@code true}或{@code 'true'}时,才会执行锁操作。 * - * @return 名称 + *

你可以在表达式中引用上下文参数: + *

+ * 并且,与{@link org.springframework.beans.factory.annotation.Value @Value}注解一样, + * 支持通过{@code "${}"}引用配置文件中的属性。 + * + * @return 条件表达式 + * @see com.baomidou.lock.MethodBasedExpressionEvaluator + * @see com.baomidou.lock.aop.AbstractConditionalLockInterceptor */ String condition() default ""; /** - * 用于多个方法锁同一把锁 可以理解为锁资源名称 为空则会使用 包名+类名+方法名 + *

key前缀,它将会被拼接到根据{@link #keys()}属性生成的key前面, + * 当为空时,默认为当前加锁方法的{@code "声明类全限定名#方法名称"}。 + * + * @return key前缀 + */ + @AliasFor("name") + String prefix() default ""; + + /** + *

key前缀,它将会被拼接到根据{@link #keys()}属性生成的key前面, + * 当为空时,默认为当前加锁方法的{@code "声明类全限定名#方法名称"}。 * - * @return 名称 + * @return key前缀 */ + @AliasFor("prefix") String name() default ""; /** - * support SPEL expresion 锁的key = name + keys + *

要使用的{@link LockExecutor 锁执行器}类型, + * 默认优先使用{@link Lock4jProperties#getPrimaryExecutor() Lck4jProperties.primaryExecutor}所指定的执行器, + * + * @return 执行器类型 + * @see LockExecutor + */ + Class executor() default LockExecutor.class; + + /** + * 要用于加锁的key,执行时, + * 将通过{@link #keyBuilderStrategy()}指定的策略生成最终的key。 * - * @return KEY + * @return 要加锁的key + * @see #keyBuilderStrategy() + * @see LockKeyBuilder */ String[] keys() default ""; /** - * @return 过期时间 单位:毫秒 - *

-     *     过期时间一定是要长于业务的执行时间. 未设置则为默认时间30秒 默认值:{@link Lock4jProperties#expire}
-     * 
+ *

要使用的{@link LockKeyBuilder key生成器}类型, + * 默认优先使用{@link Lock4jProperties#getPrimaryKeyBuilder()} Lck4jProperties.primaryKeyBuilder}所指定的执行器。 + * + * @return 生成器类型 + * @see LockKeyBuilder */ - long expire() default -1; + Class keyBuilderStrategy() default LockKeyBuilder.class; /** - * @return 获取锁超时时间 单位:毫秒 - *

-     *     结合业务,建议该时间不宜设置过长,特别在并发高的情况下. 未设置则为默认时间3秒 默认值:{@link Lock4jProperties#acquireTimeout}
-     * 
+ *

获取锁超时时间,单位为毫秒。 + *

+ * 当获取锁失败时,将会根据{@link #failStrategy()}指定的策略进行处理。 + * + *

注意:并发较高的情况下该值不易设置过大,请结合业务设置一个合理的等待时间。 + * + * @return 获取锁超时时间 + * @see #failStrategy() */ long acquireTimeout() default -1; /** - * 业务方法执行完后(方法内抛异常也算执行完)自动释放锁,如果为false,锁将不会自动释放直至到达过期时间才释放 {@link com.baomidou.lock.annotation.Lock4j#expire()} + *

锁的过期时间,单位为毫秒。 * - * @return 是否自动释放锁 + *

当该值小于等于0时,若所使用的{@link #executor() 执行器}支持自动续期, + * 则在业务执行期间将会自动续期,直至业务执行完毕后才会释放锁。
+ * 否则会使用{@link Lock4jProperties#getExpire() Lck4jProperties.expire}指定的过期时间。 + * + *

注意:过期时间请务必保证长于业务的执行时间,未在配置文件中指定超时时间时,则默认为30s。 + * + * @return 锁的过期时间 + * @see LockExecutor#supportRenewal() + * @see #autoRelease() */ - boolean autoRelease() default true; + long expire() default -1; /** - * 失败策略 + *

在方法执行完后是否自动释放锁, + * 当设置为{@code false}时,锁将不会自动释放,直至到达过期时间才会释放。 * - * @return LockFailureStrategy + * @return 是否自动释放锁 */ - Class failStrategy() default LockFailureStrategy.class; + boolean autoRelease() default true; /** - * key生成器策略 + *

要使用的{@link LockFailureStrategy 当获取锁失败时的处理策略}类型, + * 默认优先使用{@link Lock4jProperties#getPrimaryFailureStrategy()} Lck4jProperties.primaryFailureStrategy}所指定的执行器。 * - * @return LockKeyBuilder + * @return 失败策略类型 + * @see LockFailureStrategy */ - Class keyBuilderStrategy() default LockKeyBuilder.class; + Class failStrategy() default LockFailureStrategy.class; /** - * 获取顺序,值越小越先执行 + *

当存在多个{@link Lock4j}注解时, + * 将会根据该值进行排序,顺序值越小越先执行。 * * @return 顺序值 */ diff --git a/lock4j-zookeeper-spring-boot-starter/src/main/java/com/baomidou/lock/executor/ZookeeperLockExecutor.java b/lock4j-zookeeper-spring-boot-starter/src/main/java/com/baomidou/lock/executor/ZookeeperLockExecutor.java index 200170e..427995c 100644 --- a/lock4j-zookeeper-spring-boot-starter/src/main/java/com/baomidou/lock/executor/ZookeeperLockExecutor.java +++ b/lock4j-zookeeper-spring-boot-starter/src/main/java/com/baomidou/lock/executor/ZookeeperLockExecutor.java @@ -21,6 +21,7 @@ import lombok.extern.slf4j.Slf4j; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.imps.CuratorFrameworkState; import org.apache.curator.framework.recipes.locks.InterProcessMutex; +import org.springframework.lang.Nullable; import java.util.concurrent.TimeUnit; @@ -35,6 +36,7 @@ public class ZookeeperLockExecutor extends AbstractLockExecutor