(1). 项目结构如下

lixin-macbook:Workspace lixin$ tree shedlock-example
shedlock-example
├── pom.xml
├── src
│   ├── main
│   │   ├── java
│   │   │   └── help
│   │   │       └── lixin
│   │   │           └── samples
│   │   │               ├── ProviderApplication.java
│   │   │               ├── config
│   │   │               │   └── ShedLockConfig.java
│   │   │               ├── controller
│   │   │               │   └── HelloController.java
│   │   │               └── tasks
│   │   │                   └── HelloWorldTask.java
│   │   └── resources
│   │       ├── application.properties
│   │       └── logback-spring.xml
│   └── test
│       ├── java
│       └── resources
└── target

(2). 添加依赖(pom.xml)

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>help.lixin</groupId>
	<artifactId>shedlock-example</artifactId>
	<packaging>jar</packaging>
	<version>1.0.0</version>
	<name>shedlock-example</name>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<maven.compiler.source>8</maven.compiler.source>
		<maven.compiler.target>8</maven.compiler.target>
	</properties>

	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>Greenwich.RELEASE</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
			<dependency>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-dependencies</artifactId>
				<version>2.1.0.RELEASE</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-netflix</artifactId>
				<version>2.1.1.RELEASE</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		
		<!-- DataSource初始化(DataSourceConfiguration) -->
		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
		  <groupId>mysql</groupId>
		  <artifactId>mysql-connector-java</artifactId>
		  <version>5.1.49</version>
		</dependency> 
        
        <!-- 添加ShedLock依赖 -->
		<dependency>
		    <groupId>net.javacrumbs.shedlock</groupId>
		    <artifactId>shedlock-spring</artifactId>
		    <version>4.23.1-SNAPSHOT</version>
		</dependency>
		<!-- 添加lock提供者 --> 
		<dependency>
            <groupId>net.javacrumbs.shedlock</groupId>
            <artifactId>shedlock-provider-jdbc-template</artifactId>
            <version>4.23.1-SNAPSHOT</version>
        </dependency>
        
        <dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>
	
</project>

(3). application.properties

server.port=8080
spring.application.name=shedlock-example

# 连接池类型
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/shedlock?useSSL=false

(4). mysql创建数据库和表

mysql> create database shedlock;
Query OK, 1 row affected (0.02 sec)

mysql> use shedlock;
Database changed

mysql> CREATE TABLE shedlock(name VARCHAR(64) NOT NULL, lock_until TIMESTAMP(3) NOT NULL, locked_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), locked_by VARCHAR(255) NOT NULL, PRIMARY KEY (name));

(5). ShedLockConfig

package help.lixin.samples.config;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;

import net.javacrumbs.shedlock.core.LockProvider;
import net.javacrumbs.shedlock.provider.jdbctemplate.JdbcTemplateLockProvider;

@Configuration
public class ShedLockConfig {
	
	// 配置锁的提供者
	@Bean
	public LockProvider lockProvider(@Autowired DataSource dataSource) {
        return new JdbcTemplateLockProvider(
            JdbcTemplateLockProvider.Configuration.builder()
            .withJdbcTemplate(new JdbcTemplate(dataSource))
            .build()
        );
	}
}

(6). HelloWorldTask

package help.lixin.samples.tasks;

import java.util.Date;

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import net.javacrumbs.shedlock.spring.annotation.SchedulerLock;

@Component
public class HelloWorldTask {

	@Scheduled(cron = "*/5 * * * * ?")
	// ***********************************************
	// 定义锁的名称,最小锁住时间(lockAtLeastFor)和最大锁住时间(lockAtMostFor).
	// ***********************************************
	@SchedulerLock(name = "hello-world", lockAtLeastFor = "20000", lockAtMostFor = "30000")
	public void helloworld() {
		System.out.println(String.format("[%s] Hello World job run...", new Date()));
	}
}

(7). ProviderApplication

package help.lixin.samples;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

import net.javacrumbs.shedlock.spring.annotation.EnableSchedulerLock;

@SpringBootApplication
@EnableScheduling
// ***************************************************
// 注意:需要启用注解@EnableSchedulerLock
// ***************************************************
@EnableSchedulerLock(defaultLockAtMostFor = "120m")
public class ProviderApplication {
	public static void main(String[] args) throws Exception {
		SpringApplication.run(ProviderApplication.class, args);
	}
}

(8). 运行查看控制台

# 1. 测试不添加分布式锁(不加注解@SchedulerLock)的情况下(每5秒会执行一次).
[Tue May 11 15:49:45 CST 2021] Hello World job run...
[Tue May 11 15:49:50 CST 2021] Hello World job run...
[Tue May 11 15:49:55 CST 2021] Hello World job run...
[Tue May 11 15:50:00 CST 2021] Hello World job run...


# 2. 测试添加分布锁(增加注解@SchedulerLock)的情况下
#    虽然,定时任务是每隔5秒执行一次,但是,分布式锁定义的是:每次任务要锁住20秒.
#    所以,定时任务加锁失败的情况下,是直接跳过的.
[Tue May 11 15:51:35 CST 2021] Hello World job run...
[Tue May 11 15:52:00 CST 2021] Hello World job run...
[Tue May 11 15:52:25 CST 2021] Hello World job run...
[Tue May 11 15:52:45 CST 2021] Hello World job run...

(9). 总结

有人肯定会想问:明明是个分布式锁,非要跟定时任务绑在一起,这个框架,能不能做到,不和定时任务绑定呢?
答案是:可以的,因为:shedlock-spring是对shedlock-core的二次封装,以适应于定时任务场景而已.后面的小节,我会对ShedLock源码剖析.