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 e2e5d21bd2de3dac0d68756402fa4d70189ce079..9b1524121c3a1a8625c2aec342e6c7a281cd2415 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 031e0693371a322eff61aee8c4bf71b6e38f4f0d..6299b5c4d49baddc4030ba9bbb75f360aa2ffc88 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 734cc989997fd2227c3d3c0a6539384608edf0d1..02e329887504ec81dd42273defbbcb1e91ec607d 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 dfca5fc6804c0aab4d41c9a36870e666f84750a8..b3648bcf2ee7c16e5232006b01162efba82b7016 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 3ea368ecb0651abcfbfc02cef35ae5daa7c2767a..6ddacdb94843207969f71cfbc28f6c24db48b105 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 f26e0272b44aafd6178493b1a6b1d4bd190c93cb..e8d9e7e8f007dab4560c71954756e847d8079b6a 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 c64660c956b47f8489eb8de089c2a240cc0fe29d..c0742785a024f8a52c6e3699c1a83dc43899dbb5 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 af3a095ec6ccaf6f7387c89021ec69c09c0a939d..5a4547cc644208be50e5d6f3800c5ee5250eb763 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 25a6c5245eb4bef5e5a6face5ae082f7255087a4..b1d9b0294e5e97cf8b35e08948d98c36e175cd65 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 89a95d7a477b60f0dfa6c1be6d0c0b1d03f4371d..9b490f45ea5178fc7365d7a7bc839b744f26cf6b 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 b7453cd4436115e56d2a1fcda67716b197111273..8e4832872b744043642f98341a8533d121a9045f 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 3e17fff2f1b35d02c652aec39b844c415f07f0bd..aef65942c3db112b926c019dc66bee16dc77e134 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 200170ef1fbfeeed9e7ce31e8d34526b375dd2ec..427995c0aa31c4cb5b90af1cb85e7aa252ae42c9 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