@EnableAsync
@Async
@EnableScheduling
@Scheduled(cron = “”)
Cron表达式参数分别表示:
- 秒(0~59) 例如0/5表示每5秒
- 分(0~59)
- 时(0~23)
- 日(0~31)的某天,需计算
- 月(0~11)
- 周几( 可填1-7 或 SUN/MON/TUE/WED/THU/FRI/SAT)
@Scheduled:除了支持灵活的参数表达式cron之外,还支持简单的延时操作,例如 fixedDelay ,fixedRate 填写相应的毫秒数即可。
实战
首先在SpringBootApplication上开启任务功能
@EnableScheduling
然后在需要执行的方法上添加@Scheduled(cron = “”)注解
以下是源码,每隔5秒运行一次
@Service
public class MyTask {
@Scheduled(cron = "0/5 * * * * ?")
public void doTask(){
Logger logger = LoggerFactory.getLogger(MyTask.class);
logger.info("我运行了");
}
}
详细参照
https://www.cnblogs.com/mmzs/p/10161936.html
https://blog.csdn.net/qq_34125349/article/details/77430956
@RestController
public class TaskCtrl implements SchedulingConfigurer {
//时间:秒,分钟,小时,日期,月份,星期,年
private String expression = "0/1 * * * * *";
//编写更改调度时间的方法
@RequestMapping("change")
public String changeExpression() {
expression = "0/2 * * * * *";
return "changeExpression";
}
@Override
public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
//定时任务要执行的方法
Runnable task = new Runnable() {
@Override
public void run() {
System.out.println("changeTask" + new Date());
}
};
/**
* 调度实现的时间控制
*/
Trigger trigger = new Trigger() {
@Override
public Date nextExecutionTime(TriggerContext triggerContext) {
CronTrigger cronTrigger = new CronTrigger(expression);
return cronTrigger.nextExecutionTime(triggerContext);
}
};
scheduledTaskRegistrar.addTriggerTask(task, trigger);
}
}
可能会报错,但是不影响运行
@RestController
public class DynamicTaskCtrl {
@Autowired
private ThreadPoolTaskScheduler threadPoolTaskScheduler;
private ScheduledFuture<?> future;
@Bean
public ThreadPoolTaskScheduler trPoolTaskScheduler(){
return new ThreadPoolTaskScheduler();
}
/**
* 1,定义一个方法实现定时任务的启动
* 2,定义一个方法实现用于终止定时任务
* 3,修改定时任务时间:changeCron
*/
/**
* 启动定时器
* @return
*/
@RequestMapping("startTest")
public String StartTest(){
/**
* task:定时任务要执行的方法
* trigger:定时任务执行的时间
*/
future=threadPoolTaskScheduler.schedule(new myRunable(),new CronTrigger("0/5 * * * * *") );
return "startTest";
}
/**
* 停止定时任务
* @return
*/
@RequestMapping("endTask")
public String endTask(){
if(future!=null){
future.cancel(true);
}
System.out.println("endTask");
return "endTask";
}
/**
* 改变调度的时间
* 步骤:
* 1,先停止定时器
* 2,在启动定时器
*/
@RequestMapping("changeTask")
public String changeTask(){
//停止定时器
endTask();
//定义新的执行时间
future=threadPoolTaskScheduler.schedule(new myRunable(),new CronTrigger("0/10 * * * * *") );
//启动定时器
StartTest();
System.out.println("changeTask");
return "changeTask";
}
/**
* 定义定时任务执行的方法
* @author Admin
*
*/
public class myRunable implements Runnable{
@Override
public void run() {
System.out.println("定时任务要执行的方法"+new Date());
}
}
}
如果要消除报错,我们可以指定注入哪一个具体的Bean
@Qualifier("trPoolTaskScheduler")
以上我们使用的都是SpringBoot自带的Schedule框架,采用的是SimpleScheduleConfiguration
接下来我们来讲解怎么整合著名的Quartz
<dependency>
<groupId>org.quartz-schedulergroupId>
<artifactId>quartzartifactId>
<version>2.3.0version>
dependency>
public class QuartzManager {
private static SchedulerFactory gSchedulerFactory = new StdSchedulerFactory();
private static String JOB_GROUP_NAME = "MY_JOBGROUP_NAME";
private static String TRIGGER_GROUP_NAME = "MY_TRIGGERGROUP_NAME";
/**
* @param jobName 任务名
* @param cls 任务
* @Description: 添加一个定时任务,使用默认的任务组名,触发器名,触发器组名
*/
@SuppressWarnings({"rawtypes", "unchecked"})
public static void addJob(String jobName, Class cls, String cron) {
try {
Scheduler sched = gSchedulerFactory.getScheduler();
JobDetail job = JobBuilder.newJob(cls).withIdentity(jobName, JOB_GROUP_NAME).build();
// 表达式调度构建器
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
// 按新的cronExpression表达式构建一个新的trigger
Trigger trigger = TriggerBuilder.newTrigger().withIdentity(jobName, TRIGGER_GROUP_NAME)
.withSchedule(scheduleBuilder).build();
// 交给scheduler去调度
sched.scheduleJob(job, trigger);
// 启动
if (!sched.isShutdown()) {
sched.start();
System.err.println("添加任务:" + jobName);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/*带参数传入的任务创建*/
public static void addJob(String jobName, Class cls, String cron, Map<String,Object> params) {
try {
Scheduler sched = gSchedulerFactory.getScheduler();
JobDetail job = JobBuilder.newJob(cls).withIdentity(jobName, JOB_GROUP_NAME).build();
/*写入参数方便调度*/
if(params!=null&¶ms.size()!=0){
Iterator<String> iterator = params.keySet().iterator();
while(iterator.hasNext()){
String key = iterator.next();
Object value = params.get(key);
job.getJobDataMap().put(key,value);
}
}
// 表达式调度构建器
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
// 按新的cronExpression表达式构建一个新的trigger
Trigger trigger = TriggerBuilder.newTrigger().withIdentity(jobName, TRIGGER_GROUP_NAME)
.withSchedule(scheduleBuilder).build();
// 交给scheduler去调度
sched.scheduleJob(job, trigger);
// 启动
if (!sched.isShutdown()) {
sched.start();
System.err.println("添加任务:" + jobName);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* @param jobName 任务名
* @param jobGroupName 任务组名
* @param triggerName 触发器名
* @param triggerGroupName 触发器组名
* @param jobClass 任务
* @Description: 添加一个定时任务
*/
@SuppressWarnings({"rawtypes", "unchecked"})
public static void addJob(String jobName, String jobGroupName, String triggerName, String triggerGroupName,
Class jobClass, String cron) {
try {
Scheduler sched = gSchedulerFactory.getScheduler();
JobDetail job = JobBuilder.newJob(jobClass).withIdentity(jobName, jobGroupName).build();
// 表达式调度构建器
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
// 按新的cronExpression表达式构建一个新的trigger
Trigger trigger = TriggerBuilder.newTrigger().withIdentity(triggerName, triggerGroupName)
.withSchedule(scheduleBuilder).build();
sched.scheduleJob(job, trigger);
// 启动
if (!sched.isShutdown()) {
sched.start();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* @param jobName
* @Description: 修改一个任务的触发时间(使用默认的任务组名 , 触发器名 , 触发器组名)
*/
public static void modifyJobTime(String jobName, String cron) {
TriggerKey triggerKey = TriggerKey.triggerKey(jobName, TRIGGER_GROUP_NAME);
try {
Scheduler sched = gSchedulerFactory.getScheduler();
CronTrigger trigger = (CronTrigger) sched.getTrigger(triggerKey);
if (trigger == null) {
return;
}
String oldTime = trigger.getCronExpression();
if (!oldTime.equalsIgnoreCase(cron)) {
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
// 按新的cronExpression表达式重新构建trigger
trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
// 按新的trigger重新设置job执行
sched.rescheduleJob(triggerKey, trigger);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* @param oldJobName :原任务名
* @param jobName
* @param jobclass
* @param cron
* @Description:修改任务,(可以修改任务名,任务类,触发时间) 原理:移除原来的任务,添加新的任务
* @date 2018年5月23日 上午9:13:10
*/
@SuppressWarnings({"rawtypes", "unchecked"})
public static void modifyJob(String oldJobName, String jobName, Class jobclass, String cron) {
/*
* removeJob(oldJobName);
* addJob(jobName, jobclass, cron);
* System.err.println("修改任务"+oldJobName);
*/
TriggerKey triggerKey = TriggerKey.triggerKey(oldJobName, TRIGGER_GROUP_NAME);
JobKey jobKey = JobKey.jobKey(oldJobName, JOB_GROUP_NAME);
try {
Scheduler sched = gSchedulerFactory.getScheduler();
Trigger trigger = (Trigger) sched.getTrigger(triggerKey);
if (trigger == null) {
return;
}
sched.pauseTrigger(triggerKey);// 停止触发器
sched.unscheduleJob(triggerKey);// 移除触发器
sched.deleteJob(jobKey);// 删除任务
System.err.println("移除任务:" + oldJobName);
JobDetail job = JobBuilder.newJob(jobclass).withIdentity(jobName, JOB_GROUP_NAME).build();
// 表达式调度构建器
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
// 按新的cronExpression表达式构建一个新的trigger
Trigger newTrigger = TriggerBuilder.newTrigger().withIdentity(jobName, TRIGGER_GROUP_NAME)
.withSchedule(scheduleBuilder).build();
// 交给scheduler去调度
sched.scheduleJob(job, newTrigger);
// 启动
if (!sched.isShutdown()) {
sched.start();
System.err.println("添加新任务:" + jobName);
}
System.err.println("修改任务【" + oldJobName + "】为:" + jobName);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* @param triggerName
* @param triggerGroupName
* @Description: 修改一个任务的触发时间
*/
public static void modifyJobTime(String triggerName, String triggerGroupName, String cron) {
TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroupName);
try {
Scheduler sched = gSchedulerFactory.getScheduler();
CronTrigger trigger = (CronTrigger) sched.getTrigger(triggerKey);
if (trigger == null) {
return;
}
String oldTime = trigger.getCronExpression();
if (!oldTime.equalsIgnoreCase(cron)) {
// trigger已存在,则更新相应的定时设置
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
// 按新的cronExpression表达式重新构建trigger
trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
// 按新的trigger重新设置job执行
sched.resumeTrigger(triggerKey);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* @param jobName
* @Description 移除一个任务(使用默认的任务组名 , 触发器名 , 触发器组名)
*/
public static void removeJob(String jobName) {
TriggerKey triggerKey = TriggerKey.triggerKey(jobName, TRIGGER_GROUP_NAME);
JobKey jobKey = JobKey.jobKey(jobName, JOB_GROUP_NAME);
try {
Scheduler sched = gSchedulerFactory.getScheduler();
Trigger trigger = (Trigger) sched.getTrigger(triggerKey);
if (trigger == null) {
return;
}
sched.pauseTrigger(triggerKey);// 停止触发器
sched.unscheduleJob(triggerKey);// 移除触发器
sched.deleteJob(jobKey);// 删除任务
System.err.println("移除任务:" + jobName);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* @param jobName
* @param jobGroupName
* @param triggerName
* @param triggerGroupName
* @Description: 移除一个任务
*/
public static void removeJob(String jobName, String jobGroupName, String triggerName, String triggerGroupName) {
TriggerKey triggerKey = TriggerKey.triggerKey(jobName, triggerGroupName);
JobKey jobKey = JobKey.jobKey(jobName, jobGroupName);
try {
Scheduler sched = gSchedulerFactory.getScheduler();
sched.pauseTrigger(triggerKey);// 停止触发器
sched.unscheduleJob(triggerKey);// 移除触发器
sched.deleteJob(jobKey);// 删除任务
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* @param jobName
* @Description:暂停一个任务(使用默认组名)
*/
public static void pauseJob(String jobName) {
JobKey jobKey = JobKey.jobKey(jobName, JOB_GROUP_NAME);
try {
Scheduler sched = gSchedulerFactory.getScheduler();
sched.pauseJob(jobKey);
} catch (SchedulerException e) {
e.printStackTrace();
}
}
/**
* @param jobName
* @param jobGroupName
* @Description:暂停一个任务
*/
public static void pauseJob(String jobName, String jobGroupName) {
JobKey jobKey = JobKey.jobKey(jobName, jobGroupName);
try {
Scheduler sched = gSchedulerFactory.getScheduler();
sched.pauseJob(jobKey);
} catch (SchedulerException e) {
e.printStackTrace();
}
}
/**
* @param jobName
* @Description:恢复一个任务(使用默认组名)
*/
public static void resumeJob(String jobName) {
JobKey jobKey = JobKey.jobKey(jobName, JOB_GROUP_NAME);
try {
Scheduler sched = gSchedulerFactory.getScheduler();
sched.resumeJob(jobKey);
} catch (SchedulerException e) {
e.printStackTrace();
}
}
/**
* @param jobName
* @param jobGroupName
* @Description:恢复一个任务
* @date 2018年5月17日 上午9:56:09
*/
public static void resumeJob(String jobName, String jobGroupName) {
JobKey jobKey = JobKey.jobKey(jobName, jobGroupName);
try {
Scheduler sched = gSchedulerFactory.getScheduler();
sched.resumeJob(jobKey);
} catch (SchedulerException e) {
e.printStackTrace();
}
}
/**
* @Description:启动所有定时任务
*/
public static void startJobs() {
try {
Scheduler sched = gSchedulerFactory.getScheduler();
sched.start();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* @Description 关闭所有定时任务
*/
public static void shutdownJobs() {
try {
Scheduler sched = gSchedulerFactory.getScheduler();
if (!sched.isShutdown()) {
sched.shutdown();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* @param jobName
* @Description: 立即运行任务,这里的立即运行,只会运行一次,方便测试时用。
* @date 2018年5月17日 上午10:03:26
*/
public static void triggerJob(String jobName) {
JobKey jobKey = JobKey.jobKey(jobName, JOB_GROUP_NAME);
try {
Scheduler sched = gSchedulerFactory.getScheduler();
sched.triggerJob(jobKey);
} catch (SchedulerException e) {
e.printStackTrace();
}
}
/**
* @param jobName
* @param jobGroupName
* @Description: 立即运行任务,这里的立即运行,只会运行一次,方便测试时用。
* @date 2018年5月17日 上午10:03:26
*/
public static void triggerJob(String jobName, String jobGroupName) {
JobKey jobKey = JobKey.jobKey(jobName, jobGroupName);
try {
Scheduler sched = gSchedulerFactory.getScheduler();
sched.triggerJob(jobKey);
} catch (SchedulerException e) {
e.printStackTrace();
}
}
/**
* @param jobName 触发器名
* @Description: 获取任务状态
* NONE: 不存在
* NORMAL: 正常
* PAUSED: 暂停
* COMPLETE:完成
* ERROR : 错误
* BLOCKED : 阻塞
* @date 2018年5月21日 下午2:13:45
*/
public static String getTriggerState(String jobName) {
TriggerKey triggerKey = TriggerKey.triggerKey(jobName, TRIGGER_GROUP_NAME);
String name = null;
try {
Scheduler sched = gSchedulerFactory.getScheduler();
TriggerState triggerState = sched.getTriggerState(triggerKey);
name = triggerState.name();
} catch (SchedulerException e) {
e.printStackTrace();
}
return name;
}
/**
* @param cron
* @Description:获取最近8次执行时间
* @date 2018年5月24日 下午5:13:03
*/
public static List<String> getRecentTriggerTime(String cron) {
List<String> list = new ArrayList<String>();
try {
CronTriggerImpl cronTriggerImpl = new CronTriggerImpl();
cronTriggerImpl.setCronExpression(cron);
// 这个是重点,一行代码搞定
List<Date> dates = TriggerUtils.computeFireTimes(cronTriggerImpl, null, 8);
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
for (Date date : dates) {
list.add(dateFormat.format(date));
}
} catch (ParseException e) {
e.printStackTrace();
}
return list;
}
}
@Service
public class SignJob implements Job {
@Autowired
private MyMapper myMapper;
@Transactional
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
// 使得job对象可以通过注解实现依赖注入
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
/*将所有用户签到重置*/
myMapper.updateSign();
System.out.println("重置签到");
}
}
@PostConstruct
@Transactional
public void init() throws JobExecutionException {
/*装载版本信息*/
System.out.println("装载版本号...");
String version = getConfigByName("version").getConfigValue();
servletContext.setAttribute("version",version);
/*创建最基本的定时任务*/
System.out.println("创建定时任务...");
QuartzManager.addJob("smsJob", SmsJob.class,"0 0 0 * * ?");
String smsJob = QuartzManager.getTriggerState("smsJob");
System.out.println("创建短信任务完成,状态为:"+smsJob);
QuartzManager.addJob("signJob", SignJob.class,"0 0 6 * * ?");
String signJob = QuartzManager.getTriggerState("signJob");
System.out.println("创建签到任务完成,状态为:"+signJob);
/*QuartzManager.addJob("weatherJob", WeatherJob.class,"0 0 0/1 * * ?");
String weatherJob = QuartzManager.getTriggerState("weatherJob");
System.out.println("创建天气任务完成,状态为:"+weatherJob);*/
}
如果在传入cron时格式出现错误,则定时任务会停止,即使重新修改正确仍不能恢复,此时只能重新启动项目才能恢复。
引入mail的starter(在I/O中)
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-mailartifactId>
dependency>
配置邮箱的地址,用户名密码(在QQ邮箱账户中开启pop3支持,并获取备用密码,不是qq邮箱本身密码)
spring:
#邮箱相关
mail:
username: 自己的邮箱地址
password: 保密嘿嘿
host: smtp.qq.com
在单元测试中测试发送简单的邮件
@Autowired
JavaMailSenderImpl mailSender;
@Test
public void contextLoads() throws SQLException {
SimpleMailMessage message = new SimpleMailMessage();
//设置邮件标题
message.setSubject("hello beijing");
//设置邮件内容
message.setText("你好北京");
//设置收件人
message.setTo("[email protected]");
//设置发件人
message.setFrom("[email protected]");
mailSender.send(message);
/* System.out.println(dataSource.getClass());
Connection connection = dataSource.getConnection();
System.out.println(connection);
connection.close();*/
}
当然,在有的时候会发送不成功,提示我们不是安全连接(ssl),这时候需要设置邮件为安全连接
spring:
#邮箱相关
mail:
username: [email protected]
password: vshucimzjnxybgia
host: smtp.qq.com
properties:
mail.smtp.ssl.enable: true
测试成功!