当前位置: 首页 > news >正文

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

<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/testusername: rootpassword: zkb.com
springdoc:swagger-ui:path: /swagger-ui.htmltags-sorter: alphaapi-docs:path: /v3/api-docsgroup-configs:- group: '接口'paths-to-match: '/**'packages-to-scan: com.zxs.springbootmybatisflex.controller.clientdefault-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 {@Overridepublic 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 {@SneakyThrowspublic static void startJob(SysJob sysJob) {// 获取调度器 SchedulerScheduler scheduler = SchedulerStatic.getScheduler();Integer jobId = sysJob.getJobId();String jobGroup = sysJob.getJobGroup();// 构造一个jobJobKey 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);// 构造触发器 triggerTriggerKey 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);}}@SneakyThrowspublic static void doJob(JobVo sysJob,String action) {// 获取调度器 SchedulerScheduler scheduler = SchedulerStatic.getScheduler();// 构造一个jobJobKey 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;@Autowiredpublic 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 {@Overridepublic 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 基础参数@Beanpublic 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 {/*** 服务对象*/@Resourceprivate 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;}@Overridepublic int getCode() {return code;}@Overridepublic 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到静态变量中.*/@Overridepublic 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关闭时清理静态变量.*/@Overridepublic 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;}@Overridepublic 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的任务加入执行器中,不然每次重启,任务都不在执行器中与数据库的状态不一致

例如:

@PreDestroypublic 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

相关文章:

springboot之quartz动态可控定时任务

Quartz Quartz是一个开源的任务调度框架&#xff0c;可以用来实现定时任务的调度&#xff0c;如定时发送邮件、定时备份数据等。Quartz具有很高的可靠性和灵活性&#xff0c;支持集群部署和分布式调度&#xff0c;并且提供了丰富的API和插件&#xff0c;可以轻松实现复杂的调度…...

什么是CSS的外边距重叠?

区块的上下外边距有时会合并&#xff08;折叠&#xff09;为单个边距&#xff0c;其大小为两个边距中的最大值&#xff08;或如果它们相等&#xff0c;则仅为其中一个&#xff09;&#xff0c;这种行为称为外边距折叠。注意&#xff1a;有设定浮动和绝对定位的元素不会发生外边…...

设计模式之抽象工厂模式

前言 工厂模式一般指的是简单工厂模式、工厂方法模式、抽象工厂模式&#xff0c;这是三种工厂模式的最后一篇&#xff0c;其他两种的文章链接如下&#xff1a; 设计模式之简单工厂模式-CSDN博客 设计模式之工厂方法模式-CSDN博客 建议三种模式放在一起对比学习&#xff0c;…...

Compose预处理组件大比拼:性能、应用场景和可视化对比总结

在机器学习的世界里,预处理组件就像是厨师的烹饪工具。选择合适的工具不仅可以让整个烹饪过程更加顺畅,还能确保最终的菜肴更加美味。 本文将深入探讨四种“烹饪工具”:TransformedTargetRegressor、make_column_transformer、make_column_selector和ColumnTransformer。通…...

【小米】Linux 实习生

下午不准备去图书馆自习来着&#xff0c;中午就狠狠地多睡了一个小时&#xff0c;三点起床靠在椅子上剥柚子&#xff0c;太爽了&#xff0c;这秋天的下午。“邮件&#xff1a;小米公司邀请你预约面试时间”.......... 我擦&#xff0c;投了一个月了&#xff0c;认真准备的时候…...

python一点通:coroutine (协程)是什么和重要知识点?

协程已经成为Python用于编写并发和异步代码的重要工具之一。在这篇博客文章中&#xff0c;我们将深入探讨协程是什么&#xff0c;它们的优点&#xff0c;以及它们与传统的线程和进程有何不同。 什么是协程&#xff1f; 协程是用于合作式多任务处理的子程序&#xff08;或函数…...

QCC51XX-QCC30XX系列开发教程(实战篇) 之 12.1-空间音频相关模块的概述

查看全部教程开发请点击:全网最全-QCC51xx-QCC30xx(TWS)系列从入门到精通开发教程汇总(持续更新中) ==================================================================== 版权归作者所有,未经允许,请勿转载。 ==========================================...

Servlet的生命周期

2023.10.18 WEB容器创建的Servlet对象&#xff0c;这些Servlet对象都会被放到一个集合当中&#xff08;HashMap&#xff09;&#xff0c;这个集合当中存储了Servlet对象和请求路径之间的关系 。只有放到这个HashMap集合中的Servlet才能够被WEB容器管理&#xff0c;自己new的Ser…...

2.4 如何在FlinkSQL使用DataGen(数据生成器)

1、DataGen SQL 连接器 FLinkSQL中可以使用内置的DataGen SQL 连接器来生成测试数据 官网链接&#xff1a;DataGen SQL 连接器 2、随机数数据生成器 随机数数据生成器支持随机生成 char、varchar、binary、varbinary、string 类型的数据 它是一个无界流的数据生成器 -- TO…...

Gin + Ant Design Pro JWT认证

文章目录 一&#xff1a;介绍二&#xff1a;Gin JWT 后台1. Claims 定义2. 创建和解析Token3. Gin中间件编写4. 辅助函数 三&#xff1a;Ant Design Pro JWT认证四&#xff1a;Gin中间件和使用示范 一&#xff1a;介绍 JWT现在比较流行的认证方式&#xff0c;微服务中使用特别…...

canvas实现图片标注,绘制区域

使用canvas绘制通过多边形标注区域 AI视频项目中需要分析图片&#xff0c;需要前台绘制区域&#xff0c;后端获取坐标然后识别图像&#xff0c;通过canvas 获取点然后连线绘图 HEML代码段 <div class"areaDrawing"><img src"/assets/images/snapPhotos…...

SELECT COUNT(*) 会造成全表扫描吗?

前言 SELECT COUNT(*)会不会导致全表扫描引起慢查询呢&#xff1f; SELECT COUNT(*) FROM SomeTable 网上有一种说法&#xff0c;针对无 where_clause 的 COUNT(*)&#xff0c;MySQL 是有优化的&#xff0c;优化器会选择成本最小的辅助索引查询计数&#xff0c;其实反而性能…...

python考前复习(90题)

文章目录 1.Python特性的是( )。 A. 面向对象 B. 高可移植性 C. 开源、免费 2.临时改变Python语言安装源应当使用的选项是 –index-url 3.Python脚本文件的扩展名为( ) .py 4.安装Python语言的软件包使用的命令是&#xff08; &#xff09; pip install 5 . (单选题)以下哪项是…...

根据SpringBoot Guides完成进行示例学习(详细步骤)

目录 1.打开Spring | Guides官网&#xff0c;或者直接搜索springboot都可 2.选择要学习的内容 3.根据提示的网址&#xff0c;Git到本地 4.将文件用IDEA打开&#xff0c;根据教程完成示例&#xff0c;这里不做细致讲解 5.运行项目 6.在终端查看运行结果 以Scheduling Task…...

waf、yakit和ssh免密登录

WAF安全狗 脏数据适用于所有漏洞绕过waf&#xff0c;但是前提条件垃圾信息必须放在危险信息前&#xff0c;是不能打断原有数据包的结构&#xff0c;不能影响后端对数据包的解析。 以DVWA靶场文件上传为例 新建php文件 上传文件被安全狗拦截 使用bp抓包查看 在数据包Content-…...

【AIGC核心技术剖析】大型语言和视觉助手——LLaVA(论文+源码)

🔥 [新!LLaVA-1.5 在 11 个基准测试上实现了 SoTA,只需对原始 LLaVA 进行简单的修改,利用所有公共数据,在单个 1-A8 节点上在 ~100 天内完成训练,并超越使用数十亿级数据的方法。 LLaVA代表了一种新颖的端到端训练大型多模态模型,结合了视觉编码器和骆马 对于通用的视…...

IBM的WAS简介与基本使用手册

IBM的WAS简介与基本使用手册 1. 基本介绍 WebSphereApplication Server(简称WAS)是IBM的应用服务器 基本结构:单元(cell) ——> 多个节点(node) ——> 多个服务(server) ——> 多个应用(app) 单元是整个分布式网络中一个或多个节点的逻辑分组单元是一个配置概念, 是…...

Deno 快速入门

目录 1、简介 2、安装Deno MacOS下安装 Windows下安装 Linux 下安装 3、创建并运行TypeScript程序 4、内置Web API和Deno命名空间 5、运行时安全 6、导入JavaScript模块 7、远程模块和Deno标准库 8、使用deno.json配置您的项目 9、Node.js API和npm包 10、配置IDE…...

【计算机网络笔记】OSI参考模型基本概念

系列文章目录 什么是计算机网络&#xff1f; 什么是网络协议&#xff1f; 计算机网络的结构 数据交换之电路交换 数据交换之报文交换和分组交换 分组交换 vs 电路交换 计算机网络性能&#xff08;1&#xff09;——速率、带宽、延迟 计算机网络性能&#xff08;2&#xff09;…...

ConnectTimeout和ReadTimeout所代表的意义

ConnectTimeout和ReadTimeout所代表的意义 ConnectTimeout 指的是建立连接所用的时间&#xff0c;适用于网络状况正常的情况下&#xff0c;两端连接所用的时间。在java中&#xff0c;网络状况正常的情况下&#xff0c;例如使用HttpClient或者HttpURLConnetion连接时设置参数c…...

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周&#xff0c;有很多同学在写期末Java web作业时&#xff0c;运行tomcat出现乱码问题&#xff0c;经过多次解决与研究&#xff0c;我做了如下整理&#xff1a; 原因&#xff1a; IDEA本身编码与tomcat的编码与Windows编码不同导致&#xff0c;Windows 系统控制台…...

高等数学(下)题型笔记(八)空间解析几何与向量代数

目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…...

数据链路层的主要功能是什么

数据链路层&#xff08;OSI模型第2层&#xff09;的核心功能是在相邻网络节点&#xff08;如交换机、主机&#xff09;间提供可靠的数据帧传输服务&#xff0c;主要职责包括&#xff1a; &#x1f511; 核心功能详解&#xff1a; 帧封装与解封装 封装&#xff1a; 将网络层下发…...

使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台

🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...

Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信

文章目录 Linux C语言网络编程详细入门教程&#xff1a;如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket&#xff08;服务端和客户端都要&#xff09;2. 绑定本地地址和端口&#x…...

【生成模型】视频生成论文调研

工作清单 上游应用方向&#xff1a;控制、速度、时长、高动态、多主体驱动 类型工作基础模型WAN / WAN-VACE / HunyuanVideo控制条件轨迹控制ATI~镜头控制ReCamMaster~多主体驱动Phantom~音频驱动Let Them Talk: Audio-Driven Multi-Person Conversational Video Generation速…...

SiFli 52把Imagie图片,Font字体资源放在指定位置,编译成指定img.bin和font.bin的问题

分区配置 (ptab.json) img 属性介绍&#xff1a; img 属性指定分区存放的 image 名称&#xff0c;指定的 image 名称必须是当前工程生成的 binary 。 如果 binary 有多个文件&#xff0c;则以 proj_name:binary_name 格式指定文件名&#xff0c; proj_name 为工程 名&…...

人机融合智能 | “人智交互”跨学科新领域

本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...

华为OD机考-机房布局

import java.util.*;public class DemoTest5 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseSystem.out.println(solve(in.nextLine()));}}priv…...

深度剖析 DeepSeek 开源模型部署与应用:策略、权衡与未来走向

在人工智能技术呈指数级发展的当下&#xff0c;大模型已然成为推动各行业变革的核心驱动力。DeepSeek 开源模型以其卓越的性能和灵活的开源特性&#xff0c;吸引了众多企业与开发者的目光。如何高效且合理地部署与运用 DeepSeek 模型&#xff0c;成为释放其巨大潜力的关键所在&…...