Using Redisson Distributed Locks with Custom Annotations in Java

This article explains how to apply Redisson's distributed lock in Java, demonstrates the basic lock and tryLock APIs, shows how to create a custom @DistributedLock annotation and an AOP aspect to handle locking automatically, and provides a practical usage example with two services.

Architecture Digest
Architecture Digest
Architecture Digest
Using Redisson Distributed Locks with Custom Annotations in Java

In daily development, concurrent scenarios often require locking to ensure interface consistency; while local locks like ReentrantLock and synchronized are unsuitable for distributed systems, Redisson provides a reliable distributed lock implementation based on Redis.

2. Common Usage of Redisson Distributed Lock

Redisson implements the java.util.concurrent.locks.Lock interface. The basic lock acquisition pattern is shown below.

public void getLock() {
    // Get lock
    RLock lock = redisson.getLock("Lxlxxx_Lock");
    try {
        // 2. Acquire lock
        lock.lock();
    } catch (InterruptedException e) {
        e.getStackTrace();
    } finally {
        // 3. Release lock
        lock.unlock();
        System.out.println("Finally,释放锁成功");
    }
}

Using lock.lock() can block indefinitely if the lock is not obtained, which is undesirable.

TryLock

The tryLock method returns a boolean, similar to ReentrantLock.tryLock. It attempts to acquire the lock and returns true on success, avoiding thread blockage; a failed attempt allows the program to continue with alternative logic. Redisson also includes a watchdog mechanism to automatically extend the lease of a near‑expiry lock.

RLock lock = redisson.getLock(name);
try {
    if (lock.tryLock(2, 10, TimeUnit.SECONDS)) {
        // Execute business logic
    } else {
        System.out.println("已存在");
    }
} catch (InterruptedException e) {
    e.printStackTrace();
} finally {
    // Release if current thread holds the lock
    if (this.redissonLock.isHeldByCurrentThread(lockName)) {
        this.redissonLock.unlock(lockName);
    }
}

3. Implementing Lock Mechanism with a Custom Annotation

To avoid repetitive lock code across methods, an AOP aspect can be used together with a custom annotation. The annotation definition is as follows:

@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface DistributedLock {
    String key() default "";
    int leaseTime() default 10;
    boolean autoRelease() default true;
    String errorDesc() default "系统正常处理,请稍后提交";
    int waitTime() default 1;
}

The aspect class intercepts methods annotated with @DistributedLock, builds the lock key, attempts to acquire the lock, proceeds with the method execution, and finally releases the lock.

@Aspect
@Component
public class DistributedLockHandler {
    private static final Logger log = LoggerFactory.getLogger(DistributedLockHandler.class);
    @Autowired
    RedissonLock redissonLock;

    @Around("@annotation(distributedLock)")
    public Object around(ProceedingJoinPoint joinPoint, DistributedLock distributedLock) throws Throwable {
        String lockName = this.getRedisKey(joinPoint, distributedLock);
        int leaseTime = distributedLock.leaseTime();
        String errorDesc = distributedLock.errorDesc();
        int waitTime = distributedLock.waitTime();
        Object result;
        try {
            boolean lock = this.redissonLock.tryLock(lockName, (long) leaseTime, (long) waitTime);
            if (!lock) {
                throw new RuntimeException(errorDesc);
            }
            result = joinPoint.proceed();
        } catch (Throwable t) {
            log.error("执行业务方法异常", t);
            throw t;
        } finally {
            if (this.redissonLock.isHeldByCurrentThread(lockName)) {
                this.redissonLock.unlock(lockName);
            }
        }
        return result;
    }

    /**
     * Generate the Redis key for the lock.
     */
    private String getRedisKey(ProceedingJoinPoint joinPoint, DistributedLock distributedLock) {
        String key = distributedLock.key();
        Object[] args = joinPoint.getArgs();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        DefaultParameterNameDiscoverer nameDiscoverer = new DefaultParameterNameDiscoverer();
        String[] paramNames = nameDiscoverer.getParameterNames(method);
        if (StringUtils.isEmpty(key)) {
            if (paramNames != null && paramNames.length > 0) {
                StringBuilder sb = new StringBuilder();
                for (int i = 0; i < paramNames.length; i++) {
                    sb.append(paramNames[i]).append(" = ").append(args[i]);
                }
                key = sb.toString();
            } else {
                key = "redissionLock";
            }
            return key;
        } else {
            SpelExpressionParser parser = new SpelExpressionParser();
            Expression expression = parser.parseExpression(key);
            if (paramNames != null && paramNames.length != 0) {
                EvaluationContext ctx = new StandardEvaluationContext();
                for (int i = 0; i < paramNames.length; i++) {
                    ctx.setVariable(paramNames[i], args[i]);
                }
                try {
                    Object value = expression.getValue(ctx);
                    return (value != null && !"".equals(value.toString())) ? value.toString() : key;
                } catch (Exception e) {
                    return key;
                }
            } else {
                return key;
            }
        }
    }
}

4. Practical Usage

Apply the custom annotation to a method; the key attribute specifies the lock key and errorDesc provides the failure message. In the demo, two services (ports 8460 and 8461) are started; using Postman to call the same endpoint simultaneously shows that one service acquires the lock while the other fails the tryLock attempt, demonstrating the locking behavior.

5. Summary

Distributed locks should be used judiciously based on business requirements; for low concurrency scenarios they may be unnecessary, and excessive locking can degrade performance. In many B‑end projects, simple idempotent interfaces can avoid duplicate submissions without resorting to locks.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Javaaopconcurrencyspringdistributed-lockannotationredisson
Architecture Digest
Written by

Architecture Digest

Focusing on Java backend development, covering application architecture from top-tier internet companies (high availability, high performance, high stability), big data, machine learning, Java architecture, and other popular fields.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.