• springboot之quartz动态可控定时任务


    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

    1. <properties>
    2. <java.version>17java.version>
    3. <mybatis-flex.version>1.7.2mybatis-flex.version>
    4. <fastjson.version>1.2.47fastjson.version>
    5. properties>
    6. <dependencyManagement>
    7. <dependencies>
    8. <dependency>
    9. <groupId>com.github.xiaoymingroupId>
    10. <artifactId>knife4j-dependenciesartifactId>
    11. <version>4.1.0version>
    12. <type>pomtype>
    13. <scope>importscope>
    14. dependency>
    15. dependencies>
    16. dependencyManagement>
    17. <dependencies>
    18. <dependency>
    19. <groupId>org.springframework.bootgroupId>
    20. <artifactId>spring-boot-starter-webartifactId>
    21. dependency>
    22. <dependency>
    23. <groupId>com.github.xiaoymingroupId>
    24. <artifactId>knife4j-openapi3-jakarta-spring-boot-starterartifactId>
    25. dependency>
    26. <dependency>
    27. <groupId>com.mysqlgroupId>
    28. <artifactId>mysql-connector-jartifactId>
    29. <scope>runtimescope>
    30. dependency>
    31. <dependency>
    32. <groupId>org.projectlombokgroupId>
    33. <artifactId>lombokartifactId>
    34. <optional>trueoptional>
    35. dependency>
    36. <dependency>
    37. <groupId>org.springframework.bootgroupId>
    38. <artifactId>spring-boot-starter-jdbcartifactId>
    39. dependency>
    40. <dependency>
    41. <groupId>org.springframework.bootgroupId>
    42. <artifactId>spring-boot-starter-quartzartifactId>
    43. dependency>
    44. <dependency>
    45. <groupId>com.mybatis-flexgroupId>
    46. <artifactId>mybatis-flex-spring-boot-starterartifactId>
    47. <version>${mybatis-flex.version}version>
    48. dependency>
    49. <dependency>
    50. <groupId>com.alibabagroupId>
    51. <artifactId>fastjsonartifactId>
    52. <version>${fastjson.version}version>
    53. dependency>
    54. dependencies>
    1. mybatis-flex:
    2. datasource:
    3. ds1:
    4. url: jdbc:mysql://127.0.0.1:3306/test
    5. username: root
    6. password: zkb.com
    7. springdoc:
    8. swagger-ui:
    9. path: /swagger-ui.html
    10. tags-sorter: alpha
    11. api-docs:
    12. path: /v3/api-docs
    13. group-configs:
    14. - group: '接口'
    15. paths-to-match: '/**'
    16. packages-to-scan: com.zxs.springbootmybatisflex.controller.client
    17. default-flat-param-object: true
    1. package com.zxs.springbootmybatisflex.quartz;
    2. import com.zxs.springbootmybatisflex.entity.SysJob;
    3. import com.zxs.springbootmybatisflex.zenum.JobEnum;
    4. import org.quartz.Job;
    5. import org.quartz.JobExecutionContext;
    6. public class QuartzJob implements Job {
    7. @Override
    8. public void execute(JobExecutionContext context) {
    9. SysJob sysJob = (SysJob) context.getJobDetail().getJobDataMap().get(JobEnum.Key.getCode());
    10. QuartzUtil.runJob(sysJob.getInvokeTarget());
    11. }
    12. }
    1. package com.zxs.springbootmybatisflex.quartz;
    2. import com.zxs.springbootmybatisflex.entity.JobVo;
    3. import com.zxs.springbootmybatisflex.entity.SysJob;
    4. import com.zxs.springbootmybatisflex.uitl.SpringContextHolder;
    5. import com.zxs.springbootmybatisflex.zenum.ActionEnum;
    6. import com.zxs.springbootmybatisflex.zenum.JobEnum;
    7. import lombok.SneakyThrows;
    8. import org.quartz.*;
    9. import org.springframework.util.ObjectUtils;
    10. import java.lang.reflect.Method;
    11. import java.util.Date;
    12. public class QuartzUtil {
    13. @SneakyThrows
    14. public static void startJob(SysJob sysJob) {
    15. // 获取调度器 Scheduler
    16. Scheduler scheduler = SchedulerStatic.getScheduler();
    17. Integer jobId = sysJob.getJobId();
    18. String jobGroup = sysJob.getJobGroup();
    19. // 构造一个job
    20. JobKey jobKey = JobKey.jobKey(jobId.toString(), jobGroup);
    21. JobDetail jobDetail = JobBuilder
    22. .newJob(QuartzJob.class)
    23. .withIdentity(jobKey)
    24. .build();
    25. // 构造cron调度器
    26. CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(sysJob.getCronExpression());
    27. getMisfirePolicy(sysJob, cronScheduleBuilder);
    28. // 构造触发器 trigger
    29. TriggerKey triggerKey = TriggerKey.triggerKey(jobId.toString(), jobGroup);
    30. CronTrigger trigger = TriggerBuilder
    31. .newTrigger()
    32. .withIdentity(triggerKey)
    33. .withSchedule(cronScheduleBuilder)
    34. .build();
    35. // 放入job信息,为后续执行改任务的具体方法做铺垫(需要反射调用), 在execute中获取并应用
    36. jobDetail.getJobDataMap().put(JobEnum.Key.getCode(), sysJob);
    37. // 判断该任务是否存在,修改任务,先删除然后添加
    38. if (scheduler.checkExists(jobKey)) {
    39. // 防止创建时存在数据问题 先移除,然后在执行创建操作
    40. scheduler.deleteJob(jobKey);
    41. }
    42. // 判断任务是否过期
    43. CronExpression cron = new CronExpression(sysJob.getCronExpression());
    44. Date nextValidTimeAfter = cron.getNextValidTimeAfter(new Date(System.currentTimeMillis()));
    45. if (!ObjectUtils.isEmpty(nextValidTimeAfter)) {
    46. // 执行调度任务
    47. scheduler.scheduleJob(jobDetail, trigger);
    48. }
    49. }
    50. @SneakyThrows
    51. public static void doJob(JobVo sysJob,String action) {
    52. // 获取调度器 Scheduler
    53. Scheduler scheduler = SchedulerStatic.getScheduler();
    54. // 构造一个job
    55. JobKey jobKey = JobKey.jobKey(sysJob.getJobId().toString(), sysJob.getJobGroup());
    56. // 判断该任务是否存在,修改任务,先删除然后添加
    57. if (!scheduler.checkExists(jobKey)) {
    58. return;
    59. }
    60. switch (action){
    61. case ActionEnum.check: //
    62. System.out.println(true);
    63. break;
    64. case ActionEnum.pause: //暂停
    65. scheduler.pauseJob(jobKey);
    66. break;
    67. case ActionEnum.resume: //继续
    68. scheduler.resumeJob(jobKey);
    69. break;
    70. case ActionEnum.delete: //删除
    71. scheduler.deleteJob(jobKey);
    72. break;
    73. default:
    74. scheduler.checkExists(jobKey);
    75. break;
    76. }
    77. }
    78. private static void getMisfirePolicy(SysJob sysJob, CronScheduleBuilder cronScheduleBuilder) {
    79. String s= sysJob.getMisfirePolicy();
    80. if(s.equals(JobEnum.MISFIRE_DEFAULT.getCode())){
    81. }else if(s.equals(JobEnum.MISFIRE_IGNORE.getCode())){
    82. cronScheduleBuilder.withMisfireHandlingInstructionIgnoreMisfires();
    83. } else if(s.equals(JobEnum.MISFIRE_FIRE_AND_PROCEED.getCode())){
    84. cronScheduleBuilder.withMisfireHandlingInstructionFireAndProceed();
    85. } else if(s.equals(JobEnum.MISFIRE_DO_NOTHING.getCode())){
    86. cronScheduleBuilder.withMisfireHandlingInstructionDoNothing();
    87. }else{
    88. throw new RuntimeException("The task misfire policy '" + sysJob.getMisfirePolicy() + "' cannot be used in cron schedule tasks");
    89. }
    90. }
    91. public static void invokeMethod(Object bean, String methodName) throws Exception {
    92. Method method = bean.getClass().getMethod(methodName);
    93. method.invoke(bean);
    94. }
    95. public static void runJob(String invokeTarget){
    96. String[] parts = invokeTarget.split("\\.");
    97. String beanName = parts[0];
    98. String methodName = parts[1];
    99. Object bean = SpringContextHolder.getBean(beanName);
    100. try {
    101. QuartzUtil.invokeMethod(bean, methodName);
    102. } catch (Exception e) {
    103. throw new RuntimeException("Error running job", e);
    104. }
    105. }
    106. }
    1. package com.zxs.springbootmybatisflex.quartz;
    2. import org.quartz.Scheduler;
    3. import org.springframework.beans.factory.annotation.Autowired;
    4. import org.springframework.stereotype.Component;
    5. @Component
    6. public class SchedulerStatic {
    7. private static Scheduler scheduler;
    8. @Autowired
    9. public SchedulerStatic(Scheduler scheduler) {
    10. SchedulerStatic.scheduler = scheduler;
    11. }
    12. public static Scheduler getScheduler() {
    13. return scheduler;
    14. }
    15. }
    1. package com.zxs.springbootmybatisflex.config;
    2. import org.springframework.cache.annotation.EnableCaching;
    3. import org.springframework.context.annotation.Configuration;
    4. @EnableCaching
    5. @Configuration
    6. public class CacheConfig {
    7. }
    1. package com.zxs.springbootmybatisflex.config;
    2. import com.mybatisflex.core.mybatis.FlexConfiguration;
    3. import com.mybatisflex.spring.boot.ConfigurationCustomizer;
    4. import org.apache.ibatis.logging.stdout.StdOutImpl;
    5. import org.springframework.context.annotation.Configuration;
    6. @Configuration
    7. public class MyConfigurationCustomizer implements ConfigurationCustomizer {
    8. @Override
    9. public void customize(FlexConfiguration configuration) {
    10. configuration.setLogImpl(StdOutImpl.class);
    11. }
    12. }
    1. package com.zxs.springbootmybatisflex.config;
    2. import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
    3. import io.swagger.v3.oas.models.OpenAPI;
    4. import io.swagger.v3.oas.models.info.Info;
    5. import io.swagger.v3.oas.models.info.License;
    6. import org.springframework.context.annotation.Bean;
    7. import org.springframework.context.annotation.Configuration;
    8. @Configuration
    9. @EnableKnife4j
    10. public class SwaggerConfig {
    11. // 设置 openapi 基础参数
    12. @Bean
    13. public OpenAPI customOpenAPI() {
    14. return new OpenAPI()
    15. .info(new Info()
    16. .title("zxs API 管理")
    17. .version("1.0")
    18. .description("探索mybatis-flex与quartz demo")
    19. .license(new License().name("Apache 2.0")));
    20. }
    21. }
    1. package com.zxs.springbootmybatisflex.controller.client;
    2. import com.zxs.springbootmybatisflex.entity.JobVo;
    3. import com.zxs.springbootmybatisflex.entity.SysJob;
    4. import com.zxs.springbootmybatisflex.quartz.QuartzUtil;
    5. import com.zxs.springbootmybatisflex.service.SysJobService;
    6. import com.zxs.springbootmybatisflex.uitl.DataResult;
    7. import com.zxs.springbootmybatisflex.zenum.ActionEnum;
    8. import io.swagger.v3.oas.annotations.Operation;
    9. import io.swagger.v3.oas.annotations.tags.Tag;
    10. import jakarta.annotation.Resource;
    11. import org.springframework.util.ObjectUtils;
    12. import org.springframework.web.bind.annotation.*;
    13. import java.util.List;
    14. /**
    15. * 定时任务调度表(SysJob)表控制层
    16. *
    17. * @author makejava
    18. * @since 2023-10-19 09:17:58
    19. */
    20. @RestController
    21. @Tag(name = "任务中心")
    22. @RequestMapping("sysJob")
    23. public class SysJobController {
    24. /**
    25. * 服务对象
    26. */
    27. @Resource
    28. private SysJobService sysJobService;
    29. @Operation(summary = "获取任务列表", description = "获取任务列表")
    30. @PostMapping("/getJobList")
    31. public DataResult<SysJob>> getJobList() {
    32. DataResult<SysJob>> result = new DataResult<>();
    33. List<SysJob> list =sysJobService.list();
    34. result.setData(list);
    35. return result;
    36. }
    37. @Operation(summary = "根据主键获取任务信息")
    38. @PostMapping("/getJobById/{id}")
    39. public DataResult<SysJob> getJobById(@PathVariable(value = "id") Integer id) {
    40. DataResult<SysJob> result = new DataResult<>();
    41. result.setData(sysJobService.getById(id));
    42. return result;
    43. }
    44. @PostMapping("/saveJob")
    45. @Operation(summary="新增/修改任务",description="新增/修改任务,并加入的调度器中执行")
    46. public DataResult<Boolean> saveJob(SysJob sysJob) {
    47. DataResult<Boolean> result = new DataResult<>();
    48. result.setData(sysJobService.saveOrUpdate(sysJob));
    49. QuartzUtil.startJob(sysJob);
    50. return result;
    51. }
    52. @PostMapping("/deleteJob/{id}")
    53. @Operation(summary="删除任务",description="删除任务,并删除调度器中执行的该任务")
    54. public DataResult<Boolean> deleteJob(@PathVariable(value = "id") Integer id) {
    55. DataResult<Boolean> result = new DataResult<>();
    56. SysJob sysJob = sysJobService.getById(id);
    57. boolean data = this.sysJobService.removeById(id);
    58. result.setData(data);
    59. if(!ObjectUtils.isEmpty(sysJob)) {
    60. JobVo jobVo = new JobVo();
    61. jobVo.setJobId(sysJob.getJobId());
    62. jobVo.setJobGroup(sysJob.getJobGroup());
    63. QuartzUtil.doJob(jobVo, ActionEnum.delete);
    64. }
    65. return result;
    66. }
    67. @GetMapping("/getJobAndJoin/{id}")
    68. @Operation(summary="开启/关闭任务",description="获取一个任务,修改任务,并加入/丢出的调度器中执行")
    69. public DataResult<SysJob> getJobAndJoin(@PathVariable(value = "id") Integer id) {
    70. DataResult<SysJob> result = new DataResult<>();
    71. SysJob one = sysJobService.getById(id);
    72. if("0".equals(one.getStatus())){
    73. one.setStatus("1");
    74. }else{
    75. one.setStatus("0");
    76. }
    77. sysJobService.updateById(one);
    78. QuartzUtil.startJob(one);
    79. result.setData(one);
    80. return result;
    81. }
    82. @PostMapping("/runJob")
    83. @Operation(summary="手动执行任务",description="手动执行任务")
    84. public DataResult<Boolean> runJob(String invokeTarget) {
    85. DataResult<Boolean> result = new DataResult<>();
    86. QuartzUtil.runJob(invokeTarget);
    87. return result;
    88. }
    89. @PostMapping("/checkJob")
    90. @Operation(summary="检查任务",description="检测任务")
    91. public DataResult<Boolean> checkJob(JobVo jobVo) {
    92. DataResult<Boolean> result = new DataResult<>();
    93. QuartzUtil.doJob(jobVo,ActionEnum.check);
    94. return result;
    95. }
    96. @PostMapping("/resumeJob")
    97. @Operation(summary="恢复任务",description="恢复任务")
    98. public DataResult<Boolean> resumeJob(JobVo sysJob) {
    99. DataResult<Boolean> result = new DataResult<>();
    100. QuartzUtil.doJob(sysJob,ActionEnum.resume);
    101. return result;
    102. }
    103. @PostMapping("/pauseJob")
    104. @Operation(summary="暂停任务",description="暂停任务")
    105. public DataResult<Boolean> pauseJob(JobVo sysJob) {
    106. DataResult<Boolean> result = new DataResult<>();
    107. QuartzUtil.doJob(sysJob,ActionEnum.pause);
    108. return result;
    109. }
    110. }
    1. package com.zxs.springbootmybatisflex.dao;
    2. import com.mybatisflex.core.BaseMapper;
    3. import com.zxs.springbootmybatisflex.entity.SysJob;
    4. import org.apache.ibatis.annotations.Mapper;
    5. /**
    6. * 定时任务调度表(SysJob)表数据库访问层
    7. *
    8. * @author makejava
    9. * @since 2023-10-19 09:17:58
    10. */
    11. @Mapper
    12. public interface SysJobDao extends BaseMapper<SysJob> {
    13. }
    1. package com.zxs.springbootmybatisflex.entity;
    2. import lombok.Data;
    3. import java.io.Serializable;
    4. @Data
    5. public class JobVo implements Serializable {
    6. private Integer jobId;
    7. private String jobGroup;
    8. }
    1. package com.zxs.springbootmybatisflex.entity;
    2. import com.fasterxml.jackson.annotation.JsonFormat;
    3. import com.mybatisflex.annotation.Id;
    4. import com.mybatisflex.annotation.KeyType;
    5. import com.mybatisflex.annotation.Table;
    6. import com.mybatisflex.core.activerecord.Model;
    7. import io.swagger.v3.oas.annotations.media.Schema;
    8. import lombok.Data;
    9. import java.util.Date;
    10. /**
    11. * 定时任务调度表(SysJob)表实体类
    12. *
    13. * @author makejava
    14. * @since 2023-10-19 09:17:59
    15. */
    16. @Data
    17. @Schema(description="定时任务调度表")
    18. @Table("sys_job")
    19. public class SysJob extends Model<SysJob> {
    20. @Schema(description="任务ID")
    21. @Id(keyType = KeyType.Auto)
    22. private Integer jobId;
    23. @Schema(description="任务名称")
    24. private String jobName;
    25. @Schema(description="任务组名")
    26. private String jobGroup;
    27. @Schema(description="调用目标字符串")
    28. private String invokeTarget;
    29. @Schema(description="cron执行表达式")
    30. private String cronExpression;
    31. @Schema(description="计划执行错误策略(1立即执行 2执行一次 3放弃执行)")
    32. private String misfirePolicy;
    33. @Schema(description="是否并发执行(0允许 1禁止)")
    34. private String concurrent;
    35. @Schema(description="状态(0正常 1暂停)")
    36. private String status;
    37. @Schema(description="创建者")
    38. private String createBy;
    39. @Schema(description="创建时间")
    40. @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    41. private Date createTime;
    42. @Schema(description="更新者")
    43. private String updateBy;
    44. @Schema(description="更新时间")
    45. @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    46. private Date updateTime;
    47. @Schema(description="备注信息")
    48. private String remark;
    49. @Schema(description="0-正常,1-删除")
    50. private Integer deleteStatus;
    51. }
    1. package com.zxs.springbootmybatisflex.exception.code;
    2. public enum BaseResponseCode implements ResponseCodeInterface {
    3. /**
    4. * 这个要和前段约定好
    5. * 引导用户去登录界面的
    6. * code=401001 引导用户重新登录
    7. * code=401002 token 过期刷新token
    8. * code=401008 无权限访问
    9. */
    10. SUCCESS(200,"操作成功"),
    11. SYSTEM_BUSY(500001, "系统繁忙,请稍候再试"),
    12. OPERATION_ERRO(500002,"操作失败"),
    13. METHODARGUMENTNOTVALIDEXCEPTION(500003, "方法参数校验异常"),
    14. ;
    15. /**
    16. * 错误码
    17. */
    18. private final int code;
    19. /**
    20. * 错误消息
    21. */
    22. private final String msg;
    23. BaseResponseCode(int code, String msg) {
    24. this.code = code;
    25. this.msg = msg;
    26. }
    27. @Override
    28. public int getCode() {
    29. return code;
    30. }
    31. @Override
    32. public String getMsg() {
    33. return msg;
    34. }
    35. }
    1. package com.zxs.springbootmybatisflex.exception.code;
    2. public interface ResponseCodeInterface {
    3. int getCode();
    4. String getMsg();
    5. }
    1. package com.zxs.springbootmybatisflex.exception.handler;
    2. import com.zxs.springbootmybatisflex.exception.BusinessException;
    3. import com.zxs.springbootmybatisflex.exception.code.BaseResponseCode;
    4. import com.zxs.springbootmybatisflex.uitl.DataResult;
    5. import lombok.extern.slf4j.Slf4j;
    6. import org.springframework.validation.ObjectError;
    7. import org.springframework.web.bind.MethodArgumentNotValidException;
    8. import org.springframework.web.bind.annotation.ExceptionHandler;
    9. import org.springframework.web.bind.annotation.RestControllerAdvice;
    10. import java.util.List;
    11. @RestControllerAdvice
    12. @Slf4j
    13. public class RestExceptionHandler {
    14. @ExceptionHandler(Exception.class)
    15. public <T> DataResult<T> handleException(Exception e){
    16. log.error("Exception,exception:{}", e);
    17. return DataResult.getResult(BaseResponseCode.SYSTEM_BUSY);
    18. }
    19. @ExceptionHandler(value = BusinessException.class)
    20. <T> DataResult<T> businessExceptionHandler(BusinessException e) {
    21. log.error("BusinessException,exception:{}", e);
    22. return new DataResult<>(e.getMessageCode(),e.getDetailMessage());
    23. }
    24. @ExceptionHandler(MethodArgumentNotValidException.class)
    25. <T> DataResult<T> methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e) {
    26. log.error("methodArgumentNotValidExceptionHandler bindingResult.allErrors():{},exception:{}", e.getBindingResult().getAllErrors(), e);
    27. List<ObjectError> errors = e.getBindingResult().getAllErrors();
    28. return createValidExceptionResp(errors);
    29. }
    30. private <T> DataResult<T> createValidExceptionResp(List<ObjectError> errors) {
    31. String[] msgs = new String[errors.size()];
    32. int i = 0;
    33. for (ObjectError error : errors) {
    34. msgs[i] = error.getDefaultMessage();
    35. log.info("msg={}",msgs[i]);
    36. i++;
    37. }
    38. return DataResult.getResult(BaseResponseCode.METHODARGUMENTNOTVALIDEXCEPTION.getCode(), msgs[0]);
    39. }
    40. }
    1. package com.zxs.springbootmybatisflex.exception;
    2. import com.zxs.springbootmybatisflex.exception.code.ResponseCodeInterface;
    3. public class BusinessException extends RuntimeException{
    4. /**
    5. * 异常编号
    6. */
    7. private final int messageCode;
    8. /**
    9. * 对messageCode 异常信息进行补充说明
    10. */
    11. private final String detailMessage;
    12. public BusinessException(int messageCode,String message) {
    13. super(message);
    14. this.messageCode = messageCode;
    15. this.detailMessage = message;
    16. }
    17. /**
    18. * 构造函数
    19. * @param code 异常码
    20. */
    21. public BusinessException(ResponseCodeInterface code) {
    22. this(code.getCode(), code.getMsg());
    23. }
    24. public int getMessageCode() {
    25. return messageCode;
    26. }
    27. public String getDetailMessage() {
    28. return detailMessage;
    29. }
    30. }
    1. package com.zxs.springbootmybatisflex.exception;
    2. import com.zxs.springbootmybatisflex.exception.code.ResponseCodeInterface;
    3. public class RoleSaveException extends RuntimeException{
    4. /**
    5. * 异常编号
    6. */
    7. private final int messageCode;
    8. /**
    9. * 对messageCode 异常信息进行补充说明
    10. */
    11. private final String detailMessage;
    12. public RoleSaveException(int messageCode, String message) {
    13. super(message);
    14. this.messageCode = messageCode;
    15. this.detailMessage = message;
    16. }
    17. /**
    18. * 构造函数
    19. * @param code 异常码
    20. */
    21. public RoleSaveException(ResponseCodeInterface code) {
    22. this(code.getCode(), code.getMsg());
    23. }
    24. public int getMessageCode() {
    25. return messageCode;
    26. }
    27. public String getDetailMessage() {
    28. return detailMessage;
    29. }
    30. }
    1. package com.zxs.springbootmybatisflex.quartz.task;
    2. import org.springframework.stereotype.Service;
    3. @Service("task")
    4. public class DoTask {
    5. public void sout() {
    6. System.out.println("我是干输出的");
    7. }
    8. public void ceshi() {
    9. System.out.println("我是干测试的");
    10. }
    11. public void buzhidao() {
    12. System.out.println("我不知道干啥的");
    13. }
    14. public void dajiangyou() {
    15. System.out.println("我是打酱油的");
    16. }
    17. public void chuiniu() {
    18. System.out.println("我是吹牛的");
    19. }
    20. public void maren() {
    21. System.out.println("我是骂人的");
    22. }
    23. public void duiren() {
    24. System.out.println("我是怼人的");
    25. }
    26. public void yaofan() {
    27. System.out.println("我是要饭的");
    28. }
    29. public void chifan() {
    30. System.out.println("我是吃饭的");
    31. }
    32. }
    1. package com.zxs.springbootmybatisflex.service.impl;
    2. import com.mybatisflex.spring.service.impl.ServiceImpl;
    3. import com.zxs.springbootmybatisflex.dao.SysJobDao;
    4. import com.zxs.springbootmybatisflex.entity.SysJob;
    5. import com.zxs.springbootmybatisflex.service.SysJobService;
    6. import org.springframework.stereotype.Service;
    7. /**
    8. * 定时任务调度表(SysJob)表服务实现类
    9. *
    10. * @author makejava
    11. * @since 2023-10-19 09:17:59
    12. */
    13. @Service
    14. public class SysJobServiceImpl extends ServiceImpl implements SysJobService {
    15. }
    1. package com.zxs.springbootmybatisflex.service;
    2. import com.mybatisflex.core.service.IService;
    3. import com.zxs.springbootmybatisflex.entity.SysJob;
    4. /**
    5. * 定时任务调度表(SysJob)表服务接口
    6. *
    7. * @author makejava
    8. * @since 2023-10-19 09:17:59
    9. */
    10. public interface SysJobService extends IService<SysJob> {
    11. }
    1. package com.zxs.springbootmybatisflex.uitl;
    2. import com.zxs.springbootmybatisflex.exception.code.BaseResponseCode;
    3. import com.zxs.springbootmybatisflex.exception.code.ResponseCodeInterface;
    4. import lombok.Data;
    5. @Data
    6. public class DataResult<T>{
    7. /**
    8. * 请求响应code,0为成功 其他为失败
    9. */
    10. private int code;
    11. /**
    12. * 响应异常码详细信息
    13. */
    14. private String msg;
    15. /**
    16. * 响应内容 , code 0 时为 返回 数据
    17. */
    18. private T data;
    19. public DataResult(int code, T data) {
    20. this.code = code;
    21. this.data = data;
    22. this.msg=null;
    23. }
    24. public DataResult(int code, String msg, T data) {
    25. this.code = code;
    26. this.msg = msg;
    27. this.data = data;
    28. }
    29. public DataResult(int code, String msg) {
    30. this.code = code;
    31. this.msg = msg;
    32. this.data=null;
    33. }
    34. public DataResult() {
    35. this.code= BaseResponseCode.SUCCESS.getCode();
    36. this.msg=BaseResponseCode.SUCCESS.getMsg();
    37. this.data=null;
    38. }
    39. public DataResult(T data) {
    40. this.data = data;
    41. this.code=BaseResponseCode.SUCCESS.getCode();
    42. this.msg=BaseResponseCode.SUCCESS.getMsg();
    43. }
    44. public DataResult(ResponseCodeInterface responseCodeInterface) {
    45. this.data = null;
    46. this.code = responseCodeInterface.getCode();
    47. this.msg = responseCodeInterface.getMsg();
    48. }
    49. public DataResult(ResponseCodeInterface responseCodeInterface, T data) {
    50. this.data = data;
    51. this.code = responseCodeInterface.getCode();
    52. this.msg = responseCodeInterface.getMsg();
    53. }
    54. public static <T>DataResult success(){
    55. return new <T>DataResult();
    56. }
    57. public static <T>DataResult success(T data){
    58. return new <T>DataResult(data);
    59. }
    60. public static <T>DataResult getResult(int code,String msg,T data){
    61. return new <T>DataResult(code,msg,data);
    62. }
    63. public static <T>DataResult getResult(int code,String msg){
    64. return new <T>DataResult(code,msg);
    65. }
    66. public static <T>DataResult getResult(BaseResponseCode responseCode){
    67. return new <T>DataResult(responseCode);
    68. }
    69. public static <T>DataResult getResult(BaseResponseCode responseCode,T data){
    70. return new <T>DataResult(responseCode,data);
    71. }
    72. }
    1. package com.zxs.springbootmybatisflex.uitl;
    2. import org.apache.commons.lang3.Validate;
    3. import org.slf4j.Logger;
    4. import org.slf4j.LoggerFactory;
    5. import org.springframework.beans.BeansException;
    6. import org.springframework.beans.factory.DisposableBean;
    7. import org.springframework.context.ApplicationContext;
    8. import org.springframework.context.ApplicationContextAware;
    9. import org.springframework.stereotype.Component;
    10. @Component
    11. public class SpringContextHolder implements ApplicationContextAware, DisposableBean {
    12. private static ApplicationContext applicationContext = null;
    13. private static Logger logger = LoggerFactory.getLogger(SpringContextHolder.class);
    14. /**
    15. * 取得存储在静态变量中的ApplicationContext.
    16. */
    17. public static ApplicationContext getApplicationContext() {
    18. assertContextInjected();
    19. return applicationContext;
    20. }
    21. /**
    22. * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
    23. */
    24. @SuppressWarnings("unchecked")
    25. public static <T> T getBean(String name) {
    26. assertContextInjected();
    27. return (T) applicationContext.getBean(name);
    28. }
    29. /**
    30. * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
    31. */
    32. public static <T> T getBean(Class<T> requiredType) {
    33. assertContextInjected();
    34. return applicationContext.getBean(requiredType);
    35. }
    36. /**
    37. * 清除SpringContextHolder中的ApplicationContext为Null.
    38. */
    39. public static void clearHolder() {
    40. logger.debug("清除SpringContextHolder中的ApplicationContext:"
    41. + applicationContext);
    42. applicationContext = null;
    43. }
    44. /**
    45. * 实现ApplicationContextAware接口, 注入Context到静态变量中.
    46. */
    47. @Override
    48. public void setApplicationContext(ApplicationContext applicationContext)throws BeansException {
    49. // logger.debug("注入ApplicationContext到SpringContextHolder:{}", applicationContext);
    50. if (SpringContextHolder.applicationContext != null) {
    51. logger.warn("SpringContextHolder中的ApplicationContext被覆盖, 原有ApplicationContext为:" + SpringContextHolder.applicationContext);
    52. }
    53. SpringContextHolder.applicationContext = applicationContext; // NOSONAR
    54. }
    55. /**
    56. * 实现DisposableBean接口, 在Context关闭时清理静态变量.
    57. */
    58. @Override
    59. public void destroy() throws Exception {
    60. SpringContextHolder.clearHolder();
    61. }
    62. /**
    63. * 检查ApplicationContext不为空.
    64. */
    65. private static void assertContextInjected() {
    66. Validate.validState(applicationContext != null, "applicaitonContext属性未注入, 请在applicationContext.xml中定义SpringContextHolder.");
    67. }
    68. }
    1. package com.zxs.springbootmybatisflex.zenum;
    2. public interface ActionEnum {
    3. String pause="pause";
    4. String resume="resume";
    5. String check="check";
    6. String delete="delete";
    7. String start="start";
    8. }
    1. package com.zxs.springbootmybatisflex.zenum;
    2. public enum JobEnum implements JobInterface {
    3. Key("jobkey"),
    4. MISFIRE_DEFAULT("0"),
    5. MISFIRE_IGNORE("1"),
    6. MISFIRE_FIRE_AND_PROCEED("2"),
    7. MISFIRE_DO_NOTHING("3"),
    8. PAUSE("1"),
    9. ;
    10. private final String code;
    11. JobEnum(String code) {
    12. this.code = code;
    13. }
    14. @Override
    15. public String getCode() {
    16. return code;
    17. }
    18. }
    1. package com.zxs.springbootmybatisflex.zenum;
    2. public interface JobInterface {
    3. String getCode();
    4. }
    1. package com.zxs.springbootmybatisflex;
    2. import org.mybatis.spring.annotation.MapperScan;
    3. import org.springframework.boot.SpringApplication;
    4. import org.springframework.boot.autoconfigure.SpringBootApplication;
    5. @SpringBootApplication
    6. @MapperScan("com.zxs.springbootmybatisflex.dao")
    7. public class SpringbootMybatisFlexApplication {
    8. public static void main(String[] args) {
    9. SpringApplication.run(SpringbootMybatisFlexApplication.class, args);
    10. }
    11. }
    1. "1.0" encoding="UTF-8"?>
    2. mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    3. <mapper namespace="com.zxs.springbootmybatisflex.dao.SysJobDao">
    4. <resultMap type="com.zxs.springbootmybatisflex.entity.SysJob" id="SysJobMap">
    5. <result property="jobId" column="job_id" jdbcType="INTEGER"/>
    6. <result property="jobName" column="job_name" jdbcType="VARCHAR"/>
    7. <result property="jobGroup" column="job_group" jdbcType="VARCHAR"/>
    8. <result property="invokeTarget" column="invoke_target" jdbcType="VARCHAR"/>
    9. <result property="cronExpression" column="cron_expression" jdbcType="VARCHAR"/>
    10. <result property="misfirePolicy" column="misfire_policy" jdbcType="VARCHAR"/>
    11. <result property="concurrent" column="concurrent" jdbcType="VARCHAR"/>
    12. <result property="status" column="status" jdbcType="VARCHAR"/>
    13. <result property="createBy" column="create_by" jdbcType="VARCHAR"/>
    14. <result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
    15. <result property="updateBy" column="update_by" jdbcType="VARCHAR"/>
    16. <result property="updateTime" column="update_time" jdbcType="TIMESTAMP"/>
    17. <result property="remark" column="remark" jdbcType="VARCHAR"/>
    18. <result property="deleteStatus" column="delete_status" jdbcType="INTEGER"/>
    19. resultMap>
    20. mapper>

    1. DROP TABLE IF EXISTS `sys_job`;
    2. CREATE TABLE `sys_job` (
    3. `job_id` int(6) NOT NULL AUTO_INCREMENT COMMENT '任务ID',
    4. `job_name` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '任务名称',
    5. `job_group` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'DEFAULT' COMMENT '任务组名',
    6. `invoke_target` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '调用目标字符串',
    7. `cron_expression` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '' COMMENT 'cron执行表达式',
    8. `misfire_policy` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '3' COMMENT '计划执行错误策略(1立即执行 2执行一次 3放弃执行)',
    9. `concurrent` char(1) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '1' COMMENT '是否并发执行(0允许 1禁止)',
    10. `status` char(1) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '0' COMMENT '状态(0正常 1暂停)',
    11. `create_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '' COMMENT '创建者',
    12. `create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
    13. `update_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '' COMMENT '更新者',
    14. `update_time` datetime(0) NULL DEFAULT CURRENT_TIMESTAMP(0) ON UPDATE CURRENT_TIMESTAMP(0) COMMENT '更新时间',
    15. `remark` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '' COMMENT '备注信息',
    16. `delete_status` int(1) NOT NULL DEFAULT 0 COMMENT '0-正常,1-删除',
    17. PRIMARY KEY (`job_id`) USING BTREE
    18. ) ENGINE = InnoDB AUTO_INCREMENT = 10 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '定时任务调度表' ROW_FORMAT = Dynamic;
    19. -- ----------------------------
    20. -- Records of sys_job
    21. -- ----------------------------
    22. 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);
    23. INSERT INTO `sys_job` VALUES (2, '测试任务', 'DEFAULT', 'task.ceshi', '0/4 * * * * ?', '3', '0', '1', '', NULL, 'zxs', '2023-10-18 16:28:02', '', 0);
    24. 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);
    25. 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);
    26. 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);
    27. 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);
    28. 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);
    29. 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);
    30. 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);
    31. SET FOREIGN_KEY_CHECKS = 1;

    以上就是一个完整的demo了

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

     可以看到我们开放的api

    测试

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

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

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

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

    例如:

    1. @PreDestroy
    2. public void stop() {
    3. stopTask();
    4. }
    5. public void stopTask() {
    6. List<SysJob> jobs = sysJobService.list().stream()
    7. .map(s -> {
    8. s.setStatus("1");
    9. return s;
    10. })
    11. .collect(Collectors.toList());
    12. sysJobService.saveOrUpdateBatch(jobs);
    13. }

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

  • 相关阅读:
    Vue学习之--------深入理解Vuex、原理详解、实战应用(2022/9/1)
    金融业信贷风控算法10-神经网络模型
    行走的数字
    【STL】vector常见用法及模拟实现(附源码)
    Databend 开源周报第112期
    Verilog HDL复习总结
    JUC并发编程第七篇,volatile凭什么可以保证可见性和有序性?我们该如何正确使用它?
    1740. 找到⼆叉树中的距离
    【算法中的Java】— 判断语句
    陈志泊主编《数据库原理及应用教程第4版微课版》的实验题目参考答案实验4
  • 原文地址:https://blog.csdn.net/qq_14926283/article/details/133918620