Spring Scheduler定時任務實戰(zhàn)指南(零基礎(chǔ)入門任務調(diào)度)
前言
在日常開發(fā)中,我們經(jīng)常需要處理定時任務:每天凌晨的數(shù)據(jù)同步、每小時的統(tǒng)計報表、每5分鐘的狀態(tài)檢查等。Spring框架提供了一個簡單而強大的定時任務框架——Spring Scheduler,讓我們能夠以聲明的方式輕松實現(xiàn)各種定時任務需求。
本文將通過一個真實案例,帶你從入門到掌握Spring Scheduler的使用。
一、Spring Scheduler簡介
Spring Scheduler是Spring框架提供的定時任務調(diào)度器,它基于注解和配置的方式,讓任務調(diào)度變得簡單直觀。主要特點包括:
- 支持cron表達式、固定延遲、固定頻率等多種調(diào)度方式
- 與Spring容器無縫集成,可直接使用Spring管理的Bean
- 支持異步執(zhí)行和線程池配置
- 無需額外依賴,Spring Boot中開箱即用
二、項目場景:電商訂單超時處理
假設(shè)我們有一個電商系統(tǒng),需要處理訂單超時自動關(guān)閉的功能:訂單創(chuàng)建后30分鐘內(nèi)未支付,系統(tǒng)自動將其標記為已關(guān)閉。
環(huán)境準備
在Spring Boot項目中,首先確保添加了基礎(chǔ)依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>Spring Scheduler已經(jīng)在Spring Boot的web starter中包含,無需額外添加依賴。
啟用定時任務
在Spring Boot主類或配置類上添加@EnableScheduling注解:
@SpringBootApplication
@EnableScheduling
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}三、實現(xiàn)訂單超時檢查任務
1. 創(chuàng)建訂單服務
首先創(chuàng)建一個訂單服務類,包含基本的訂單處理方法:
@Service
public class OrderService {
private static final Logger logger = LoggerFactory.getLogger(OrderService.class);
// 模擬訂單存儲
private Map<Long, Order> orderMap = new ConcurrentHashMap<>();
private AtomicLong idGenerator = new AtomicLong(0);
/**
* 創(chuàng)建新訂單
*/
public Order createOrder(Order order) {
Long orderId = idGenerator.incrementAndGet();
order.setId(orderId);
order.setCreateTime(new Date());
order.setStatus(OrderStatus.CREATED);
orderMap.put(orderId, order);
logger.info("創(chuàng)建訂單成功,訂單ID: {}", orderId);
return order;
}
/**
* 檢查并處理超時訂單
*/
public void checkAndCloseTimeoutOrders() {
Date now = new Date();
int timeoutMinutes = 30;
for (Order order : orderMap.values()) {
if (OrderStatus.CREATED.equals(order.getStatus())) {
long diffInMillis = now.getTime() - order.getCreateTime().getTime();
long diffInMinutes = diffInMillis / (1000 * 60);
if (diffInMinutes >= timeoutMinutes) {
order.setStatus(OrderStatus.CLOSED);
order.setCloseTime(now);
logger.info("訂單超時已關(guān)閉,訂單ID: {}", order.getId());
}
}
}
}
/**
* 獲取訂單狀態(tài)
*/
public OrderStatus getOrderStatus(Long orderId) {
Order order = orderMap.get(orderId);
return order != null ? order.getStatus() : null;
}
}
/**
* 訂單狀態(tài)枚舉
*/
public enum OrderStatus {
CREATED, // 已創(chuàng)建
PAID, // 已支付
CLOSED // 已關(guān)閉
}
/**
* 訂單實體類
*/
public class Order {
private Long id;
private String productName;
private BigDecimal amount;
private Date createTime;
private Date closeTime;
private OrderStatus status;
// 省略getter和setter方法
}2. 實現(xiàn)定時任務
現(xiàn)在創(chuàng)建定時任務類,定期檢查超時訂單:
@Component
public class OrderTimeoutScheduler {
private static final Logger logger = LoggerFactory.getLogger(OrderTimeoutScheduler.class);
@Autowired
private OrderService orderService;
/**
* 每5分鐘檢查一次超時訂單
* 使用cron表達式:每5分鐘執(zhí)行一次
*/
@Scheduled(cron = "0 */5 * * * ?")
public void checkOrderTimeout() {
logger.info("開始執(zhí)行訂單超時檢查任務...");
long startTime = System.currentTimeMillis();
try {
orderService.checkAndCloseTimeoutOrders();
} catch (Exception e) {
logger.error("訂單超時檢查任務執(zhí)行失敗", e);
}
long endTime = System.currentTimeMillis();
logger.info("訂單超時檢查任務執(zhí)行完成,耗時:{}ms", (endTime - startTime));
}
/**
* 另一種方式:固定延遲執(zhí)行
* 上一次任務執(zhí)行完成后,延遲5分鐘再執(zhí)行
*/
// @Scheduled(fixedDelay = 5 * 60 * 1000)
// public void checkOrderTimeoutWithFixedDelay() {
// // 實現(xiàn)邏輯
// }
/**
* 固定頻率執(zhí)行
* 每5分鐘執(zhí)行一次,無論上一次任務是否完成
*/
// @Scheduled(fixedRate = 5 * 60 * 1000)
// public void checkOrderTimeoutWithFixedRate() {
// // 實現(xiàn)邏輯
// }
}3. 測試定時任務
創(chuàng)建一個測試控制器來驗證我們的定時任務:
@RestController
@RequestMapping("/orders")
public class OrderController {
@Autowired
private OrderService orderService;
@PostMapping
public ResponseEntity<Order> createOrder(@RequestBody Order order) {
Order createdOrder = orderService.createOrder(order);
return ResponseEntity.ok(createdOrder);
}
@GetMapping("/{orderId}/status")
public ResponseEntity<OrderStatus> getOrderStatus(@PathVariable Long orderId) {
OrderStatus status = orderService.getOrderStatus(orderId);
return status != null ?
ResponseEntity.ok(status) :
ResponseEntity.notFound().build();
}
}啟動應用后,你可以:
- 通過POST
/orders創(chuàng)建訂單 - 等待5分鐘,查看日志中定時任務的執(zhí)行情況
- 30分鐘后,通過GET
/orders/{id}/status檢查訂單狀態(tài)是否變?yōu)镃LOSED
四、cron表達式詳解
Spring Scheduler支持標準的cron表達式,由6個字段組成(Spring支持7個字段,包含秒):
秒 分 時 日 月 周 年(可選)
常用cron表達式示例:
0 * * * * ?:每分鐘執(zhí)行一次0 */5 * * * ?:每5分鐘執(zhí)行一次0 0 * * * ?:每小時執(zhí)行一次0 0 0 * * ?:每天凌晨執(zhí)行0 0 12 * * ?:每天中午12點執(zhí)行0 0 10,14,16 * * ?:每天10點、14點、16點執(zhí)行
五、高級配置:線程池與異步執(zhí)行
默認情況下,Spring Scheduler使用單線程執(zhí)行所有定時任務。如果任務較多或執(zhí)行時間較長,可能需要配置線程池:
@Configuration
public class SchedulerConfig {
@Bean
public TaskScheduler taskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(10); // 線程池大小
scheduler.setThreadNamePrefix("scheduled-task-");
scheduler.setAwaitTerminationSeconds(60);
scheduler.setWaitForTasksToCompleteOnShutdown(true);
return scheduler;
}
}對于需要異步執(zhí)行的任務,可以結(jié)合@Async注解使用:
@Async
@Scheduled(fixedRate = 5000)
public void asyncScheduledTask() {
// 這個任務會在單獨的線程中異步執(zhí)行
}確保在配置類上添加@EnableAsync注解。
六、最佳實踐與注意事項
- ??任務冪等性??:確保定時任務可以多次執(zhí)行而不會產(chǎn)生副作用
- ??異常處理??:在任務內(nèi)部妥善處理異常,避免影響其他任務執(zhí)行
- ??分布式環(huán)境??:在集群部署時,需要考慮使用分布式鎖或只在一臺實例上執(zhí)行
- ??避免長時間執(zhí)行??:長時間運行的任務會影響其他定時任務的執(zhí)行
- ??配置化??:將cron表達式放在配置文件中,便于不同環(huán)境調(diào)整
# application.properties
order.timeout.cron=0 */5 * * * ?
@Scheduled(cron = "${order.timeout.cron}")
public void checkOrderTimeout() {
// ...
}七、總結(jié)
Spring Scheduler提供了簡單而強大的定時任務功能,通過本文的電商訂單超時處理案例,我們可以看到:
- 使用
@EnableScheduling啟用定時任務支持 - 通過
@Scheduled注解聲明定時方法,支持cron表達式、固定延遲和固定頻率 - 定時任務方法可以是任何Spring管理的Bean的方法
- 可以通過配置線程池來優(yōu)化任務執(zhí)行性能
- 結(jié)合
@Async可以實現(xiàn)異步定時任務
Spring Scheduler雖然功能強大,但在分布式環(huán)境中需要注意任務重復執(zhí)行的問題。對于復雜的分布式調(diào)度需求,可以考慮使用Quartz或XXL-Job等專業(yè)調(diào)度框架。
希望本文能幫助你理解和掌握Spring Scheduler的使用,為你的項目開發(fā)提供便利。
到此這篇關(guān)于Spring Scheduler定時任務實戰(zhàn):從零掌握任務調(diào)度的文章就介紹到這了,更多相關(guān)Spring Scheduler定時任務內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringMVC?RESTFul實體類創(chuàng)建及環(huán)境搭建
這篇文章主要為大家介紹了SpringMVC?RESTFul實體類創(chuàng)建及環(huán)境搭建詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-05-05
SpringBoot中的yaml語法及靜態(tài)資源訪問問題
這篇文章主要介紹了SpringBoot中的yaml語法及靜態(tài)資源訪問問題,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-07-07
Java獲取網(wǎng)絡(luò)文件并插入數(shù)據(jù)庫的代碼
抓取各大網(wǎng)站的數(shù)據(jù)插入數(shù)據(jù)庫,這樣就不用為沒有數(shù)據(jù)而煩惱了2010-06-06
Java并發(fā)系列之CyclicBarrier源碼分析
這篇文章主要為大家詳細分析了Java并發(fā)系列之CyclicBarrier源碼,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-03-03
MyBatis的SQL執(zhí)行結(jié)果和客戶端執(zhí)行結(jié)果不一致問題排查
本文主要介紹了MyBatis的SQL執(zhí)行結(jié)果和客戶端執(zhí)行結(jié)果不一致問題排查,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2023-04-04
詳解Spring Cloud Zuul 服務網(wǎng)關(guān)
本篇文章主要介紹了詳解Spring Cloud Zuul 服務網(wǎng)關(guān),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-12-12

