Spring Boot定时器的使用

本章介绍Spring Boot中定时器的使用方法,总结个人对一些定时器用法的了解,如有错误欢迎指正。

定时器是什么?

定时器就是让程序定时执行某些任务,不需要人工手动执行。

为什么要使用定时器?

使用定时器,有很多好处。举个例子:在平台api对接中,请求通常需要携带token信息,而token有可能有固定时效。在token失效后,需要重新获取token信息。此时可以使用定时任务重新获取token。

怎么用定时器?

在Java中,有很多中方式处理定时任务。Timer,SpringBoot的@Scheduled注解,SpringBoot整合Quartz等方式。

整合Quartz

Spring Boot中使用Quartz是通过继承QuartzJobBean的方式,创建JobDetail和Trigger以达到定时效果。

1、创建SimpleScheduleExtendsQuartzJobTask任务类

package com.study.controller.schedule;

import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;

import java.util.Date;

public class SimpleScheduleExtendsQuartzJobTask extends QuartzJobBean {
    @Override
    protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        System.out.println("do schedule extends quartz job task " + new Date());
    }
}

2、创建JobDetail和Trigger。创建JobDetail时需要使用JobBuilder.newJob()方法添加任务类,withIdentity()方法指定key,build()方法进行构建。TriggerBuilder.forJob()方法添加JobDetail,withSchedule()方法添加schedule。schedule可以通过SimpleScheduleBuilder进行构建,withIntervalInSeconds()指明每次执行定时任务的间隔时间。

package com.study.config;

import com.study.controller.schedule.SimpleScheduleExtendsQuartzJobTask;
import org.quartz.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class SimpleScheduleConfig {
    @Bean
    public JobDetail simpleJobBeanDetails() {
        return JobBuilder.newJob(SimpleScheduleExtendsQuartzJobTask.class).withIdentity("simpleJobBean").storeDurably().build();
    }

    @Bean
    public Trigger simpleTrigger() {
        SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(15).repeatForever();
        return TriggerBuilder.newTrigger().withIdentity("simpleTrigger").forJob(simpleJobBeanDetails()).withSchedule(scheduleBuilder).build();
    }
}

上述方法已经可以到达效果,但仍然有需要改进的地方。我们来思考几个问题:

1、有大量的定时任务时如何解决?

2、当某个任务的执行时间需要修改时如何解决?

3、如何控制任务的启动和停止?

我们是否可以将所有的任务的类名,执行方法, 参数,以及执行时间保存在数据库中,通过对数据库的修改和访问,实现任务调度。

我们约定所有的定时脚本,都通过http://localhost:8080/authenticate/runScript?type=DoSimpleJobTask&args=aa方式进行访问,其中type指定类名,设置统一的访问方法为runScript()方法,通过反射的方式处理不同的任务。创建AuthenticateController

package com.study.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;

@RestController
public class AuthenticateController {
    @RequestMapping("authenticate/runScript")
    public String runScript(HttpServletRequest request, HttpServletResponse response) {
        String type = request.getParameter("type");
        if (null == type) {
            System.out.println("type is null");
            return "ok";
        }
        String argList = request.getParameter("args");
        type = type.replace("/", ".");
        String className = "script." + type;
        String methodName = "runScript";
        try {
            Class<?> customClass = Class.forName(className);
            Method method = customClass.getMethod(methodName, String.class);
            Object result = method.invoke(customClass.newInstance(), argList);
            if (null != result) {
                if ((Boolean) result) {

                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "ok";
    }
}

因为配置信息保存在数据库中,所以添加maven依赖访问数据库

        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.0.1</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>6.0.6</version>
        </dependency>

application.properties中设置mysql的连接配置。此处做下说明,设置时区serverTimezone=UTC,useSSL=false,因为mysql驱动为6.0版本,driver也改成了com.mysql.cj.jdbc.Driver

#mysql
spring.datasource.url=jdbc:mysql://localhost:3306/test?serverTimezone=UTC&useSSL=false
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

任务配置类

package com.study.orm;

public class ScheduleTaskConfig {
    private Integer id;
    private String name;
    private String task;
    private String cron;

    public ScheduleTaskConfig() {
    }

    public ScheduleTaskConfig(Integer id, String name, String task, String cron) {
        this.id = id;
        this.name = name;
        this.task = task;
        this.cron = cron;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getTask() {
        return task;
    }

    public void setTask(String task) {
        this.task = task;
    }

    public String getCron() {
        return cron;
    }

    public void setCron(String cron) {
        this.cron = cron;
    }
}

创建Mapper

package com.study.mapper;

import com.study.orm.ScheduleTaskConfig;
import org.apache.ibatis.annotations.Select;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public interface ScheduleTaskConfigMapper {
    @Select("select * from tb_task")
    List<ScheduleTaskConfig> fetchList();
}

service接口及实现

package com.study.service;

import com.study.orm.ScheduleTaskConfig;

import java.util.List;

public interface ScheduleTaskConfigService {
    List<ScheduleTaskConfig> fetchList();
}
package com.study.service.impl;

import com.study.mapper.ScheduleTaskConfigMapper;
import com.study.orm.ScheduleTaskConfig;
import com.study.service.ScheduleTaskConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class ScheduleTaskConfigServiceImpl implements ScheduleTaskConfigService {
    @Autowired
    private ScheduleTaskConfigMapper scheduleTaskConfigMapper;

    @Override
    public List<ScheduleTaskConfig> fetchList() {
        return scheduleTaskConfigMapper.fetchList();
    }
}

扫描mapper

package com;

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

@MapperScan("com.study.mapper")
@SpringBootApplication
@EnableScheduling
public class SpringbootApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootApplication.class, args);
    }

}

考虑到需要控制任务的启动和停止,采用ThreadPoolTaskScheduler进行任务调度。ThreadPoolTaskScheduler的schedule方法可以创建ScheduledFuture,ScheduledFuture中可以通过cancel(true),取消任务。

package com.study.controller.schedule;

import com.mchange.v1.util.MapUtils;
import com.study.orm.ScheduleTaskConfig;
import com.study.service.ScheduleTaskConfigService;
import com.study.utils.ServiceResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.TriggerContext;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ScheduledFuture;

@RequestMapping("schedule")
@RestController
public class ScheduleTaskManageController {

    @Autowired
    private ThreadPoolTaskScheduler threadPoolTaskScheduler;

    @Bean
    public ThreadPoolTaskScheduler getThreadPoolTaskScheduler() {
        return new ThreadPoolTaskScheduler();
    }

    private Map<Integer, ScheduledFuture> scheduledFutureMap = new HashMap<>();
    @Autowired
    private ScheduleTaskConfigService scheduleTaskConfigService;

    public void _init(){
        System.out.println("++++++++++++init++++++++++++++++");
        List<ScheduleTaskConfig> scheduleTaskConfigs = scheduleTaskConfigService.fetchList();
        if (!CollectionUtils.isEmpty(scheduleTaskConfigs)) {
            for (ScheduleTaskConfig scheduleTaskConfig : scheduleTaskConfigs) {
                ScheduledFuture future = threadPoolTaskScheduler.schedule(new Runnable() {
                    @Override
                    public void run() {
                        try {                  //task的内容设置为 curl "http://localhost:8080/authenticate/runScript?type=DoSimpleJobTask&args=aa"                 //请求通过authenticate/runScript映射,通过反射调用script中的任务
                            System.out.println(scheduleTaskConfig.getTask());
                            Process process = Runtime.getRuntime().exec(scheduleTaskConfig.getTask());
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }, new Trigger() {
                    @Override
                    public Date nextExecutionTime(TriggerContext triggerContext) {
                        return new CronTrigger(scheduleTaskConfig.getCron()).nextExecutionTime(triggerContext);
                    }
                });
                scheduledFutureMap.put(scheduleTaskConfig.getId(), future);
            }
        }
    }
    public void _destory(){
        System.out.println("++++++++++++++++destory++++++++++++");
        if (scheduledFutureMap != null && !scheduledFutureMap.isEmpty()) {
            for (Integer key : scheduledFutureMap.keySet()) {
                System.out.println("scheduledFutureMap :" + key);
                scheduledFutureMap.get(key).cancel(true);
            }
        }
        scheduledFutureMap.clear();
    }
    @RequestMapping("start")
    public String startScheduleTask() {
        _init();
        return "success";
    }

    @RequestMapping("stop")
    public String stopScheduleTask() {
        _destory();
        return "success";
    }
}

创建tb_task存放需要执行的任务

CREATE TABLE `tb_task` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(1000) DEFAULT NULL,
  `task` varchar(1000) DEFAULT NULL,
  `cron` varchar(1000) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8

select * from tb_task   

1 测试 curl "http://localhost:8080/authenticate/runScript?type=DoSimpleJobTask&args=aa" 0/35 * * * * ?

2 测试访问百度 curl "www.baidu.com" 0/35 * * * * ?

最后再script下添加不同的任务类

package script;

public class DoSimpleJobTask {
    public Boolean runScript(String args) {
        System.out.println("this is test schedule task : " + args);
        return false;
    }
}

附cron表达式说明:

0/35 * * * * ?

秒 分 时 天 年 ?

大概的思路就是这样,可以根据自己需要添加其它。如

  • 添加event,在项目启动之后,自动执行_init方法进行初始化。
  • 当重新添加或修改了task表中的数据时,可以先调用stop方法,取消所有的任务,然后再调用start方法添加。
  • 或者在页面编辑数据表中数据时,对应新增的方法,可以添加到schedule中,对于修改的方法可以根据id值获取到scheduledFutureMap的值,先取消,然后重新添加一个等等。
package com.study.event;

import com.study.controller.schedule.ScheduleTaskManageController;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

@Component
public class ApplicationStartup implements ApplicationListener<ApplicationStartedEvent> {
    @Override
    public void onApplicationEvent(ApplicationStartedEvent applicationStartedEvent) {
        System.out.println("++++++++++++++application started event+++++++++++++++");
        ScheduleTaskManageController scheduleTaskManageController = applicationStartedEvent.getApplicationContext().getBean(ScheduleTaskManageController.class);
        scheduleTaskManageController._destory();
        scheduleTaskManageController._init();
    }
}

如果有什么错误,或者有更好的处理方式,也可以留言告诉我

  

原文地址:https://www.cnblogs.com/noob-mengling/p/10756002.html

时间: 2024-08-29 23:47:25

Spring Boot定时器的使用的相关文章

spring boot使用定时器框架Quartz案例

一.你需要在项目中加入quartz-all-2.1.7.jar的jar包(我这里使用spring boot环境) 二.然后需要新建一个类去注册定时任务和销毁定时任务,这个类需要实现ServletContextListener的接口中的contextInitialized和contextDestroyed方法 三.接着就是去定义你自己的定时任务了,也就是再去新建一个类去实现Job的接口中的execute()方法,这个方法就是你在定时任务执行的时候要执行的内容 1, SimpleTrigger 指定

Spring Boot整合MyBatis学习总结

公司的很多项目都陆陆续续引入了Spring Boot,通过对Spring Boot的接触了解发现其真的是大大地简化了开发.简化了依赖配置,很多功能注解一下就可以实现,真的是太方便了.下面记录了一个Spring Boot的入门程序实现. 1,pom.xml文件: <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance&qu

Spring Boot 定时任务 @Scheduled

项目开发中经常需要执行一些定时任务,比如在每天凌晨,需要从 implala 数据库拉取产品功能活跃数据,分析处理后存入到 MySQL 数据库中.类似这样的需求还有许多,那么怎么去实现定时任务呢,有以下几种实现方式. Java 定时任务的几种实现方式 基于 java.util.Timer 定时器,实现类似闹钟的定时任务 使用 Quartz.elastic-job.xxl-job 等开源第三方定时任务框架,适合分布式项目应用 使用 Spring 提供的一个注解: @Schedule,开发简单,使用比

致Spring Boot初学者

1.引言 ????    Spring Boot是近两年来火的一塌糊涂,来这里的每一位同学,之前应该大致上学习了web项目开发方面的知识,正在努力成长过程中.因为最近有不少人来向我"请教",他们大都是一些刚入门的新手,对Spring Boot知识体系还不太了解,一方面听别人说Spring Boot配置简单.开发简单.部署简单,另一方面自己着手开始学习时,却发现头绪好多.有点迷茫,实在是每天回复很多人很麻烦,车轱辘话重复多遍自己也觉得有点无聊,所以在这里统一做个回复吧.        回

Spring Boot 热部署

需要在pom.xml文件中加如下代码: 1 <dependencies> 2 <dependency> 3 <groupId>org.springframework.boot</groupId> 4 <artifactId>spring-boot-devtools</artifactId> 5 <optional>true</optional> 6 </dependency> 7 </depe

《spring boot》8.2章学习时无法正常启动,报“ORA-00942: 表或视图不存在 ”

在学习<spring boot>一书的过程中,由于原书作者难免有一些遗漏的的地方,或者系统.软件版本不一致.框架更新等各种因素,完全安装书中源码页不能实现项目的正常启动 在8.2章节,演示JPA对oracle的支持时,配置文件中设置了如下代码,正常情况下应该支持数据库自动创建序列和表,但实际启动时却报错"ORA-00942: 表或视图不存在 " spring.datasource.driverClassName=oracle.jdbc.OracleDriver spring

Spring Boot 学习笔记1---初体验之3分钟启动你的Web应用

前言 早在去年就简单的使用了一下Spring Boot,当时就被其便捷的功能所震惊.但是那是也没有深入的研究,随着其在业界被应用的越来越广泛,因此决定好好地深入学习一下,将自己的学习心得在此记录,本文主要围绕以下几点进行说明: Spring Boot 简介 使用Spring Boot快速搭建一个Web应用如有不对的地方,请指正. 1. Spring Boot简介 Spring Boot是一个基于Spring的衍生框架,其主要的目的是帮助我们快速构建独立.生产级别的Spring的应用,其崇尚的理念

[web] spring boot 整合MyBatis

1.maven依赖 <?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

Spring Boot工程结构推荐

工程结构(最佳实践) Spring Boot框架本身并没有对工程结构有特别的要求,但是按照最佳实践的工程结构可以帮助我们减少可能会遇见的坑,尤其是Spring包扫描机制的存在,如果您使用最佳实践的工程结构,可以免去不少特殊的配置工作. 典型示例 root package结构:com.example.myproject 应用主类Application.java置于root package下,通常我们会在应用主类中做一些框架配置扫描等配置,我们放在root package下可以帮助程序减少手工配置来