(一)解决的问题: core表示式无法灵活修改,定时任务无法用数据库控制开关等问题,定时任务执行数据库相关操作需要额外写代码。
(二)原理: ThreadPoolTaskScheduler 线程池可以管理spring的定时任务
(三)思路:
1 一个定时任务管理实体类,主要字段有core表达式,bean名称,方法名,状态,定时任务名称等
2 使用反射根据bean名称和方法名找到定时任务需要执行的方法
3 使用ThreadPoolTaskScheduler提供的方法进行定时任务添加/关闭/执行一次等
4 如何实现动态开关:将数据存在数据库中,配置类初始化方法使用**@PostConstruct**初始化需要执行的定时任务列表,添加到ThreadPoolTaskScheduler 线程池中执行
(四)代码

ScheduleConfigDO 实体类,mapper,service等不贴出了
public class ScheduleConfigDO {/*** 自增主键*/private Integer id;/*** 定时任务名称*/private String jobName;/*** 类名称*/private String className;/*** 方法*/private String method;/*** cron 表达式*/private String cron;/*** 状态:1正常,0停用*/private Integer enabled;/*** 创建时间*/private Date createTime;/*** 创建人*/private String createBy;/*** 更新时间*/private Date updateTime;/*** 更新人*/private String updateBy;
}
AsyncAndScheduleTaskExecutePool 线程池配置类
@Slf4j
@EnableAsync
@Configuration
@EnableScheduling
public class AsyncAndScheduleTaskExecutePool implements SchedulingConfigurer,AsyncConfigurer {@Overridepublic void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {ThreadPoolTaskScheduler taskScheduler = taskScheduler();scheduledTaskRegistrar.setTaskScheduler(taskScheduler);}/*** 定时任务使用的线程池* @return*/@Bean(destroyMethod = "shutdown", name = "taskScheduler")public ThreadPoolTaskScheduler  taskScheduler(){ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();scheduler.setPoolSize(50);scheduler.setThreadNamePrefix("taskScheduler-");scheduler.setAwaitTerminationSeconds(600);scheduler.setErrorHandler(throwable -> log.error("定时调度任务发生异常{}", throwable.toString()));scheduler.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());scheduler.setWaitForTasksToCompleteOnShutdown(true);scheduler.initialize();return scheduler;}@Overridepublic Executor getAsyncExecutor() {return asyncExecutor();}/*** 异步任务执行线程池* @return*/@Bean(name = "asyncExecutor")public ThreadPoolTaskExecutor asyncExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();//核心线程池大小executor.setCorePoolSize(50);//最大线程数executor.setMaxPoolSize(100);//队列容量executor.setQueueCapacity(100);//活跃时间executor.setKeepAliveSeconds(60);//设置线程池关闭的时候等待所有任务都完成再继续销毁其他的Beanexecutor.setWaitForTasksToCompleteOnShutdown(true);//线程名字前缀executor.setThreadNamePrefix("asyncExecutor-");// setRejectedExecutionHandler:当pool已经达到max size的时候,如何处理新任务// CallerRunsPolicy:不在新线程中执行任务,而是由调用者所在的线程来执行executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());executor.initialize();return executor;}/*** 异步任务异常处理* @return*/@Overridepublic AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {return (throwable, method, objects) -> {log.error("异步任务执行出现异常{}", throwable.toString());log.error("exception method:{}", method.getName());log.error("exception objects:{}", objects.toString());};}}
ScheduleTaskComponent 定时任务管理类
@Component
public class ScheduleTaskComponent {// 保存任务private Map<String, ScheduledFuture<?>> futuresMap = new ConcurrentHashMap<String, ScheduledFuture<?>>();@Autowiredprivate ScheduleConfigService scheduleConfigService;// 创建ThreadPoolTaskScheduler线程池@Autowiredprivate ThreadPoolTaskScheduler threadPoolTaskScheduler;// 初始化任务@PostConstructpublic void initSchedule(){List<ScheduleConfigDTO> list = scheduleConfigService.getList(null);for (ScheduleConfigDTO config : list){ScheduledFuture<?> future = threadPoolTaskScheduler.schedule(getRunnable(config), getTrigger(config));futuresMap.put(config.getJobName(), future);}}/*** 暂停任务* @param key* @return*/public boolean pauseeTask(String key) {ScheduledFuture toBeRemovedFuture = futuresMap.remove(key);if (toBeRemovedFuture != null) {toBeRemovedFuture.cancel(true);return true;} else {return false;}}/*** 添加任务* @param config*/public void addTask(ScheduleConfigDTO config){ScheduledFuture<?> future = threadPoolTaskScheduler.schedule(getRunnable(config), getTrigger(config));futuresMap.put(config.getJobName(), future);}/*** 更新任务* @param config*/public void updateTask(ScheduleConfigDTO config) {ScheduledFuture toBeRemovedFuture = futuresMap.remove(config.getJobName());if (toBeRemovedFuture != null) {toBeRemovedFuture.cancel(true);}addTask(config);}/*** 转换首字母小写** @param str* @return*/public static String lowerFirstCapse(String str) {char[] chars = str.toCharArray();chars[0] += 32;return String.valueOf(chars);}/***  //反射使用newInstance静态方法来实例化对象 获取不了spring中注入的对象* runnable* @param scheduleConfig* @return*/private Runnable getRunnable(ScheduleConfigDTO scheduleConfig) {return new Runnable() {@Overridepublic void run() {Class<?> clazz;try {//注意这里如果@Service注解没指定名字,生成的默认首字母小写Object o = ApplicationContextUtil.getBean(scheduleConfig.getClassName());clazz = o.getClass();Method method = clazz.getMethod(scheduleConfig.getMethod());//method.invoke(clazz.newInstance());method.invoke(o);} catch (Exception e) {log.error(e.getMessage());}}};}/*** Trigger* @param scheduleConfig* @return*/private Trigger getTrigger(ScheduleConfigDTO scheduleConfig) {return new Trigger() {@Overridepublic Date nextExecutionTime(TriggerContext triggerContext) {CronTrigger trigger = new CronTrigger(scheduleConfig.getCron());Date nextExec = trigger.nextExecutionTime(triggerContext);return nextExec;}};}
}
控制器controller类
@RestController
@Api(tags = "定时任务配置")
@RequestMapping("/scheduleConfig")
public class ScheduleConfigController {private static final Logger LOGGER = LoggerFactory.getLogger(ScheduleConfigController.class);@Autowiredprivate ScheduleConfigService scheduleConfigService;@Autowiredprivate ScheduleTaskComponent scheduleTaskComponent;/*** 分页查询* @param scheduleConfigQueryVO 分页查询参数* @return 分页参数*/@ApiOperation("分页查询")@PostMapping("getPageList")public ScheduleConfigPageInfoVO selectPageList(@RequestBody @Valid ScheduleConfigQueryVO scheduleConfigQueryVO) {String info = String.format("The method name[selectPageList] params:%s", scheduleConfigQueryVO.toString());LOGGER.info(info);ScheduleConfigDTO scheduleConfig = BeanUtil.convert(scheduleConfigQueryVO, ScheduleConfigDTO.class);Page<ScheduleConfigResponseVO> page = PageHelper.startPage(scheduleConfigQueryVO.getPageNum(), scheduleConfigQueryVO.getPageSize());List<ScheduleConfigDTO> scheduleConfigList = scheduleConfigService.getList(scheduleConfig);ScheduleConfigPageInfoVO scheduleConfigPageInfo = new ScheduleConfigPageInfoVO();BeanUtils.copyProperties(page.toPageInfo(), scheduleConfigPageInfo);List<ScheduleConfigResponseVO> voList = BeanUtil.convert(scheduleConfigList, ScheduleConfigResponseVO.class);scheduleConfigPageInfo.setList(voList);return scheduleConfigPageInfo;}/*** 添加任务* @return*/@Log(type = Log.OperationType.ADD,button = "添加任务",menu = "定时任务管理")@ApiOperation("添加任务")@PostMapping("/insert")public void insertTask(@RequestBody @Valid ScheduleConfigUpdateVO vo) throws BusinessException {ScheduleConfigDTO dto = BeanUtil.convert(vo, ScheduleConfigDTO.class);if(StringUtils.isBlank(dto.getClassName())||StringUtils.isBlank(dto.getMethod())||StringUtils.isBlank(dto.getCron())){throw new BusinessException("类名,方法名,core表达式不能为空");}if(!CronExpression.isValidExpression(dto.getCron())){throw new BusinessException("core表达式格式错误");}dto.setCreateBy(SecurityUtils.getUsername());dto.setUpdateBy(SecurityUtils.getUsername());dto.setUpdateTime(DateUtils.getNowDate());dto.setCreateTime(DateUtils.getNowDate());dto.setEnabled(1);scheduleConfigService.insert(dto);//新增任务taskscheduleTaskComponent.addTask(dto);}/*** 修改任务* @return*/@Log(type = Log.OperationType.UPDATE,button = "修改任务",menu = "定时任务管理")@ApiOperation("修改任务")@PostMapping("/update")public void updateTask(@RequestBody @Valid ScheduleConfigUpdateVO vo){if(StringUtils.isBlank(vo.getCron())){vo.setCron(" 0 0/1 * * * ?");}if(vo.getId() == null||StringUtils.isBlank(vo.getClassName())||StringUtils.isBlank(vo.getMethod())){throw new BusinessException("ID,类名,方法名,core表达式不能为空");}if(!CronExpression.isValidExpression(vo.getCron())){throw new BusinessException("core表达式格式错误");}ScheduleConfigDTO dto = BeanUtil.convert(vo, ScheduleConfigDTO.class);dto.setUpdateBy(SecurityUtils.getUsername());dto.setUpdateTime(new Date());scheduleConfigService.update(dto);//更新任务taskscheduleTaskComponent.updateTask(dto);}/*** 暂停任务*/@Log(type = Log.OperationType.UPDATE,button = "暂停任务",menu = "定时任务管理")@ApiOperation("暂停任务")@GetMapping("/pause")public void pauseTask(@RequestParam Integer id){ScheduleConfigDTO dto = new ScheduleConfigDTO();dto.setId(id);//1正常,0停用dto.setEnabled(0);dto.setUpdateBy(SecurityUtils.getUsername());dto.setUpdateTime(new Date());scheduleConfigService.update(dto);//暂停任务taskscheduleTaskComponent.pauseeTask(scheduleConfigService.getById(id).getJobName());}/*** 开启任务*/@Log(type = Log.OperationType.UPDATE,button = "开启任务",menu = "定时任务管理")@ApiOperation("开启任务")@GetMapping("/start")public void startTask(@RequestParam Integer id){ScheduleConfigDTO dto = new ScheduleConfigDTO();dto.setId(id);dto.setEnabled(1);dto.setUpdateBy(SecurityUtils.getUsername());dto.setUpdateTime(new Date());scheduleConfigService.update(dto);//开启任务taskscheduleTaskComponent.addTask(scheduleConfigService.getById(id));}/*** 执行一次*/@Log(type = Log.OperationType.UPDATE,button = "执行一次",menu = "定时任务管理")@ApiOperation("执行一次")@GetMapping("/executeOnce")public void executeOnce(@RequestParam Integer id) throws  Exception{ScheduleConfigDTO dto = scheduleConfigService.getById(id);Class<?> clazz;//注意这里如果@Service注解没指定名字,生成的默认首字母小写Object o = ApplicationContextUtil.getBean(dto.getClassName());clazz = o.getClass();Method method = clazz.getMethod(dto.getMethod());//method.invoke(clazz.newInstance());method.invoke(o);}/*** 删除任务*/@Log(type = Log.OperationType.DELETE,button = "删除任务",menu = "定时任务管理")@ApiOperation("删除任务")@DeleteMapping("/delete")public void delTask(@RequestParam Integer id){ScheduleConfigDTO dto = scheduleConfigService.getById(id);scheduleConfigService.delete(dto);scheduleTaskComponent.pauseeTask(dto.getJobName());}}

前端效果(这里还没做对接)

后端启动效果:

spring定时任务Scheduled之动态开关,动态core表达式相关推荐

  1. Spring定时任务@scheduled多线程的使用(@Async注解)

    1.开篇 在Spring定时任务@Scheduled注解使用方式浅窥这篇文章里面提及过,spring的定时任务默认是单线程的,他在某些场景下会造成堵塞,那么如果我们想让每一个任务都起一条线程去执行呢? ...

  2. spring定时任务Scheduled与定时任务线程池配置SchedulingConfigurer ,Java

    spring定时任务Scheduled与定时任务线程池配置SchedulingConfigurer ,Java spring默认定时任务的使用 package zhangphil.demo;impor ...

  3. Spring定时任务@Scheduled注解使用配置方式(cron表达式、fixedRate和fixedDelay)

    Spring定时任务@Scheduled注解使用配置方式(cron表达式.fixedRate和fixedDelay) 序言: 个人推荐一个很方便的在线Cron生成器(网页版):https://qqe2 ...

  4. Spring定时任务-@Scheduled

    目的:使用Spring的@Scheduled实现定时任务 1.在spring的配置文件中加入以下配置: xmlns:task="http://www.springframework.org/ ...

  5. Spring定时任务scheduled

    Spring定时任务 一. cron 表达式 ​ 1. 概念:Cron表达式是一个字符串,字符串以5或6个空格隔开,分为6或7个域,每一个域代表一个含义,Cron有如下两种语法格式: ​ 2. Cro ...

  6. 【定时任务】——Spring定时任务Scheduled

    定时任务在日常开发过程中非常常见,而且在日常的项目开发中也有多种实现方式,而且做任务调度的框架有很多种,小编最近的感受,如果想真正使用好任务调度还是存在困难的,所以分步学习,逐个击破!在这篇文章小编主 ...

  7. Spring定时任务@Scheduled注解使用方式浅窥(cron表达式、fixedRate和fixedDelay)

    1.开篇 spring的@Scheduled定时任务相信大家都是十分熟悉.最近在使用过程中发现了一些问题,写篇文章,和大家分享一下.结论在最后,不想看冗长过程的小伙伴可以直接拉到最后看结论. 2.简单 ...

  8. spring 定时任务@Scheduled详解

    1.注解@Scheduled只能用于满足下面两个条件的方法上: (1)没有返回类型,或者说返回类型为void: (2)没有参数: 2.springBoot 定时任务开启方式和使用形式 Springbo ...

  9. Spring 定时任务@Scheduled用法

    基本用法 @Scheduled 由Spring定义,用于将方法设置为调度任务.如:方法每隔十秒钟被执行.方法在固定时间点被执行等 1. @Scheduled(fixedDelay = 1000)    ...

最新文章

  1. geany怎么创建文件夹_SAP事务码太多,记不住怎么办?
  2. 电脑服务器信息,电脑服务器怎么看模块信息
  3. 道德规范的心理学透视
  4. PCL点云库法向量显示,vtk错误“no override found for vtkActor”
  5. [深度学习] 面试常见问题+解析汇总
  6. Hive hang without response
  7. 零基础入门│带你理解Kubernetes
  8. bootstrap 页面分成三列_20分钟成功编写bootstrap响应式页面 就这么简单
  9. Shell学习五-分割文件和提取文件名扩展名
  10. 【leetcode】538. Convert BST to Greater Tree
  11. 计算机常见的编码规范
  12. 一文搞懂CAN总线协议帧格式
  13. MarkDown支持Emoji表情
  14. 压缩包安装fitter库,gbk编码错误解决方法
  15. Android之WebView的使用与简单浏览器
  16. VC下如何使用GDI+进行图像程序设计
  17. 1024 程序员节狂欢盛会,等了一年终于来了(内附大会日程)
  18. 微信小程序注册相关资料
  19. 如何在JPG照片尺寸不变的情况下压缩大小?
  20. 获取当前日期是一年中的第几周

热门文章

  1. 微信小程序获取用户昵称中含有emoj表情,后端处理方式以及回显。
  2. 【Scratch】魔术表演
  3. 联邦学习论文笔记——FedFair: Training Fair Models In Cross-Silo Fedrated Learning
  4. c语言课程设计简易年月历计算,c语言课程设计《打印月历》.docx
  5. 华为 HarmonyOS 系统一键升级来了!
  6. 年终总结:2021年征信行业十大关键词及2022年展望
  7. 十分钟之内实现stack和queue?容器适配器是什么?priority_queue不是队列?
  8. 机房重构验收一重构系统
  9. 解决Navicat启动SQLPLUS闪退和乱码的问题
  10. 可以取代Altium的在线电路EDA软件EasyEDA,中国人自己开发的产品