springboot之quartz动态可控定时任务

news2024/12/28 3:23:52

Quartz

        Quartz是一个开源的任务调度框架,可以用来实现定时任务的调度,如定时发送邮件、定时备份数据等。Quartz具有很高的可靠性和灵活性,支持集群部署和分布式调度,并且提供了丰富的API和插件,可以轻松实现复杂的调度需求。Quartz的工作原理是通过Job和Trigger两个核心概念来实现的,Job是具体需要执行的任务,Trigger用来触发任务的执行时机。在Quartz中,可以通过定义各种Trigger来实现不同的调度策略,如简单调度、Cron调度等。Quartz还提供了很多内置的Job和Trigger实现,如邮件发送、HTTP请求等,可以方便地用来实现常见的任务调度需求。

 核心

        Quartz的核心组件包含Scheduler、Job和Trigger。这三个核心组件共同组成了Quartz的任务调度机制,使得开发人员可以通过配置简单的定时任务来实现复杂的调度策略。

Scheduler

        是Quartz的核心组件,它负责调度和执行任务。Scheduler有一个任务管理器,负责维护任务列表,并根据Triggers的定义来决定何时执行任务。Scheduler还提供了API,通过API可以动态地添加、删除和修改任务。

  1. 管理作业:Scheduler负责管理Quartz中的所有作业,包括创建、修改和删除作业。

  2. 触发器管理:Scheduler负责管理Quartz中的所有触发器,包括创建、修改和删除触发器。

  3. 作业执行:Scheduler负责执行Quartz中的所有作业,并记录作业执行情况。

  4. 调度管理:Scheduler负责管理Quartz的整个调度过程,包括启动调度器、暂停调度器和恢复调度器。

  5. 监控和统计:Scheduler提供了各种监控和统计信息,以帮助开发人员了解Quartz的运行状况。

Job

        Job是一个接口,它定义了需要执行的逻辑,开发人员需要实现该接口,并在其中编写需要执行的业务逻辑。

Trigger

        Trigger是一个定义了任务执行时间的对象,Quartz提供了多种类型的Trigger,例如

  1. SimpleTrigger:简单触发器,用于在指定时间执行一次或者按照指定的时间间隔重复执行。

  2. CronTrigger:Cron触发器,用于按照类似于Unix/Linux系统中Cron表达式的方式指定复杂的时间计划,例如每周五下午五点执行。

  3. CalendarIntervalTrigger:日历间隔触发器,用于按照在指定时间间隔内执行的时间计划,例如每隔一小时执行一次。

  4. DailyTimeIntervalTrigger:每日时间间隔触发器,用于按照每日指定时间间隔执行的时间计划,例如每天上午10点和下午3点各执行一次。

  实战

    我这里采用jdk17   Springboot3.1.2+mybatis-flex+openapi3与mysql来实现一个动态控制任务的demo

<properties>
        <java.version>17</java.version>
        <mybatis-flex.version>1.7.2</mybatis-flex.version>
        <fastjson.version>1.2.47</fastjson.version>
</properties>

<dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.github.xiaoymin</groupId>
                <artifactId>knife4j-dependencies</artifactId>
                <version>4.1.0</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        
       
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-quartz</artifactId>
        </dependency>
        <dependency>
            <groupId>com.mybatis-flex</groupId>
            <artifactId>mybatis-flex-spring-boot-starter</artifactId>
            <version>${mybatis-flex.version}</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>${fastjson.version}</version>
        </dependency>
    </dependencies>
mybatis-flex:
  datasource:
    ds1:
      url: jdbc:mysql://127.0.0.1:3306/test
      username: root
      password: zkb.com
springdoc:
  swagger-ui:
    path: /swagger-ui.html
    tags-sorter: alpha
  api-docs:
    path: /v3/api-docs
  group-configs:
    - group: '接口'
      paths-to-match: '/**'
      packages-to-scan: com.zxs.springbootmybatisflex.controller.client
  default-flat-param-object: true

package com.zxs.springbootmybatisflex.quartz;

import com.zxs.springbootmybatisflex.entity.SysJob;
import com.zxs.springbootmybatisflex.zenum.JobEnum;
import org.quartz.Job;
import org.quartz.JobExecutionContext;

public class QuartzJob implements Job {

    @Override
    public void execute(JobExecutionContext context) {
        SysJob sysJob = (SysJob) context.getJobDetail().getJobDataMap().get(JobEnum.Key.getCode());
        QuartzUtil.runJob(sysJob.getInvokeTarget());
    }
}

package com.zxs.springbootmybatisflex.quartz;

import com.zxs.springbootmybatisflex.entity.JobVo;
import com.zxs.springbootmybatisflex.entity.SysJob;
import com.zxs.springbootmybatisflex.uitl.SpringContextHolder;
import com.zxs.springbootmybatisflex.zenum.ActionEnum;
import com.zxs.springbootmybatisflex.zenum.JobEnum;
import lombok.SneakyThrows;
import org.quartz.*;
import org.springframework.util.ObjectUtils;

import java.lang.reflect.Method;
import java.util.Date;

public class QuartzUtil {

    @SneakyThrows
    public static void startJob(SysJob sysJob) {
        // 获取调度器 Scheduler
        Scheduler scheduler = SchedulerStatic.getScheduler();
        Integer jobId = sysJob.getJobId();
        String jobGroup = sysJob.getJobGroup();

        // 构造一个job
        JobKey jobKey = JobKey.jobKey(jobId.toString(), jobGroup);
        JobDetail jobDetail = JobBuilder
                .newJob(QuartzJob.class)
                .withIdentity(jobKey)
                .build();

        // 构造cron调度器
        CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(sysJob.getCronExpression());
        getMisfirePolicy(sysJob, cronScheduleBuilder);

        // 构造触发器 trigger
        TriggerKey triggerKey = TriggerKey.triggerKey(jobId.toString(), jobGroup);
        CronTrigger trigger = TriggerBuilder
                .newTrigger()
                .withIdentity(triggerKey)
                .withSchedule(cronScheduleBuilder)
                .build();

        // 放入job信息,为后续执行改任务的具体方法做铺垫(需要反射调用), 在execute中获取并应用
        jobDetail.getJobDataMap().put(JobEnum.Key.getCode(), sysJob);
        // 判断该任务是否存在,修改任务,先删除然后添加
        if (scheduler.checkExists(jobKey)) {
            // 防止创建时存在数据问题 先移除,然后在执行创建操作
            scheduler.deleteJob(jobKey);
        }
        // 判断任务是否过期
        CronExpression cron = new CronExpression(sysJob.getCronExpression());
        Date nextValidTimeAfter = cron.getNextValidTimeAfter(new Date(System.currentTimeMillis()));
        if (!ObjectUtils.isEmpty(nextValidTimeAfter)) {
            // 执行调度任务
            scheduler.scheduleJob(jobDetail, trigger);
        }
    }


    @SneakyThrows
    public static void doJob(JobVo sysJob,String action) {
        // 获取调度器 Scheduler
        Scheduler scheduler = SchedulerStatic.getScheduler();
        // 构造一个job
        JobKey jobKey = JobKey.jobKey(sysJob.getJobId().toString(), sysJob.getJobGroup());
        // 判断该任务是否存在,修改任务,先删除然后添加
        if (!scheduler.checkExists(jobKey)) {
            return;
        }
        switch (action){
            case ActionEnum.check:    //
                System.out.println(true);
                break;
            case ActionEnum.pause:   //暂停
                scheduler.pauseJob(jobKey);
                break;
            case ActionEnum.resume:   //继续
                scheduler.resumeJob(jobKey);
                break;
            case ActionEnum.delete:    //删除
                scheduler.deleteJob(jobKey);
                break;
            default:
                scheduler.checkExists(jobKey);
                break;
        }

    }

    private static void getMisfirePolicy(SysJob sysJob, CronScheduleBuilder cronScheduleBuilder) {
        String s= sysJob.getMisfirePolicy();
        if(s.equals(JobEnum.MISFIRE_DEFAULT.getCode())){
        }else if(s.equals(JobEnum.MISFIRE_IGNORE.getCode())){
            cronScheduleBuilder.withMisfireHandlingInstructionIgnoreMisfires();
        } else if(s.equals(JobEnum.MISFIRE_FIRE_AND_PROCEED.getCode())){
            cronScheduleBuilder.withMisfireHandlingInstructionFireAndProceed();
        } else if(s.equals(JobEnum.MISFIRE_DO_NOTHING.getCode())){
            cronScheduleBuilder.withMisfireHandlingInstructionDoNothing();
        }else{
            throw new RuntimeException("The task misfire policy '" + sysJob.getMisfirePolicy() + "' cannot be used in cron schedule tasks");
        }
    }

    public static void invokeMethod(Object bean, String methodName) throws Exception {
        Method method = bean.getClass().getMethod(methodName);
        method.invoke(bean);
    }
    public static void runJob(String invokeTarget){
        String[] parts = invokeTarget.split("\\.");
        String beanName = parts[0];
        String methodName = parts[1];
        Object bean = SpringContextHolder.getBean(beanName);
        try {
            QuartzUtil.invokeMethod(bean, methodName);
        } catch (Exception e) {
            throw new RuntimeException("Error running job", e);
        }
    }


}
package com.zxs.springbootmybatisflex.quartz;

import org.quartz.Scheduler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class SchedulerStatic {

    private static Scheduler scheduler;

    @Autowired
    public SchedulerStatic(Scheduler scheduler) {
        SchedulerStatic.scheduler = scheduler;
    }

    public static Scheduler getScheduler() {
        return scheduler;
    }
}
package com.zxs.springbootmybatisflex.config;

import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Configuration;

@EnableCaching
@Configuration
public class CacheConfig {
}
package com.zxs.springbootmybatisflex.config;

import com.mybatisflex.core.mybatis.FlexConfiguration;
import com.mybatisflex.spring.boot.ConfigurationCustomizer;
import org.apache.ibatis.logging.stdout.StdOutImpl;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MyConfigurationCustomizer implements ConfigurationCustomizer {

    @Override
    public void customize(FlexConfiguration configuration) {
        configuration.setLogImpl(StdOutImpl.class);
    }
}
package com.zxs.springbootmybatisflex.config;

import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.License;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableKnife4j
public class SwaggerConfig {
    // 设置 openapi 基础参数
    @Bean
    public OpenAPI customOpenAPI() {
        return new OpenAPI()
                .info(new Info()
                        .title("zxs API 管理")
                        .version("1.0")
                        .description("探索mybatis-flex与quartz demo")
                        .license(new License().name("Apache 2.0")));
    }
}
package com.zxs.springbootmybatisflex.controller.client;


import com.zxs.springbootmybatisflex.entity.JobVo;
import com.zxs.springbootmybatisflex.entity.SysJob;
import com.zxs.springbootmybatisflex.quartz.QuartzUtil;
import com.zxs.springbootmybatisflex.service.SysJobService;
import com.zxs.springbootmybatisflex.uitl.DataResult;
import com.zxs.springbootmybatisflex.zenum.ActionEnum;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.*;

import java.util.List;

/**
 * 定时任务调度表(SysJob)表控制层
 *
 * @author makejava
 * @since 2023-10-19 09:17:58
 */
@RestController
@Tag(name = "任务中心")
@RequestMapping("sysJob")
public class SysJobController {
    /**
     * 服务对象
     */
    @Resource
    private SysJobService sysJobService;




    @Operation(summary = "获取任务列表", description = "获取任务列表")
    @PostMapping("/getJobList")
    public DataResult<List<SysJob>> getJobList() {
        DataResult<List<SysJob>> result = new DataResult<>();
        List<SysJob> list =sysJobService.list();
        result.setData(list);
        return result;
    }

    @Operation(summary = "根据主键获取任务信息")
    @PostMapping("/getJobById/{id}")
    public DataResult<SysJob> getJobById(@PathVariable(value = "id") Integer id) {
        DataResult<SysJob> result = new DataResult<>();
        result.setData(sysJobService.getById(id));
        return result;
    }
    @PostMapping("/saveJob")
    @Operation(summary="新增/修改任务",description="新增/修改任务,并加入的调度器中执行")
    public  DataResult<Boolean> saveJob(SysJob sysJob) {
        DataResult<Boolean> result = new DataResult<>();
        result.setData(sysJobService.saveOrUpdate(sysJob));
        QuartzUtil.startJob(sysJob);
        return result;
    }

    @PostMapping("/deleteJob/{id}")
    @Operation(summary="删除任务",description="删除任务,并删除调度器中执行的该任务")
    public DataResult<Boolean> deleteJob(@PathVariable(value = "id") Integer id) {
        DataResult<Boolean> result = new DataResult<>();
        SysJob sysJob = sysJobService.getById(id);
        boolean data = this.sysJobService.removeById(id);
        result.setData(data);
        if(!ObjectUtils.isEmpty(sysJob)) {
            JobVo jobVo = new JobVo();
            jobVo.setJobId(sysJob.getJobId());
            jobVo.setJobGroup(sysJob.getJobGroup());
            QuartzUtil.doJob(jobVo, ActionEnum.delete);
        }
        return result;
    }


    @GetMapping("/getJobAndJoin/{id}")
    @Operation(summary="开启/关闭任务",description="获取一个任务,修改任务,并加入/丢出的调度器中执行")
    public DataResult<SysJob> getJobAndJoin(@PathVariable(value = "id") Integer id) {
        DataResult<SysJob> result = new DataResult<>();
        SysJob one = sysJobService.getById(id);
        if("0".equals(one.getStatus())){
            one.setStatus("1");
        }else{
            one.setStatus("0");
        }
        sysJobService.updateById(one);
        QuartzUtil.startJob(one);
        result.setData(one);
        return result;
    }



    @PostMapping("/runJob")
    @Operation(summary="手动执行任务",description="手动执行任务")
    public DataResult<Boolean> runJob(String invokeTarget) {
        DataResult<Boolean> result = new DataResult<>();
        QuartzUtil.runJob(invokeTarget);
        return result;
    }



    @PostMapping("/checkJob")
    @Operation(summary="检查任务",description="检测任务")
    public  DataResult<Boolean> checkJob(JobVo jobVo) {
        DataResult<Boolean> result = new DataResult<>();
        QuartzUtil.doJob(jobVo,ActionEnum.check);
        return result;
    }

    @PostMapping("/resumeJob")
    @Operation(summary="恢复任务",description="恢复任务")
    public  DataResult<Boolean> resumeJob(JobVo sysJob) {
        DataResult<Boolean> result = new DataResult<>();
        QuartzUtil.doJob(sysJob,ActionEnum.resume);
        return result;
    }

    @PostMapping("/pauseJob")
    @Operation(summary="暂停任务",description="暂停任务")
    public  DataResult<Boolean> pauseJob(JobVo sysJob) {
        DataResult<Boolean> result = new DataResult<>();
        QuartzUtil.doJob(sysJob,ActionEnum.pause);
        return result;
    }

}

package com.zxs.springbootmybatisflex.dao;


import com.mybatisflex.core.BaseMapper;
import com.zxs.springbootmybatisflex.entity.SysJob;
import org.apache.ibatis.annotations.Mapper;

/**
 * 定时任务调度表(SysJob)表数据库访问层
 *
 * @author makejava
 * @since 2023-10-19 09:17:58
 */
@Mapper
public interface SysJobDao extends BaseMapper<SysJob> {

}

package com.zxs.springbootmybatisflex.entity;

import lombok.Data;

import java.io.Serializable;

@Data
public class JobVo implements Serializable {
    private Integer jobId;
    private String jobGroup;
}
package com.zxs.springbootmybatisflex.entity;


import com.fasterxml.jackson.annotation.JsonFormat;
import com.mybatisflex.annotation.Id;
import com.mybatisflex.annotation.KeyType;
import com.mybatisflex.annotation.Table;
import com.mybatisflex.core.activerecord.Model;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;

import java.util.Date;

/**
 * 定时任务调度表(SysJob)表实体类
 *
 * @author makejava
 * @since 2023-10-19 09:17:59
 */
@Data
@Schema(description="定时任务调度表")
@Table("sys_job")
public class SysJob extends Model<SysJob> {

	@Schema(description="任务ID")
    @Id(keyType = KeyType.Auto)
    private Integer jobId;


	@Schema(description="任务名称")
    private String jobName;


	@Schema(description="任务组名")
    private String jobGroup;


	@Schema(description="调用目标字符串")
    private String invokeTarget;


	@Schema(description="cron执行表达式")
    private String cronExpression;


	@Schema(description="计划执行错误策略(1立即执行 2执行一次 3放弃执行)")
    private String misfirePolicy;


	@Schema(description="是否并发执行(0允许 1禁止)")
    private String concurrent;


	@Schema(description="状态(0正常 1暂停)")
    private String status;


	@Schema(description="创建者")
    private String createBy;


	@Schema(description="创建时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date createTime;


	@Schema(description="更新者")
    private String updateBy;


	@Schema(description="更新时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date updateTime;


	@Schema(description="备注信息")
    private String remark;


	@Schema(description="0-正常,1-删除")
    private Integer deleteStatus;

}

package com.zxs.springbootmybatisflex.exception.code;


public enum BaseResponseCode implements ResponseCodeInterface {
    /**
     * 这个要和前段约定好
     * 引导用户去登录界面的
     * code=401001 引导用户重新登录
     * code=401002 token 过期刷新token
     * code=401008 无权限访问
     */
    SUCCESS(200,"操作成功"),
    SYSTEM_BUSY(500001, "系统繁忙,请稍候再试"),
    OPERATION_ERRO(500002,"操作失败"),
    METHODARGUMENTNOTVALIDEXCEPTION(500003, "方法参数校验异常"),
    ;

    /**
     * 错误码
     */
    private final int code;
    /**
     * 错误消息
     */
    private final String msg;

    BaseResponseCode(int code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    @Override
    public int getCode() {
        return code;
    }

    @Override
    public String getMsg() {
        return msg;
    }
}
package com.zxs.springbootmybatisflex.exception.code;


public interface ResponseCodeInterface {
    int getCode();

    String getMsg();
}
package com.zxs.springbootmybatisflex.exception.handler;


import com.zxs.springbootmybatisflex.exception.BusinessException;
import com.zxs.springbootmybatisflex.exception.code.BaseResponseCode;
import com.zxs.springbootmybatisflex.uitl.DataResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.util.List;


@RestControllerAdvice
@Slf4j
public class RestExceptionHandler {


    @ExceptionHandler(Exception.class)
    public <T> DataResult<T> handleException(Exception e){
        log.error("Exception,exception:{}", e);
        return DataResult.getResult(BaseResponseCode.SYSTEM_BUSY);
    }


    @ExceptionHandler(value = BusinessException.class)
    <T> DataResult<T> businessExceptionHandler(BusinessException e) {
        log.error("BusinessException,exception:{}", e);
        return new DataResult<>(e.getMessageCode(),e.getDetailMessage());
    }


    @ExceptionHandler(MethodArgumentNotValidException.class)
    <T> DataResult<T> methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e) {
        log.error("methodArgumentNotValidExceptionHandler bindingResult.allErrors():{},exception:{}", e.getBindingResult().getAllErrors(), e);
        List<ObjectError> errors = e.getBindingResult().getAllErrors();
        return createValidExceptionResp(errors);
    }
    private <T> DataResult<T> createValidExceptionResp(List<ObjectError> errors) {
        String[] msgs = new String[errors.size()];
        int i = 0;
        for (ObjectError error : errors) {
            msgs[i] = error.getDefaultMessage();
            log.info("msg={}",msgs[i]);
            i++;
        }
        return DataResult.getResult(BaseResponseCode.METHODARGUMENTNOTVALIDEXCEPTION.getCode(), msgs[0]);
    }
}
package com.zxs.springbootmybatisflex.exception;


import com.zxs.springbootmybatisflex.exception.code.ResponseCodeInterface;

public class BusinessException extends RuntimeException{
    /**
     * 异常编号
     */
    private final int messageCode;

    /**
     * 对messageCode 异常信息进行补充说明
     */
    private final String detailMessage;

    public BusinessException(int messageCode,String message) {
        super(message);
        this.messageCode = messageCode;
        this.detailMessage = message;
    }
    /**
     * 构造函数
     * @param code 异常码
     */
    public BusinessException(ResponseCodeInterface code) {
        this(code.getCode(), code.getMsg());
    }

    public int getMessageCode() {
        return messageCode;
    }

    public String getDetailMessage() {
        return detailMessage;
    }
}
package com.zxs.springbootmybatisflex.exception;


import com.zxs.springbootmybatisflex.exception.code.ResponseCodeInterface;

public class RoleSaveException extends RuntimeException{
    /**
     * 异常编号
     */
    private final int messageCode;

    /**
     * 对messageCode 异常信息进行补充说明
     */
    private final String detailMessage;

    public RoleSaveException(int messageCode, String message) {
        super(message);
        this.messageCode = messageCode;
        this.detailMessage = message;
    }
    /**
     * 构造函数
     * @param code 异常码
     */
    public RoleSaveException(ResponseCodeInterface code) {
        this(code.getCode(), code.getMsg());
    }

    public int getMessageCode() {
        return messageCode;
    }

    public String getDetailMessage() {
        return detailMessage;
    }
}
package com.zxs.springbootmybatisflex.quartz.task;

import org.springframework.stereotype.Service;

@Service("task")
public class DoTask {
    public void sout() {
        System.out.println("我是干输出的");
    }
    public void ceshi() {
        System.out.println("我是干测试的");
    }
    public void buzhidao() {
        System.out.println("我不知道干啥的");
    }
    public void dajiangyou() {
        System.out.println("我是打酱油的");
    }
    public void chuiniu() {
        System.out.println("我是吹牛的");
    }

    public void maren() {
        System.out.println("我是骂人的");
    }
    public void duiren() {
        System.out.println("我是怼人的");
    }
    public void yaofan() {
        System.out.println("我是要饭的");
    }
    public void chifan() {
        System.out.println("我是吃饭的");
    }
}
package com.zxs.springbootmybatisflex.service.impl;

import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.zxs.springbootmybatisflex.dao.SysJobDao;
import com.zxs.springbootmybatisflex.entity.SysJob;
import com.zxs.springbootmybatisflex.service.SysJobService;
import org.springframework.stereotype.Service;

/**
 * 定时任务调度表(SysJob)表服务实现类
 *
 * @author makejava
 * @since 2023-10-19 09:17:59
 */
@Service
public class SysJobServiceImpl extends ServiceImpl<SysJobDao, SysJob> implements SysJobService {


}

package com.zxs.springbootmybatisflex.service;

import com.mybatisflex.core.service.IService;
import com.zxs.springbootmybatisflex.entity.SysJob;

/**
 * 定时任务调度表(SysJob)表服务接口
 *
 * @author makejava
 * @since 2023-10-19 09:17:59
 */
public interface SysJobService extends IService<SysJob> {

}

package com.zxs.springbootmybatisflex.uitl;

import com.zxs.springbootmybatisflex.exception.code.BaseResponseCode;
import com.zxs.springbootmybatisflex.exception.code.ResponseCodeInterface;
import lombok.Data;


@Data
public class DataResult<T>{

    /**
     * 请求响应code,0为成功 其他为失败
     */
    private int code;

    /**
     * 响应异常码详细信息
     */
    private String msg;

    /**
     * 响应内容 , code 0 时为 返回 数据
     */
    private T data;

    public DataResult(int code, T data) {
        this.code = code;
        this.data = data;
        this.msg=null;
    }

    public DataResult(int code, String msg, T data) {
        this.code = code;
        this.msg = msg;
        this.data = data;
    }

    public DataResult(int code, String msg) {
        this.code = code;
        this.msg = msg;
        this.data=null;
    }


    public DataResult() {
        this.code= BaseResponseCode.SUCCESS.getCode();
        this.msg=BaseResponseCode.SUCCESS.getMsg();
        this.data=null;
    }

    public DataResult(T data) {
        this.data = data;
        this.code=BaseResponseCode.SUCCESS.getCode();
        this.msg=BaseResponseCode.SUCCESS.getMsg();
    }

    public DataResult(ResponseCodeInterface responseCodeInterface) {
        this.data = null;
        this.code = responseCodeInterface.getCode();
        this.msg = responseCodeInterface.getMsg();
    }

    public DataResult(ResponseCodeInterface responseCodeInterface, T data) {
        this.data = data;
        this.code = responseCodeInterface.getCode();
        this.msg = responseCodeInterface.getMsg();
    }

    public static <T>DataResult success(){
        return new <T>DataResult();
    }

    public static <T>DataResult success(T data){
        return new <T>DataResult(data);
    }

    public static <T>DataResult getResult(int code,String msg,T data){
        return new <T>DataResult(code,msg,data);
    }

    public static <T>DataResult getResult(int code,String msg){
        return new <T>DataResult(code,msg);
    }

    public static <T>DataResult getResult(BaseResponseCode responseCode){
        return new <T>DataResult(responseCode);
    }

    public static <T>DataResult getResult(BaseResponseCode responseCode,T data){

        return new <T>DataResult(responseCode,data);
    }
}
package com.zxs.springbootmybatisflex.uitl;

import org.apache.commons.lang3.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

@Component
public class SpringContextHolder implements ApplicationContextAware, DisposableBean {

	private static ApplicationContext applicationContext = null;

	private static Logger logger = LoggerFactory.getLogger(SpringContextHolder.class);

	/**
	 * 取得存储在静态变量中的ApplicationContext.
	 */
	public static ApplicationContext getApplicationContext() {
		assertContextInjected();
		return applicationContext;
	}

	/**
	 * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
	 */
	@SuppressWarnings("unchecked")
	public static <T> T getBean(String name) {
		assertContextInjected();
		return (T) applicationContext.getBean(name);
	}

	/**
	 * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
	 */
	public static <T> T getBean(Class<T> requiredType) {
		assertContextInjected();
		return applicationContext.getBean(requiredType);
	}

	/**
	 * 清除SpringContextHolder中的ApplicationContext为Null.
	 */
	public static void clearHolder() {
		logger.debug("清除SpringContextHolder中的ApplicationContext:"
				+ applicationContext);
		applicationContext = null;
	}

	/**
	 * 实现ApplicationContextAware接口, 注入Context到静态变量中.
	 */
	@Override
	public void setApplicationContext(ApplicationContext applicationContext)throws BeansException {
//		logger.debug("注入ApplicationContext到SpringContextHolder:{}", applicationContext);

		if (SpringContextHolder.applicationContext != null) {
			logger.warn("SpringContextHolder中的ApplicationContext被覆盖, 原有ApplicationContext为:" + SpringContextHolder.applicationContext);
		}

		SpringContextHolder.applicationContext = applicationContext; // NOSONAR
	}

	/**
	 * 实现DisposableBean接口, 在Context关闭时清理静态变量.
	 */
	@Override
	public void destroy() throws Exception {
		SpringContextHolder.clearHolder();
	}

	/**
	 * 检查ApplicationContext不为空.
	 */
	private static void assertContextInjected() {
		Validate.validState(applicationContext != null, "applicaitonContext属性未注入, 请在applicationContext.xml中定义SpringContextHolder.");
	}
}
package com.zxs.springbootmybatisflex.zenum;


public interface ActionEnum {
    String pause="pause";
    String resume="resume";
    String check="check";
    String delete="delete";
    String start="start";
}
package com.zxs.springbootmybatisflex.zenum;


public enum JobEnum implements JobInterface {

    Key("jobkey"),
    MISFIRE_DEFAULT("0"),
    MISFIRE_IGNORE("1"),
    MISFIRE_FIRE_AND_PROCEED("2"),
    MISFIRE_DO_NOTHING("3"),
    PAUSE("1"),
    ;


    private final String code;


    JobEnum(String code) {
        this.code = code;
    }

    @Override
    public String getCode() {
        return code;
    }

}
package com.zxs.springbootmybatisflex.zenum;


public interface JobInterface {
    String getCode();
}
package com.zxs.springbootmybatisflex;

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

@SpringBootApplication
@MapperScan("com.zxs.springbootmybatisflex.dao")
public class SpringbootMybatisFlexApplication {

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


}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zxs.springbootmybatisflex.dao.SysJobDao">

    <resultMap type="com.zxs.springbootmybatisflex.entity.SysJob" id="SysJobMap">
        <result property="jobId" column="job_id" jdbcType="INTEGER"/>
        <result property="jobName" column="job_name" jdbcType="VARCHAR"/>
        <result property="jobGroup" column="job_group" jdbcType="VARCHAR"/>
        <result property="invokeTarget" column="invoke_target" jdbcType="VARCHAR"/>
        <result property="cronExpression" column="cron_expression" jdbcType="VARCHAR"/>
        <result property="misfirePolicy" column="misfire_policy" jdbcType="VARCHAR"/>
        <result property="concurrent" column="concurrent" jdbcType="VARCHAR"/>
        <result property="status" column="status" jdbcType="VARCHAR"/>
        <result property="createBy" column="create_by" jdbcType="VARCHAR"/>
        <result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
        <result property="updateBy" column="update_by" jdbcType="VARCHAR"/>
        <result property="updateTime" column="update_time" jdbcType="TIMESTAMP"/>
        <result property="remark" column="remark" jdbcType="VARCHAR"/>
        <result property="deleteStatus" column="delete_status" jdbcType="INTEGER"/>
    </resultMap>


</mapper>


DROP TABLE IF EXISTS `sys_job`;
CREATE TABLE `sys_job`  (
  `job_id` int(6) NOT NULL AUTO_INCREMENT COMMENT '任务ID',
  `job_name` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '任务名称',
  `job_group` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'DEFAULT' COMMENT '任务组名',
  `invoke_target` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '调用目标字符串',
  `cron_expression` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '' COMMENT 'cron执行表达式',
  `misfire_policy` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '3' COMMENT '计划执行错误策略(1立即执行 2执行一次 3放弃执行)',
  `concurrent` char(1) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '1' COMMENT '是否并发执行(0允许 1禁止)',
  `status` char(1) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '0' COMMENT '状态(0正常 1暂停)',
  `create_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '' COMMENT '创建者',
  `create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
  `update_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '' COMMENT '更新者',
  `update_time` datetime(0) NULL DEFAULT CURRENT_TIMESTAMP(0) ON UPDATE CURRENT_TIMESTAMP(0) COMMENT '更新时间',
  `remark` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '' COMMENT '备注信息',
  `delete_status` int(1) NOT NULL DEFAULT 0 COMMENT '0-正常,1-删除',
  PRIMARY KEY (`job_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 10 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '定时任务调度表' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of sys_job
-- ----------------------------
INSERT INTO `sys_job` VALUES (1, '输出任务', 'DEFAULT', 'task.sout', '0/3 * * * * ?', '3', '0', '0', 'admin', '2023-06-09 11:27:28', 'zxs', '2023-10-19 10:51:17', '', 0);
INSERT INTO `sys_job` VALUES (2, '测试任务', 'DEFAULT', 'task.ceshi', '0/4 * * * * ?', '3', '0', '1', '', NULL, 'zxs', '2023-10-18 16:28:02', '', 0);
INSERT INTO `sys_job` VALUES (3, '我不知道干啥的', 'DEFAULT', 'task.buzhidao', '0/5 * * * * ?', '3', '0', '1', 'zxs', '2023-10-18 14:48:40', '', '2023-10-18 16:28:04', '', 0);
INSERT INTO `sys_job` VALUES (4, '打酱油', 'DEFAULT', 'task.dajiangyou', '0/6 * * * * ?', '3', '0', '1', 'zxs', '2023-10-18 14:49:20', 'zxs', '2023-10-18 16:28:06', '', 0);
INSERT INTO `sys_job` VALUES (5, '吹牛', 'DEFAULT', 'task.chuiniu', '0/7 * * * * ?', '3', '0', '1', 'zxs', '2023-10-18 14:50:53', '', '2023-10-18 16:28:07', '', 0);
INSERT INTO `sys_job` VALUES (6, '骂人', 'DEFAULT', 'task.maren', '0/8 * * * * ?', '3', '0', '1', 'zxs', '2023-10-18 14:51:28', '', '2023-10-18 16:28:09', '', 0);
INSERT INTO `sys_job` VALUES (7, '怼人的', 'DEFAULT', 'task.duiren', '0/4 * * * * ?', '3', '0', '1', 'zxs', '2023-10-18 15:22:34', '', '2023-10-18 16:28:10', '', 0);
INSERT INTO `sys_job` VALUES (8, '要饭的', 'DEFAULT', 'task.yaofan', '0/5 * * * * ?', '3', '0', '1', 'zxs', '2023-10-18 15:23:38', '', '2023-10-18 16:28:12', '', 0);
INSERT INTO `sys_job` VALUES (9, '吃饭的', 'DEFAULT', 'task.chifan', '0/6 * * * * ?', '3', '0', '1', 'zxs', '2023-10-18 15:24:08', 'zxs', '2023-10-18 16:28:15', '哈哈哈哈0', 0);

SET FOREIGN_KEY_CHECKS = 1;

以上就是一个完整的demo了

运行之后访问http://127.0.0.1:8080/doc.html

 可以看到我们开放的api

测试

 执行以上的sql,有可以看到我内置的一些拿来测试的任务

 然后我随便找一个任务开启

 当任务状态为0的时候,该任务就是正常的运行中的状态

 当然你也可以控制一下,当服务关闭时,去把所有为0状态重置为1,把任务关闭掉,或者当服务重启时,去初始化把状态为0的任务加入执行器中,不然每次重启,任务都不在执行器中与数据库的状态不一致

例如:

@PreDestroy
	public void stop() {
		stopTask();
	}
	public void stopTask() {
		List<SysJob> jobs = sysJobService.list().stream()
				.map(s -> {
					s.setStatus("1");
					return s;
				})
				.collect(Collectors.toList());
		sysJobService.saveOrUpdateBatch(jobs);
	}

 https://download.csdn.net/download/qq_14926283/88445520

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1109834.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2023年中国GPS导航设备产量、销量及市场规模分析[图]

GPS导航设备行业是指生产和销售用于导航、定位和监控目的的GPS设备的行业&#xff0c;可以用于汽车、船只、飞机、人员和其他物体的定位和导航&#xff0c;以及用于地理信息系统&#xff08;GIS&#xff09;、测绘、海洋抢险、森林监测、地质勘探、气象预报、交通管理、物流跟踪…

中国地级市-环境规制18个相关指标数据(2002-2021年)

本次数据为地级市-环境规制相关指标&#xff0c;主要用于计算城市的环境规制、污染排放水平。指标数目总计18个&#xff0c;数据来源为《中国城市统计年鉴》&#xff0c;存在一定缺失。数据包含原始、线性插值、回归填补3个版本&#xff0c;希望对大家有用 一、数据介绍 数据…

uni-app:js二维数组与对象数组之间的转换

一、二维数组整理成对象数组 效果 [ ["前绿箭","DI10","RO1"], ["前红叉","DI2","RO2"], ["后绿箭","DI12","RO3"], ["后红叉","DI4","RO6"] ] …

2023国考证件照要求什么底色?证件照换背景底色的方法

2023年国家公务员考试报名已经开始了&#xff0c;我们在考试平台提交报名信息的时候&#xff0c;有一项就是需要上传证件照片&#xff0c;对于证件照片也会有具体的要求&#xff0c;比如背景底色、尺寸大小、dpi和kb大小。今天就为大家详细介绍一下关于国考证件照背景色的内容&…

数据结构与算法课后题-第六章(图的存储及基本操作)

文章目录 1、选择题12、选择题23、选择题34、选择题45、选择题56、选择题67、选择题78、选择题89、选择题910、选择题1011、选择题1112、选择题1214、选择题1415、选择题1516、选择题16 1、选择题1 2、选择题2 3、选择题3 4、选择题4 5、选择题5 6、选择题6 7、选择题7 8、选择…

zzy-project-cli,提供多个框架的脚手架

npm地址 install npm install zzy-project-cli -g做什么&#xff1f; 将多个可选的框架提供给使用者选择&#xff0c;选中后自动下载对应模板&#xff0c;快捷使用。 使用 step1 zzy-cli create [项目名称]step2 获取模板之后选取任一进行下载 下载完成之后即可使用 模…

藏在超级应用背后的逻辑和哲学

众所周知&#xff0c;Elon Musk 想将 Twitter 重新设计定位成一款“超级应用 - X”的野心已经不再是秘密。伴随着应用商店中 Twitter 标志性的蓝鸟 Logo 被 X 取代后&#xff0c;赛博世界充满了对这件事情各种角度的探讨与分析。 Musk 曾经无数次通过微信这一样本来推广他的“超…

龙芯3A3000源码编译安装deepin-ide

安装环境 系统为统领专业版1050 CPU为龙芯3A3000 安装步骤 1.安装所有依赖库 sudo apt-get install git debhelper cmake qt5-qmake qtbase5-dev qttools5-dev qttools5-dev-tools lxqt-build-tools libssl-dev llvm llvm-dev libclang-dev libutf8proc-dev libmicrohttpd-d…

利用MixProxy自动录制生成Pytest案例:轻松实现测试脚本编写!

前言 进行接口自动化时&#xff0c;有时候往往没有接口文档&#xff0c;或者文档更新并不及时&#xff0c;此时&#xff0c;想要获取相关接口&#xff0c;通过抓包是一种快速便捷的手段。抓包获取到接口后&#xff0c;开始写接口用例&#xff0c;此时需要复制请求url、请求参数…

人工智能驱动的个性化学习:技术如何彻底改变教育

随着计算机辅助教学的出现&#xff0c;人工智能在教育领域的发展始于20世纪50年代。然而&#xff0c;在20世纪90年代&#xff0c;由于机器学习和数据处理的进步&#xff0c;该领域开始出现大幅增长。人工智能在教育领域的早期应用之一是智能辅导系统&#xff08;ITS&#xff09…

最新最全网络安全专业毕业设计选题精华汇总-持续更新中

文章目录 0 前言1 网络安全(信息安全)毕设选题推荐2 开题指导3 最后 0 前言 Hi&#xff0c;大家好&#xff0c;随着毕业季的临近&#xff0c;许多同学开始向学长咨询关于选题和开题的问题。在这里&#xff0c;学长分享一些关于网络安全(信息安全)毕业设计选题的内容。 以下为…

2023人工智能全景报告《State of AI Report》出炉!AI未来一年的10大预测:GPT-4仍是全球最强,GenAI 大爆发,...

文章目录 2023 人工智能全景报告《State of AI Report》出炉&#xff01;给出AI未来一年的10大预测&#xff0c;GPT-4仍是全球最强&#xff0c;GenAI 大爆发&#xff0c;...1. 研究进展1.1 GPT-4仍是全球最强1.2 闭源模型趋于技术封闭&#xff0c;开源模型紧追不舍1.3 小模型的…

日常中msvcr120.dll丢失怎么解决?有效的5个解决方法分享

在我日常的计算机维护和故障排除中&#xff0c;我经常会遇到一些常见的问题&#xff0c;其中之一就是“msvcr120.dll丢失”。这是一个非常常见的错误&#xff0c;通常出现在运行某些程序或游戏时。这个问题可能会对用户的电脑操作造成不便&#xff0c;甚至导致一些重要的应用程…

风力发电场安科瑞集中监控系统解决方案

安科瑞 崔丽洁 风力发电场集中监控系统解决方案 作为清洁能源之一&#xff0c;风力发电场近几年装机容量快速增长。8月17日&#xff0c;国家能源局发布1-7月份全国电力工业统计数据。截至7月底&#xff0c;全国累计发电装机容量约27.4亿千瓦&#xff0c;同比增长11.5%。其中&am…

1024程序员节特辑 | ELK+ 用户画像构建个性化推荐引擎,智能实现“千人千面”

专栏集锦&#xff0c;赶紧收藏以备不时之需 Spring Cloud实战专栏&#xff1a;https://blog.csdn.net/superdangbo/category_9270827.html Python 实战专栏&#xff1a;https://blog.csdn.net/superdangbo/category_9271194.html Logback 详解专栏&#xff1a;https://blog.…

Mac苹果电脑开不了机怎么办,该怎么修复

台式机Mac或MacBook无法打开&#xff0c;或者可能无法通过Apple图标启动&#xff1f;不用担心&#xff0c;虽然会让人烦躁不安&#xff0c;但通常是可以修复的。 以下就是重新启动Mac所需的所有步骤。只需按顺序进行操作即可&#xff0c;除非操作系统更新失败后Mac无法启动。在…

[1024]程序员节 一晃6年过去了

加入开发者大军&#xff0c;一晃已是6年有余&#xff0c;从最初的Andoird开发如火如荼&#xff0c;到现在的秋风萧瑟&#xff0c;宛如被秋风吹得只剩躯干的树木&#xff0c;等待来年的焕发新芽。 我本不是一个科班出身的开发者&#xff0c;但是为了生活&#xff0c;说白了为了钱…

macos平台好用的FTP客户端 Transmit 5最新中文

Transmit 5是一款Mac平台上的FTP(文件传输协议)客户端软件&#xff0c;它可以让用户方便地在不同的计算机之间传输文件&#xff0c;例如从本地计算机上传或下载文件到远程服务器。以下是Transmit 5的一些主要功能和特点&#xff1a; 用户友好的界面&#xff1a;Transmit 5具有…

英语——分享篇——每日200词——1401-1600

1401——willing——[wɪlɪŋ]——adj.乐意的&#xff0c;自愿的——willing——will意志(熟词)ing鹰(谐音)——这只意志力强的鹰乐意帮助别人——Im perfectly willing to discuss the problem.——我十分乐意讨论这个问题。 1402——otherwise——[ʌəwaɪz]——conj.否则&…

KT6368A的封装怎么画 原理图怎么画 资料怎么看 怎么下载呢

一、简介 KT6368A的封装怎么画 原理图怎么画 资料怎么看 怎么下载呢 也有好几个客户问我们这样的问题&#xff0c;实在是太难了 但是没办法&#xff0c;客户就是上帝&#xff0c;也只能选择认真的回答&#xff0c; 这里对这类型的问题进行统一精细化回复&#xff0c;高手请…