A Spring Boot starter that provides annotation-driven distributed locks via AOP. Supported backends: Redisson, Zookeeper (Curator), and Etcd (jetcd).
-
@DistributedLock annotation for method-level locking
-
SpEL-based lock key resolution with sensible defaults
-
Multiple lock types: reentrant, fair, read, write
-
Auto-configuration based on available client beans
-
Spring Boot 3.x users should use the 0.0.x release line
-
Spring Boot 4.x users should use the 0.1.x release line
Add the starter dependency and one backend client dependency.
Maven:
<dependency>
<groupId>com.childrengreens</groupId>
<artifactId>distributed-lock-spring-boot-starter</artifactId>
<version>${latest-version}</version>
</dependency>Backend dependencies (pick one or more):
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>4.0.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>5.9.0</version>
</dependency>
<dependency>
<groupId>io.etcd</groupId>
<artifactId>jetcd-core</artifactId>
<version>0.8.6</version>
</dependency>Application properties:
distributed:
lock:
prefix: lock-
distributed.lock.prefix (default: lock)
-
distributed.lock.provider (optional: redisson, zookeeper, etcd)
Annotate a method with @DistributedLock:
import com.childrengreens.distributedlock.annotation.DistributedLock;
import com.childrengreens.distributedlock.annotation.LockType;
@DistributedLock(key = "#orderId", prefix = "order", waitTime = 3, leaseTime = 10, lockType = LockType.FAIR)
public void process(String orderId) {
// business logic
}SpEL examples:
@DistributedLock(key = "#user.id + ':' + #order.id")
public void pay(User user, Order order) {
}
@DistributedLock(key = "@tenantProvider.tenantId + ':' + #p0")
public void handle(String id) {
}Default key behavior: - If key is empty, the key is generated as:
+ DeclaringClass.methodName:<SimpleKey>
Prefix behavior: - @DistributedLock(prefix = "...") overrides distributed.lock.prefix - If both are empty, no prefix is used
Provider selection: - Set distributed.lock.provider to pick the backend when multiple clients exist
The starter auto-configures the executor when a client bean exists.
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
class RedissonConfig {
@Bean
RedissonClient redissonClient() {
Config config = new Config();
config.useSingleServer().setAddress("redis://127.0.0.1:6379");
return Redisson.create(config);
}
}import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
class CuratorConfig {
@Bean(initMethod = "start", destroyMethod = "close")
CuratorFramework curatorFramework() {
return CuratorFrameworkFactory.newClient(
"127.0.0.1:2181",
new ExponentialBackoffRetry(1000, 3)
);
}
}import io.etcd.jetcd.Client;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
class EtcdConfig {
@Bean(destroyMethod = "close")
Client etcdClient() {
return Client.builder().endpoints("http://127.0.0.1:2379").build();
}
}-
REENTRANT: default reentrant lock
-
FAIR: fair lock when supported
-
READ: read lock (read/write lock)
-
WRITE: write lock (read/write lock)
Mapping notes: - Redisson: maps directly to the respective lock variants - Zookeeper: FAIR uses a reentrant mutex implementation - Etcd: uses a single lock type; LockType is tracked for unlock bookkeeping
-
If a lock cannot be acquired, a DistributedLockException is thrown
-
Unlock failures are logged and suppressed to avoid masking business errors