Java定时任务

本文使用Spring框架自带的Scheduled注解来实现Java定时任务,只说明不停止服务自定义时间执行任务。

配置

在Spring框架的主启动类上加上@EnableScheduling注解,表示开启定时任务,具体的执行任务在其他类。

执行

表达式

在执行类的方法上增加@Scheduled(cron = "* * * * * ?")注解,

​ cron的参数示例如下:

参数 允许的特殊字符
0-59 , - * /
0-59 , - * /
小时 0-23 , - * /
1-31 , - * ? / L W C
1-12 或者 JAN-DEC , - * /
星期 1-7 或者 SUN-SAT , - * ? / L C #
年(可选) 留空, 1970-2099 , - * /

0 0 10,14,16 * * ? 每天上午10点,下午2点,4点

0 0/30 9-17 * * ? 朝九晚五工作时间内每半小时

0 0 12 ? * WED 表示每个星期三中午12点

“0 0 12 * * ?” 每天中午12点触发

“0 15 10 ? * *” 每天上午10:15触发

“0 15 10 * * ?” 每天上午10:15触发

“0 15 10 * * ? *” 每天上午10:15触发

“0 15 10 * * ? 2005” 2005年的每天上午10:15触发

“0 * 14 * * ?” 在每天下午2点到下午2:59期间的每1分钟触发

“0 0/5 14 * * ?” 在每天下午2点到下午2:55期间的每5分钟触发

“0 0/5 14,18 * * ?” 在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发

“0 0-5 14 * * ?” 在每天下午2点到下午2:05期间的每1分钟触发

“0 10,44 14 ? 3 WED” 每年三月的星期三的下午2:10和2:44触发

“0 15 10 ? * MON-FRI” 周一至周五的上午10:15触发

“0 15 10 15 * ?” 每月15日上午10:15触发

“0 15 10 L * ?” 每月最后一日的上午10:15触发

“0 15 10 ? * 6L” 每月的最后一个星期五上午10:15触发

“0 15 10 ? * 6L 2002-2005” 2002年至2005年的每月的最后一个星期五上午10:15触发

“0 15 10 ? * 6#3” 每月的第三个星期五上午10:15触发

有些子表达式能包含一些范围或列表

例如:子表达式(天(星期))可以为 “MON-FRI”,“MON,WED,FRI”,“MON-WED,SAT”

“*”字符代表所有可能的值

因此,“”在子表达式()里表示每个月的含义,“”在子表达式(天(星期))表示星期的每一天

动态修改时间
资源文件配置变量

cron使用自定义变量,在SpringCloud的配置中心修改即可不重启服务使用修改后的变量

保存到数据库

将时间表达式保存到数据库,从程序中读取,就可以随时修改任务时间

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
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for spring_scheduled_cron
-- ----------------------------
DROP TABLE IF EXISTS `spring_scheduled_cron`;
CREATE TABLE `spring_scheduled_cron` (
`cron_id` varchar(10) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '主键id',
`cron_key` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '定时任务完整类名',
`cron_expression` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'cron表达式',
`task_explain` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '任务描述',
`status` tinyint(0) NOT NULL DEFAULT 1 COMMENT '状态,1:正常;2:停用',
PRIMARY KEY (`cron_id`) USING BTREE,
UNIQUE INDEX `cron_key`(`cron_key`) USING BTREE,
UNIQUE INDEX `cron_key_unique_idx`(`cron_key`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '定时任务表' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of spring_scheduled_cron
-- ----------------------------
INSERT INTO `spring_scheduled_cron` VALUES ('1', 'com.example.springcloud0614.controller.DynamicPrintTask', '*/5 * * * * ?', '定时任务描述', 1);
INSERT INTO `spring_scheduled_cron` VALUES ('2', 'com.example.springcloud0614.controller.DynamicPrintTask1', '*/5 * * * * ?', '定时任务描述1', 1);
INSERT INTO `spring_scheduled_cron` VALUES ('3', 'com.example.springcloud0614.controller.DynamicPrintTask2', '*/5 * * * * ?', '定时任务描述2', 1);

SET FOREIGN_KEY_CHECKS = 1;
Spring工具类
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
package com.example.springcloud0614.config;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.LocaleResolver;

import javax.servlet.http.HttpServletRequest;
import java.util.Locale;

/**
* SpringUtils工具类获取bean
* Description: <br/>
* date: 2020/4/26 14:59<br/>
*
* @author lvxk<br />
* @since JDK 1.8
*/
@Component
public class SpringUtils implements ApplicationContextAware {
private static ApplicationContext applicationContext;

@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}

public static <T> T getBean(Class<T> tClass){
return applicationContext.getBean(tClass);
}

public static <T> T getBean(String name, Class<T> type) {
return applicationContext.getBean(name, type);
}

public static HttpServletRequest getCurrentReq() {
ServletRequestAttributes requestAttrs = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (requestAttrs == null) {
return null;
}
return requestAttrs.getRequest();
}

public static String getMessage(String code, Object... args) {
LocaleResolver localeResolver = getBean(LocaleResolver.class);
Locale locale = localeResolver.resolveLocale(getCurrentReq());
return applicationContext.getMessage(code, args, locale);
}

}
定时任务类
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
package com.example.springcloud0614.config;

import com.example.springcloud0614.entity.ScheduledCron;
import com.example.springcloud0614.service.ScheduledCronService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.config.Task;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.util.Assert;

import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

/**
* @Author kuang
* @Date 2021/11/27 17:55
*/
@Configuration
@Slf4j
public class ScheduledConfig implements SchedulingConfigurer {

@Autowired
private ApplicationContext context;
@Autowired
private ScheduledCronService cronService;

/**
* Callback allowing a {@link TaskScheduler
* TaskScheduler} and specific {@link Task Task}
* instances to be registered against the given the {@link ScheduledTaskRegistrar}.
*
* @param taskRegistrar the registrar to be configured.
*/
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
for (ScheduledCron scheduledCron : cronService.findAll()) {
log.info("表达式:{}", scheduledCron);
Class<?> clazz;
Object task;
try {
clazz = Class.forName(scheduledCron.getCronKey());
task = context.getBean(clazz);
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException("spring_scheduled_cron表数据" + scheduledCron.getCronKey() + "有误", e);
} catch (BeansException e) {
throw new IllegalArgumentException(scheduledCron.getCronKey() + "未纳入到spring管理", e);
}
Assert.isAssignable(ScheduledOfTask.class, task.getClass(), "定时任务类必须实现ScheduledOfTask接口");
// 可以通过改变数据库数据进而实现动态改变执行周期
//所有定时任务类只需要实现这个接口并相应的在数据库插入一条记录,那么在微服务启动的时候,就会被自动注册到Spring的定时任务里
taskRegistrar.addTriggerTask(((Runnable) task),
triggerContext -> {
String cronExpression = cronService.findByCronId(scheduledCron.getCronId()).getCronExpression();
return new CronTrigger(cronExpression).nextExecutionTime(triggerContext);
}
);
}
}

@Bean
public Executor taskExecutor() {
return Executors.newScheduledThreadPool(10);
}
}
启用和禁用功能

如果不需要可以不实现

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
package com.example.springcloud0614.config;

import com.example.springcloud0614.entity.ScheduledCron;
import com.example.springcloud0614.service.ScheduledCronService;

/**
* @Author kuang
* @Date 2021/11/27 20:41
*/
public interface ScheduledOfTask extends Runnable {

/**
* 定时任务方法
*/
void execute();

/**
* 实现控制定时任务启用或禁用的功能
*/
@Override
default void run() {
ScheduledCronService cronService = SpringUtils.getBean(ScheduledCronService.class);
ScheduledCron scheduledCron = cronService.findByCronKey(this.getClass().getName());
if (scheduledCron.getStatus() == 0) {
return;
}
execute();
}
}

定时任务

其余方法一致

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.example.springcloud0614.controller;

import com.example.springcloud0614.config.ScheduledOfTask;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;

/**
* @Author kuang
* @Date 2021/11/27 20:48
*/
@Slf4j
@Controller
public class DynamicPrintTask implements ScheduledOfTask {
private int i;

@Override
public void execute() {
log.info("DynamicPrintTask execute times:{}", ++i);
}
}