Job scheduling là một công việc chúng ta thường hay gặp trong khi xây dựng các ứng dụng. Để thực hiện job scheduling trong Spring Boot, chúng ta có thể áp dụng một trong hai cách sau:
- Sử dụng annotation
@Scheduled
- Sử dụng thư viện Quartz
…
1. Sử dụng annotation @Scheduled
1.1. Enable scheduling
Với cách này, trước tiên chúng ta cần phải enable scheduling trong Spring Boot bằng cách thêm annotation @EnableScheduling
ở configuration class:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>boot<span class="token punctuation">.</span>SpringApplication<span class="token punctuation">;</span> <span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>boot<span class="token punctuation">.</span>autoconfigure<span class="token punctuation">.</span>SpringBootApplication<span class="token punctuation">;</span> <span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>scheduling<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span>EnableScheduling<span class="token punctuation">;</span> <span class="token annotation punctuation">@SpringBootApplication</span> <span class="token annotation punctuation">@EnableScheduling</span> <span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">DemoApplication</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span>String<span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span> <span class="token punctuation">{</span> SpringApplication<span class="token punctuation">.</span><span class="token function">run</span><span class="token punctuation">(</span>DemoApplication<span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">,</span> args<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
1.2. Annotation @Scheduled
Annotatinon @Scheduled
được khai báo bên trên method xử lý job.
Method này phải đảm bảo 2 yêu cầu sau:
- Trả về
void
. - Không được ném ra các ngoại lệ.
Các tham số thường dùng của @Scheduled
là:
long initialDelay
Khoảng thời gian được tính từ thời điểm startup của ứng dụng Spring Boot cho đến thời điểm bắt đầu của lần thực thi đầu tiên của method xử lý job. Đơn vị: millisecond.
long fixedDelay
Chúng ta có thể dùng kết hợp initialDelay
với fixedDelay
. Lúc này, thời điểm của các lần thực thi method xử lý job sẽ là:
- Lần thực thi thứ 1: sau
initialDelay
- Lần thực thi thứ 2: sau khi lần thực thi thứ 1 chấm dứt +
fixedDelay
- Lần thực thi thứ 3: sau khi lần thực thi thứ 2 chấm dứt +
fixedDelay
- Lần thực thi thứ 4: sau khi lần thực thi thứ 3 chấm dứt +
fixedDelay
- …
long fixedRate
Chúng ta có thể dùng kết hợp initialDelay
với fixedRate
. Lúc này, thời điểm của các lần thực thi method xử lý job sẽ là:
- Lần thực thi thứ 1: sau
initialDelay
- Lần thực thi thứ 2: sau
initialDelay
+fixedRate
- Lần thực thi thứ 3: sau
initialDelay
+ 2 *fixedRate
- Lần thực thi thứ 4: sau
initialDelay
+ 3 *fixedRate
- …
String cron
Biểu thức crontab: method xử lý job sẽ được thực thi tại đúng thời điểm thỏa mãn biểu thức crontab.
Chú ý: Phải đảm bảo một và chỉ một trong 3 tham số initialDelay
, fixedDelay
và cron
xuất hiện khi khai báo @Scheduled
.
1.3. Ví dụ
Mình sẽ viết một job cứ 5 giây in thời gian hiện tại 1 lần.
PrintNowService.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>scheduling<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span>Scheduled<span class="token punctuation">;</span> <span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>stereotype<span class="token punctuation">.</span>Service<span class="token punctuation">;</span> <span class="token keyword">import</span> java<span class="token punctuation">.</span>time<span class="token punctuation">.</span>LocalDateTime<span class="token punctuation">;</span> <span class="token annotation punctuation">@Service</span> <span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">PrintNowService</span> <span class="token punctuation">{</span> <span class="token annotation punctuation">@Scheduled</span><span class="token punctuation">(</span>fixedDelay <span class="token operator">=</span> <span class="token number">5000</span><span class="token punctuation">)</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">printNow</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> System<span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>LocalDateTime<span class="token punctuation">.</span><span class="token function">now</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Chạy ứng dụng Spring Boot và chúng ta sẽ thấy kết quả như sau:
1 2 3 4 5 | 2020-01-15T10:22:52.437 2020-01-15T10:22:57.437 2020-01-15T10:23:02.444 ... |
2. Sử dụng thư viện Quartz
2.1. Spring Boot Quartz
Quartz sử dụng các đối tượng Trigger
, Job
và JobDetail
để thực hiện job scheduling.
Để tích hợp thư viện Quartz trong Spring Boot, chúng ta sẽ khai báo Quartz starter trong pom.xml
:
1 2 3 4 5 | <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dependency</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>groupId</span><span class="token punctuation">></span></span>org.springframework.boot<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>groupId</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>artifactId</span><span class="token punctuation">></span></span>spring-boot-starter-quartz<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>artifactId</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dependency</span><span class="token punctuation">></span></span> |
2.2. Ví dụ
Mình sẽ viết một job cứ 5 giây in 1 số ngẫu nhiên.
PrintRandomService.java
: lớp xử lý logic của job.
1 2 3 4 5 6 7 8 9 10 11 12 | <span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>stereotype<span class="token punctuation">.</span>Service<span class="token punctuation">;</span> <span class="token keyword">import</span> java<span class="token punctuation">.</span>util<span class="token punctuation">.</span>concurrent<span class="token punctuation">.</span>ThreadLocalRandom<span class="token punctuation">;</span> <span class="token annotation punctuation">@Service</span> <span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">PrintRandomService</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">printRandom</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> System<span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>ThreadLocalRandom<span class="token punctuation">.</span><span class="token function">current</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">nextInt</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
PrintRandomJob.java
: bean đại diện cho job và phải kế thừa org.springframework.scheduling.quartz.QuartzJobBean
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <span class="token keyword">import</span> org<span class="token punctuation">.</span>quartz<span class="token punctuation">.</span>JobExecutionContext<span class="token punctuation">;</span> <span class="token keyword">import</span> org<span class="token punctuation">.</span>quartz<span class="token punctuation">.</span>JobExecutionException<span class="token punctuation">;</span> <span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>beans<span class="token punctuation">.</span>factory<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span>Autowired<span class="token punctuation">;</span> <span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>scheduling<span class="token punctuation">.</span>quartz<span class="token punctuation">.</span>QuartzJobBean<span class="token punctuation">;</span> <span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">PrintRandomJob</span> <span class="token keyword">extends</span> <span class="token class-name">QuartzJobBean</span> <span class="token punctuation">{</span> <span class="token annotation punctuation">@Autowired</span> <span class="token keyword">private</span> PrintRandomService printRandomService<span class="token punctuation">;</span> <span class="token annotation punctuation">@Override</span> <span class="token keyword">protected</span> <span class="token keyword">void</span> <span class="token function">executeInternal</span><span class="token punctuation">(</span>JobExecutionContext jobExecutionContext<span class="token punctuation">)</span> <span class="token keyword">throws</span> JobExecutionException <span class="token punctuation">{</span> printRandomService<span class="token punctuation">.</span><span class="token function">printRandom</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
JobConfiguration.java
: lớp cấu hình JobDetail
và Trigger
của job.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | <span class="token keyword">import</span> org<span class="token punctuation">.</span>quartz<span class="token punctuation">.</span>CronScheduleBuilder<span class="token punctuation">;</span> <span class="token keyword">import</span> org<span class="token punctuation">.</span>quartz<span class="token punctuation">.</span>JobBuilder<span class="token punctuation">;</span> <span class="token keyword">import</span> org<span class="token punctuation">.</span>quartz<span class="token punctuation">.</span>JobDetail<span class="token punctuation">;</span> <span class="token keyword">import</span> org<span class="token punctuation">.</span>quartz<span class="token punctuation">.</span>JobKey<span class="token punctuation">;</span> <span class="token keyword">import</span> org<span class="token punctuation">.</span>quartz<span class="token punctuation">.</span>Trigger<span class="token punctuation">;</span> <span class="token keyword">import</span> org<span class="token punctuation">.</span>quartz<span class="token punctuation">.</span>TriggerBuilder<span class="token punctuation">;</span> <span class="token keyword">import</span> org<span class="token punctuation">.</span>quartz<span class="token punctuation">.</span>TriggerKey<span class="token punctuation">;</span> <span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>context<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span>Bean<span class="token punctuation">;</span> <span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>context<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span>Configuration<span class="token punctuation">;</span> <span class="token annotation punctuation">@Configuration</span> <span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">JobConfiguration</span> <span class="token punctuation">{</span> <span class="token annotation punctuation">@Bean</span> <span class="token keyword">public</span> JobDetail <span class="token function">printRandomJobDetail</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> JobBuilder <span class="token punctuation">.</span><span class="token function">newJob</span><span class="token punctuation">(</span>PrintRandomJob<span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">withIdentity</span><span class="token punctuation">(</span>JobKey<span class="token punctuation">.</span><span class="token function">jobKey</span><span class="token punctuation">(</span><span class="token string">"printRandom"</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">storeDurably</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token annotation punctuation">@Bean</span> <span class="token keyword">public</span> Trigger <span class="token function">printRandomJobTrigger</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> TriggerBuilder <span class="token punctuation">.</span><span class="token function">newTrigger</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">forJob</span><span class="token punctuation">(</span><span class="token function">printRandomJobDetail</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">withIdentity</span><span class="token punctuation">(</span>TriggerKey<span class="token punctuation">.</span><span class="token function">triggerKey</span><span class="token punctuation">(</span><span class="token string">"printRandom"</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">withSchedule</span><span class="token punctuation">(</span>CronScheduleBuilder<span class="token punctuation">.</span><span class="token function">cronSchedule</span><span class="token punctuation">(</span><span class="token string">"*/5 * * * * ?"</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Chạy ứng dụng Spring Boot và chúng ta sẽ thấy kết quả như sau:
1 2 3 4 5 | -1727615121 878308058 -803885137 ... |