Spring Framework integration for ShedLock distributed locking system providing annotation-based task scheduling locks.
—
Classes and utilities for integrating with Spring's TaskScheduler infrastructure, primarily used in the deprecated PROXY_SCHEDULER intercept mode.
A TaskScheduler wrapper that automatically applies locking to all scheduled tasks.
public class LockableTaskScheduler implements TaskScheduler, DisposableBean {
public LockableTaskScheduler(TaskScheduler taskScheduler, LockManager lockManager);
// TaskScheduler methods with Trigger
public ScheduledFuture<?> schedule(Runnable task, Trigger trigger);
// TaskScheduler methods with Date
public ScheduledFuture<?> schedule(Runnable task, Date startTime);
public ScheduledFuture<?> scheduleAtFixedRate(Runnable task, Date startTime, long period);
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, Date startTime, long delay);
// TaskScheduler methods with plain timing
public ScheduledFuture<?> scheduleAtFixedRate(Runnable task, long period);
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, long delay);
// TaskScheduler methods with Instant (Java 8+ time API)
public ScheduledFuture<?> schedule(Runnable task, Instant startTime);
public ScheduledFuture<?> scheduleAtFixedRate(Runnable task, Instant startTime, Duration period);
public ScheduledFuture<?> scheduleAtFixedRate(Runnable task, Duration period);
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, Instant startTime, Duration delay);
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, Duration delay);
// DisposableBean
public void destroy() throws Exception;
}TaskScheduler to wrap (e.g., ConcurrentTaskScheduler)LockManager that handles lock acquisition and release@Configuration
public class SchedulerConfig {
@Bean
public LockProvider lockProvider() {
return new JdbcTemplateLockProvider(dataSource);
}
@Bean
public LockManager lockManager(LockProvider lockProvider) {
return new DefaultLockManager(lockProvider);
}
@Bean
public TaskScheduler taskScheduler(LockManager lockManager) {
ConcurrentTaskScheduler scheduler = new ConcurrentTaskScheduler();
return new LockableTaskScheduler(scheduler, lockManager);
}
}The LockableTaskScheduler automatically wraps all submitted tasks in LockableRunnable:
private Runnable wrap(Runnable task) {
return new LockableRunnable(task, lockManager);
}This ensures that any task scheduled through this scheduler will be protected by distributed locking.
AOP advisor that intercepts calls to TaskScheduler methods and wraps tasks with locking.
public class SchedulerProxyScheduledLockAdvisor extends AbstractPointcutAdvisor {
public SchedulerProxyScheduledLockAdvisor(
LockProviderSupplier lockProviderSupplier,
ExtendedLockConfigurationExtractor lockConfigurationExtractor
);
public Pointcut getPointcut(); // Matches TaskScheduler methods
public Advice getAdvice(); // Returns scheduler interceptor
}The advisor targets TaskScheduler interface methods:
// Internal pointcut implementation
private static class TaskSchedulerPointcut implements Pointcut {
public ClassFilter getClassFilter(); // Matches TaskScheduler classes
public MethodMatcher getMethodMatcher(); // Matches schedule* methods
}Targeted methods:
schedulescheduleAtFixedRatescheduleWithFixedDelayThe advisor intercepts scheduler calls and:
ScheduledMethodRunnable and OutcomeTrackingRunnableLockableRunnable wrapper with appropriate lock configurationLockProvider for the task// Internal interceptor (simplified)
private static class LockingInterceptor implements MethodInterceptor {
public Object invoke(MethodInvocation invocation) throws Throwable;
}Standard Spring scheduled method wrapper:
// Internal handling
private LockableRunnable wrapTask(ScheduledMethodRunnable task) {
LockProvider lockProvider = lockProviderSupplier.supply(
task.getTarget(), task.getMethod(), new Object[] {}
);
LockManager lockManager = new DefaultLockManager(lockProvider, lockConfigurationExtractor);
return new LockableRunnable(task, lockManager);
}Spring's outcome tracking wrapper (requires reflection):
// Internal reflection-based handling for OutcomeTrackingRunnable
private Object wrapTask(Object firstArgument) throws NoSuchFieldException, IllegalAccessException {
if (firstArgument.getClass().getSimpleName().equals("OutcomeTrackingRunnable")) {
Field runnable = firstArgument.getClass().getDeclaredField("runnable");
runnable.setAccessible(true);
Object wrappedRunnable = runnable.get(firstArgument);
if (wrappedRunnable instanceof ScheduledMethodRunnable task) {
return wrapTask(task);
}
}
return firstArgument;
}When using PROXY_SCHEDULER mode, ShedLock automatically registers a default TaskScheduler if none exists.
public class RegisterDefaultTaskSchedulerPostProcessor
implements BeanDefinitionRegistryPostProcessor, Ordered, BeanFactoryAware {
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
public int getOrder(); // Returns LOWEST_PRECEDENCE
public void setBeanFactory(BeanFactory beanFactory) throws BeansException;
}TaskScheduler beans exist, do nothingConcurrentTaskScheduler using itConcurrentTaskSchedulerConcurrentTaskScheduler and log warning// Equivalent registration when one ScheduledExecutorService exists
@Bean(DEFAULT_TASK_SCHEDULER_BEAN_NAME)
public TaskScheduler taskScheduler(ScheduledExecutorService scheduledExecutor) {
ConcurrentTaskScheduler scheduler = new ConcurrentTaskScheduler();
scheduler.setScheduledExecutor(scheduledExecutor);
return scheduler;
}// Equivalent registration when no ScheduledExecutorService exists
@Bean(DEFAULT_TASK_SCHEDULER_BEAN_NAME)
public TaskScheduler taskScheduler() {
return new ConcurrentTaskScheduler();
}The default bean name is DEFAULT_TASK_SCHEDULER_BEAN_NAME from Spring's ScheduledAnnotationBeanPostProcessor.
Before (PROXY_SCHEDULER):
@EnableSchedulerLock(
interceptMode = InterceptMode.PROXY_SCHEDULER,
defaultLockAtMostFor = "10m"
)
public class SchedulerConfig {
// TaskScheduler beans are automatically wrapped
}After (PROXY_METHOD):
@EnableSchedulerLock(
interceptMode = InterceptMode.PROXY_METHOD, // or omit (default)
defaultLockAtMostFor = "10m"
)
public class SchedulerConfig {
// Methods are intercepted directly
}No other changes required - the @SchedulerLock annotations work the same way.
Install with Tessl CLI
npx tessl i tessl/maven-net-javacrumbs-shedlock--shedlock-spring