├── LICENSE ├── README.md ├── pom.xml └── src └── main ├── java └── org │ └── javaboy │ └── scheduling02 │ ├── App.java │ ├── config │ ├── CronTaskRegistrar.java │ ├── InitTask.java │ ├── ScheduledTask.java │ ├── SchedulingConfig.java │ ├── SchedulingRunnable.java │ └── SpringContextUtils.java │ ├── controller │ └── SysJobController.java │ ├── dao │ └── SysJobRepository.java │ ├── model │ ├── RespBean.java │ └── SysJob.java │ └── service │ ├── SchedulingTaskDemo.java │ └── SysJobService.java └── resources ├── application.yaml └── static └── index.html /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 定时任务也算是我们日常开发中比较常见的需求了,市面上也有很多成熟的框架: 2 | 3 | - quartz 4 | - elastic-job 5 | - xxl-job 6 | - ... 7 | 8 | 不过小伙伴们知道,其实我们 Spring 框架中也提供了相应的定时任务,这个定时任务通过 `@EnableScheduling` 注解开启,松哥之前也写过文章和大家分享这个注解的基本用法: 9 | 10 | - [Spring Boot 中实现定时任务的两种方式!](https://mp.weixin.qq.com/s/_20RYBkjKrB4tdpXI3hBOA) 11 | 12 | 不过之前的定时任务都是固定的,提前写死的,没法动态配置,前两天有小伙伴问松哥如何实现定时任务的动态配置? 13 | 14 | 这个东西要是基于 xxl-job 之类的框架来做其实是比较容易的,不过用 Spring 自带的 `@EnableScheduling` 注解其实也能实现,而且并不难,松哥基于此火急火燎的写了一个,今天先和大家聊聊大致用法,后面抽空再写一篇文章介绍实现原理。 15 | 16 | 项目已开源,项目地址: 17 | 18 | - [https://github.com/lenve/scheduling](https://github.com/lenve/scheduling) 19 | 20 | ## 食用方式 21 | 22 | 1. 克隆项目:`git clone https://github.com/lenve/scheduling.git`。 23 | 2. 本地数据库创建一个名为 `scheduling` 的库。 24 | 3. 修改配置文件 `src/main/resources/application.yaml`,主要修改数据库连接的用户名和地址。 25 | 4. 启动项目。 26 | 5. 浏览器访问 `http://localhost:8080`,可以看到如下页面: 27 | 28 | ![](http://img.itboyhub.com/2021/07/20210910174209.png) 29 | 30 | 表示启动成功。 31 | 32 | ## 功能介绍 33 | 34 | 1. 项目启动时,会自动从数据库中加载状态为 1 的定时任务并开始执行,1 表示处于开启状态的定时任务,0 表示处于禁用状态的定时任务。 35 | 2. 点击页面上的**添加作业**按钮,可以添加一个新的定时任务,新任务的 Bean 名称、方法名称以及方法参数如果和已有的记录相同,则认为是重复作业,重复作业会添加失败。 36 | 37 | 添加作业的页面如下: 38 | 39 | ![](http://img.itboyhub.com/2021/07/20210910181820.png) 40 | 41 | 这里涉及到几个参数,含义如下: 42 | 43 | - Bean 名称:这是项目中注入 Spring 的 Bean 名称,测试代码中以 `org/javaboy/scheduling02/service/SchedulingTaskDemo.java` 为例。 44 | - 方法名称:参数 1 中 bean 里边的方法名称。 45 | - 方法参数:参数 2 中方法的参数。 46 | - Cron 表达式:定时任务的 Cron 表达式。 47 | - 作业状态:开启和禁用两种。开启的话,添加完成后这个定时任务就会开始执行,禁用的话,就单纯只是将记录添加到数据库中。 48 | 49 | 50 | 作业添加成功提示如下: 51 | 52 | ![](http://img.itboyhub.com/2021/07/20210910182636.png) 53 | 54 | 作业添加失败提示如下: 55 | 56 | ![](http://img.itboyhub.com/2021/07/20210910181458.png) 57 | 58 | 3. 点击作业编辑,可以修改作业的各项数据: 59 | 60 | ![](http://img.itboyhub.com/2021/07/20210910182736.png) 61 | 62 | 修改后会立马生效。 63 | 64 | 4. 点击作业删除,可以删除一个现有的作业。假如删除的作业正在执行,则先停止该作业,然后删除。 65 | 5. 点击列表中的 switch 按钮也可以切换作业的状态。 66 | 67 | ![](http://img.itboyhub.com/2021/07/20210910183133.png) 68 | 69 | ## 技术栈 70 | 71 | - SpringBoot 72 | - Jpa 73 | - MySQL 74 | - Spring Job 75 | - Vue 76 | 77 | ## 其他 78 | 79 | 这是一个学习的 Demo,并非完整项目,后面松哥会出一篇文章和大家分享具体的实现思路。 80 | 81 | 好啦,先说这么多。 82 | 83 | 感兴趣的小伙伴赶紧去体验一把吧:[https://github.com/lenve/scheduling](https://github.com/lenve/scheduling) -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.5.4 9 | 10 | 11 | org.javaboy 12 | scheduling 13 | 0.0.1-SNAPSHOT 14 | scheduling 15 | Demo project for Spring Boot 16 | 17 | 11 18 | 19 | 20 | 21 | 22 | org.springframework.boot 23 | spring-boot-starter-data-jpa 24 | 25 | 26 | 27 | org.springframework.boot 28 | spring-boot-starter-web 29 | 30 | 31 | 32 | mysql 33 | mysql-connector-java 34 | runtime 35 | 36 | 37 | org.springframework.boot 38 | spring-boot-starter-test 39 | test 40 | 41 | 42 | 43 | 44 | 45 | 46 | org.springframework.boot 47 | spring-boot-maven-plugin 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /src/main/java/org/javaboy/scheduling02/App.java: -------------------------------------------------------------------------------- 1 | package org.javaboy.scheduling02; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.scheduling.annotation.EnableScheduling; 6 | 7 | /** 8 | * @author 江南一点雨 9 | * @微信公众号 江南一点雨 10 | * @网站 http://www.itboyhub.com 11 | * @国际站 http://www.javaboy.org 12 | * @微信 a_java_boy 13 | * @GitHub https://github.com/lenve 14 | * @Gitee https://gitee.com/lenve 15 | */ 16 | @SpringBootApplication 17 | @EnableScheduling 18 | public class App { 19 | public static void main(String[] args) { 20 | SpringApplication.run(App.class, args); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/org/javaboy/scheduling02/config/CronTaskRegistrar.java: -------------------------------------------------------------------------------- 1 | package org.javaboy.scheduling02.config; 2 | 3 | import org.springframework.beans.factory.DisposableBean; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.scheduling.TaskScheduler; 6 | import org.springframework.scheduling.config.CronTask; 7 | import org.springframework.stereotype.Component; 8 | 9 | import java.util.Map; 10 | import java.util.concurrent.ConcurrentHashMap; 11 | 12 | /** 13 | * @author 江南一点雨 14 | * @微信公众号 江南一点雨 15 | * @网站 http://www.itboyhub.com 16 | * @国际站 http://www.javaboy.org 17 | * @微信 a_java_boy 18 | * @GitHub https://github.com/lenve 19 | * @Gitee https://gitee.com/lenve 20 | */ 21 | @Component 22 | public class CronTaskRegistrar implements DisposableBean { 23 | 24 | private final Map scheduledTasks = new ConcurrentHashMap<>(16); 25 | 26 | @Autowired 27 | private TaskScheduler taskScheduler; 28 | 29 | public TaskScheduler getScheduler() { 30 | return this.taskScheduler; 31 | } 32 | 33 | public void addCronTask(Runnable task, String cronExpression) { 34 | addCronTask(new CronTask(task, cronExpression)); 35 | } 36 | 37 | public void addCronTask(CronTask cronTask) { 38 | if (cronTask != null) { 39 | Runnable task = cronTask.getRunnable(); 40 | if (this.scheduledTasks.containsKey(task)) { 41 | removeCronTask(task); 42 | } 43 | 44 | this.scheduledTasks.put(task, scheduleCronTask(cronTask)); 45 | } 46 | } 47 | 48 | public void removeCronTask(Runnable task) { 49 | ScheduledTask scheduledTask = this.scheduledTasks.remove(task); 50 | if (scheduledTask != null) 51 | scheduledTask.cancel(); 52 | } 53 | 54 | public ScheduledTask scheduleCronTask(CronTask cronTask) { 55 | ScheduledTask scheduledTask = new ScheduledTask(); 56 | scheduledTask.future = this.taskScheduler.schedule(cronTask.getRunnable(), cronTask.getTrigger()); 57 | 58 | return scheduledTask; 59 | } 60 | 61 | 62 | @Override 63 | public void destroy() { 64 | for (ScheduledTask task : this.scheduledTasks.values()) { 65 | task.cancel(); 66 | } 67 | 68 | this.scheduledTasks.clear(); 69 | } 70 | } -------------------------------------------------------------------------------- /src/main/java/org/javaboy/scheduling02/config/InitTask.java: -------------------------------------------------------------------------------- 1 | package org.javaboy.scheduling02.config; 2 | 3 | import org.javaboy.scheduling02.model.SysJob; 4 | import org.javaboy.scheduling02.service.SysJobService; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.boot.CommandLineRunner; 7 | import org.springframework.stereotype.Component; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * @author 江南一点雨 13 | * @微信公众号 江南一点雨 14 | * @网站 http://www.itboyhub.com 15 | * @国际站 http://www.javaboy.org 16 | * @微信 a_java_boy 17 | * @GitHub https://github.com/lenve 18 | * @Gitee https://gitee.com/lenve 19 | */ 20 | @Component 21 | public class InitTask implements CommandLineRunner { 22 | @Autowired 23 | CronTaskRegistrar cronTaskRegistrar; 24 | @Autowired 25 | SysJobService sysJobService; 26 | 27 | @Override 28 | public void run(String... args) throws Exception { 29 | List list = sysJobService.getJobsByStatus(1); 30 | for (SysJob sysJob : list) { 31 | cronTaskRegistrar.addCronTask(new SchedulingRunnable(sysJob.getBeanName(), sysJob.getMethodName(), sysJob.getMethodParams()), sysJob.getCronExpression()); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/org/javaboy/scheduling02/config/ScheduledTask.java: -------------------------------------------------------------------------------- 1 | package org.javaboy.scheduling02.config; 2 | 3 | import java.util.concurrent.ScheduledFuture; 4 | 5 | /** 6 | * @author 江南一点雨 7 | * @微信公众号 江南一点雨 8 | * @网站 http://www.itboyhub.com 9 | * @国际站 http://www.javaboy.org 10 | * @微信 a_java_boy 11 | * @GitHub https://github.com/lenve 12 | * @Gitee https://gitee.com/lenve 13 | */ 14 | public final class ScheduledTask { 15 | 16 | volatile ScheduledFuture future; 17 | 18 | 19 | public void cancel() { 20 | ScheduledFuture future = this.future; 21 | if (future != null) { 22 | future.cancel(true); 23 | } 24 | } 25 | } 26 | 27 | -------------------------------------------------------------------------------- /src/main/java/org/javaboy/scheduling02/config/SchedulingConfig.java: -------------------------------------------------------------------------------- 1 | package org.javaboy.scheduling02.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.scheduling.TaskScheduler; 6 | import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; 7 | 8 | /** 9 | * @author 江南一点雨 10 | * @微信公众号 江南一点雨 11 | * @网站 http://www.itboyhub.com 12 | * @国际站 http://www.javaboy.org 13 | * @微信 a_java_boy 14 | * @GitHub https://github.com/lenve 15 | * @Gitee https://gitee.com/lenve 16 | */ 17 | @Configuration 18 | public class SchedulingConfig { 19 | @Bean 20 | public TaskScheduler taskScheduler() { 21 | ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler(); 22 | 23 | taskScheduler.setPoolSize(4); 24 | taskScheduler.setRemoveOnCancelPolicy(true); 25 | taskScheduler.setThreadNamePrefix("TaskSchedulerThreadPool-"); 26 | return taskScheduler; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/org/javaboy/scheduling02/config/SchedulingRunnable.java: -------------------------------------------------------------------------------- 1 | package org.javaboy.scheduling02.config; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.util.ReflectionUtils; 6 | import org.springframework.util.StringUtils; 7 | 8 | import java.lang.reflect.Method; 9 | import java.util.Objects; 10 | 11 | /** 12 | * @author 江南一点雨 13 | * @微信公众号 江南一点雨 14 | * @网站 http://www.itboyhub.com 15 | * @国际站 http://www.javaboy.org 16 | * @微信 a_java_boy 17 | * @GitHub https://github.com/lenve 18 | * @Gitee https://gitee.com/lenve 19 | */ 20 | public class SchedulingRunnable implements Runnable { 21 | 22 | private static final Logger logger = LoggerFactory.getLogger(SchedulingRunnable.class); 23 | 24 | private String beanName; 25 | 26 | private String methodName; 27 | 28 | private String params; 29 | 30 | private Object targetBean; 31 | 32 | private Method method; 33 | 34 | public SchedulingRunnable(String beanName, String methodName) { 35 | this(beanName, methodName, null); 36 | } 37 | 38 | public SchedulingRunnable(String beanName, String methodName, String params) { 39 | this.beanName = beanName; 40 | this.methodName = methodName; 41 | this.params = params; 42 | init(); 43 | } 44 | 45 | private void init() { 46 | try { 47 | targetBean = SpringContextUtils.getBean(beanName); 48 | 49 | if (StringUtils.hasText(params)) { 50 | method = targetBean.getClass().getDeclaredMethod(methodName, String.class); 51 | } else { 52 | method = targetBean.getClass().getDeclaredMethod(methodName); 53 | } 54 | 55 | ReflectionUtils.makeAccessible(method); 56 | } catch (NoSuchMethodException e) { 57 | e.printStackTrace(); 58 | } 59 | } 60 | 61 | @Override 62 | public void run() { 63 | logger.info("定时任务开始执行 - bean:{},方法:{},参数:{}", beanName, methodName, params); 64 | long startTime = System.currentTimeMillis(); 65 | 66 | try { 67 | if (StringUtils.hasText(params)) { 68 | method.invoke(targetBean, params); 69 | } else { 70 | method.invoke(targetBean); 71 | } 72 | } catch (Exception ex) { 73 | logger.error(String.format("定时任务执行异常 - bean:%s,方法:%s,参数:%s ", beanName, methodName, params), ex); 74 | } 75 | 76 | long times = System.currentTimeMillis() - startTime; 77 | logger.info("定时任务执行结束 - bean:{},方法:{},参数:{},耗时:{} 毫秒", beanName, methodName, params, times); 78 | } 79 | 80 | @Override 81 | public boolean equals(Object o) { 82 | if (this == o) return true; 83 | if (o == null || getClass() != o.getClass()) return false; 84 | SchedulingRunnable that = (SchedulingRunnable) o; 85 | if (params == null) { 86 | return beanName.equals(that.beanName) && 87 | methodName.equals(that.methodName) && 88 | that.params == null; 89 | } 90 | 91 | return beanName.equals(that.beanName) && 92 | methodName.equals(that.methodName) && 93 | params.equals(that.params); 94 | } 95 | 96 | @Override 97 | public int hashCode() { 98 | if (params == null) { 99 | return Objects.hash(beanName, methodName); 100 | } 101 | 102 | return Objects.hash(beanName, methodName, params); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/main/java/org/javaboy/scheduling02/config/SpringContextUtils.java: -------------------------------------------------------------------------------- 1 | package org.javaboy.scheduling02.config; 2 | 3 | import org.springframework.beans.BeansException; 4 | import org.springframework.context.ApplicationContext; 5 | import org.springframework.context.ApplicationContextAware; 6 | import org.springframework.stereotype.Component; 7 | 8 | /** 9 | * @author 江南一点雨 10 | * @微信公众号 江南一点雨 11 | * @网站 http://www.itboyhub.com 12 | * @国际站 http://www.javaboy.org 13 | * @微信 a_java_boy 14 | * @GitHub https://github.com/lenve 15 | * @Gitee https://gitee.com/lenve 16 | */ 17 | @Component 18 | public class SpringContextUtils implements ApplicationContextAware { 19 | 20 | private static ApplicationContext applicationContext; 21 | 22 | @Override 23 | public void setApplicationContext(ApplicationContext applicationContext) 24 | throws BeansException { 25 | SpringContextUtils.applicationContext = applicationContext; 26 | } 27 | 28 | public static Object getBean(String name) { 29 | return applicationContext.getBean(name); 30 | } 31 | 32 | public static T getBean(Class requiredType) { 33 | return applicationContext.getBean(requiredType); 34 | } 35 | 36 | public static T getBean(String name, Class requiredType) { 37 | return applicationContext.getBean(name, requiredType); 38 | } 39 | 40 | public static boolean containsBean(String name) { 41 | return applicationContext.containsBean(name); 42 | } 43 | 44 | public static boolean isSingleton(String name) { 45 | return applicationContext.isSingleton(name); 46 | } 47 | 48 | public static Class getType(String name) { 49 | return applicationContext.getType(name); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/org/javaboy/scheduling02/controller/SysJobController.java: -------------------------------------------------------------------------------- 1 | package org.javaboy.scheduling02.controller; 2 | 3 | import org.javaboy.scheduling02.model.RespBean; 4 | import org.javaboy.scheduling02.model.SysJob; 5 | import org.javaboy.scheduling02.service.SysJobService; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.web.bind.annotation.*; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * @author 江南一点雨 13 | * @微信公众号 江南一点雨 14 | * @网站 http://www.itboyhub.com 15 | * @国际站 http://www.javaboy.org 16 | * @微信 a_java_boy 17 | * @GitHub https://github.com/lenve 18 | * @Gitee https://gitee.com/lenve 19 | */ 20 | @RestController 21 | @RequestMapping("/jobs") 22 | public class SysJobController { 23 | @Autowired 24 | SysJobService sysJobService; 25 | @GetMapping("/") 26 | public List getAllJobs() { 27 | return sysJobService.getAllJobs(); 28 | } 29 | 30 | @PostMapping("/") 31 | public RespBean addJob(@RequestBody SysJob sysJob) { 32 | Boolean flag = sysJobService.addJob(sysJob); 33 | if (flag) { 34 | return RespBean.ok("作业添加成功"); 35 | } 36 | return RespBean.error("作业重复,添加失败"); 37 | } 38 | 39 | @PutMapping("/") 40 | public RespBean updateJob(@RequestBody SysJob sysJob) { 41 | Boolean flag = sysJobService.updateJob(sysJob); 42 | if (flag) { 43 | return RespBean.ok("作业更新成功"); 44 | } 45 | return RespBean.error("作业更新失败"); 46 | } 47 | 48 | @DeleteMapping("/") 49 | public RespBean deleteJobs(Integer id) { 50 | Boolean flag = sysJobService.deleteJobsById(id); 51 | if (flag) { 52 | return RespBean.ok("删除成功"); 53 | } 54 | return RespBean.error("删除失败"); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/org/javaboy/scheduling02/dao/SysJobRepository.java: -------------------------------------------------------------------------------- 1 | package org.javaboy.scheduling02.dao; 2 | 3 | import org.javaboy.scheduling02.model.SysJob; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * @author 江南一点雨 10 | * @微信公众号 江南一点雨 11 | * @网站 http://www.itboyhub.com 12 | * @国际站 http://www.javaboy.org 13 | * @微信 a_java_boy 14 | * @GitHub https://github.com/lenve 15 | * @Gitee https://gitee.com/lenve 16 | */ 17 | public interface SysJobRepository extends JpaRepository { 18 | List findAllByJobStatus(Integer status); 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/org/javaboy/scheduling02/model/RespBean.java: -------------------------------------------------------------------------------- 1 | package org.javaboy.scheduling02.model; 2 | 3 | /** 4 | * @author 江南一点雨 5 | * @微信公众号 江南一点雨 6 | * @网站 http://www.itboyhub.com 7 | * @国际站 http://www.javaboy.org 8 | * @微信 a_java_boy 9 | * @GitHub https://github.com/lenve 10 | * @Gitee https://gitee.com/lenve 11 | */ 12 | public class RespBean { 13 | private Integer status; 14 | private String msg; 15 | private Object obj; 16 | 17 | public static RespBean ok(String msg,Object obj) { 18 | return new RespBean(200, msg, obj); 19 | } 20 | 21 | 22 | public static RespBean ok(String msg) { 23 | return new RespBean(200, msg, null); 24 | } 25 | 26 | 27 | public static RespBean error(String msg,Object obj) { 28 | return new RespBean(500, msg, obj); 29 | } 30 | 31 | 32 | public static RespBean error(String msg) { 33 | return new RespBean(500, msg, null); 34 | } 35 | 36 | private RespBean() { 37 | } 38 | 39 | private RespBean(Integer status, String msg, Object obj) { 40 | this.status = status; 41 | this.msg = msg; 42 | this.obj = obj; 43 | } 44 | 45 | public Integer getStatus() { 46 | return status; 47 | } 48 | 49 | public void setStatus(Integer status) { 50 | this.status = status; 51 | } 52 | 53 | public String getMsg() { 54 | return msg; 55 | } 56 | 57 | public void setMsg(String msg) { 58 | this.msg = msg; 59 | } 60 | 61 | public Object getObj() { 62 | return obj; 63 | } 64 | 65 | public void setObj(Object obj) { 66 | this.obj = obj; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/org/javaboy/scheduling02/model/SysJob.java: -------------------------------------------------------------------------------- 1 | package org.javaboy.scheduling02.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonFormat; 4 | 5 | import javax.persistence.Entity; 6 | import javax.persistence.GeneratedValue; 7 | import javax.persistence.GenerationType; 8 | import javax.persistence.Id; 9 | import java.util.Date; 10 | import java.util.Objects; 11 | 12 | /** 13 | * @author 江南一点雨 14 | * @微信公众号 江南一点雨 15 | * @网站 http://www.itboyhub.com 16 | * @国际站 http://www.javaboy.org 17 | * @微信 a_java_boy 18 | * @GitHub https://github.com/lenve 19 | * @Gitee https://gitee.com/lenve 20 | */ 21 | @Entity(name = "t_sys_job") 22 | public class SysJob { 23 | 24 | @Id 25 | @GeneratedValue(strategy = GenerationType.IDENTITY) 26 | private Integer jobId; 27 | 28 | private String beanName; 29 | 30 | private String methodName; 31 | 32 | private String methodParams; 33 | 34 | private String cronExpression; 35 | 36 | private Integer jobStatus; 37 | 38 | private String remark; 39 | 40 | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") 41 | private Date createTime; 42 | 43 | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") 44 | private Date updateTime; 45 | 46 | @Override 47 | public boolean equals(Object o) { 48 | if (this == o) return true; 49 | if (o == null || getClass() != o.getClass()) return false; 50 | SysJob sysJob = (SysJob) o; 51 | return Objects.equals(beanName, sysJob.beanName) && 52 | Objects.equals(methodName, sysJob.methodName) && 53 | Objects.equals(methodParams, sysJob.methodParams) && 54 | Objects.equals(cronExpression, sysJob.cronExpression); 55 | } 56 | 57 | @Override 58 | public int hashCode() { 59 | return Objects.hash(beanName, methodName, methodParams, cronExpression); 60 | } 61 | 62 | public Integer getJobId() { 63 | return jobId; 64 | } 65 | 66 | public void setJobId(Integer jobId) { 67 | this.jobId = jobId; 68 | } 69 | 70 | public String getBeanName() { 71 | return beanName; 72 | } 73 | 74 | public void setBeanName(String beanName) { 75 | this.beanName = beanName; 76 | } 77 | 78 | public String getMethodName() { 79 | return methodName; 80 | } 81 | 82 | public void setMethodName(String methodName) { 83 | this.methodName = methodName; 84 | } 85 | 86 | public String getMethodParams() { 87 | return methodParams; 88 | } 89 | 90 | public void setMethodParams(String methodParams) { 91 | this.methodParams = methodParams; 92 | } 93 | 94 | public String getCronExpression() { 95 | return cronExpression; 96 | } 97 | 98 | public void setCronExpression(String cronExpression) { 99 | this.cronExpression = cronExpression; 100 | } 101 | 102 | public Integer getJobStatus() { 103 | return jobStatus; 104 | } 105 | 106 | public void setJobStatus(Integer jobStatus) { 107 | this.jobStatus = jobStatus; 108 | } 109 | 110 | public String getRemark() { 111 | return remark; 112 | } 113 | 114 | public void setRemark(String remark) { 115 | this.remark = remark; 116 | } 117 | 118 | public Date getCreateTime() { 119 | return createTime; 120 | } 121 | 122 | public void setCreateTime(Date createTime) { 123 | this.createTime = createTime; 124 | } 125 | 126 | public Date getUpdateTime() { 127 | return updateTime; 128 | } 129 | 130 | public void setUpdateTime(Date updateTime) { 131 | this.updateTime = updateTime; 132 | } 133 | } -------------------------------------------------------------------------------- /src/main/java/org/javaboy/scheduling02/service/SchedulingTaskDemo.java: -------------------------------------------------------------------------------- 1 | package org.javaboy.scheduling02.service; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | /** 6 | * @author 江南一点雨 7 | * @微信公众号 江南一点雨 8 | * @网站 http://www.itboyhub.com 9 | * @国际站 http://www.javaboy.org 10 | * @微信 a_java_boy 11 | * @GitHub https://github.com/lenve 12 | * @Gitee https://gitee.com/lenve 13 | */ 14 | @Component("schedulingTaskDemo") 15 | public class SchedulingTaskDemo { 16 | public void taskWithParams(String params) { 17 | System.out.println("执行有参示例任务:" + params); 18 | } 19 | 20 | public void taskNoParams() { 21 | System.out.println("执行无参示例任务"); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/org/javaboy/scheduling02/service/SysJobService.java: -------------------------------------------------------------------------------- 1 | package org.javaboy.scheduling02.service; 2 | 3 | import org.javaboy.scheduling02.config.CronTaskRegistrar; 4 | import org.javaboy.scheduling02.config.SchedulingRunnable; 5 | import org.javaboy.scheduling02.dao.SysJobRepository; 6 | import org.javaboy.scheduling02.model.SysJob; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.stereotype.Service; 9 | 10 | import java.util.Date; 11 | import java.util.List; 12 | 13 | /** 14 | * @author 江南一点雨 15 | * @微信公众号 江南一点雨 16 | * @网站 http://www.itboyhub.com 17 | * @国际站 http://www.javaboy.org 18 | * @微信 a_java_boy 19 | * @GitHub https://github.com/lenve 20 | * @Gitee https://gitee.com/lenve 21 | */ 22 | @Service 23 | public class SysJobService { 24 | 25 | @Autowired 26 | SysJobRepository sysJobRepository; 27 | 28 | @Autowired 29 | CronTaskRegistrar cronTaskRegistrar; 30 | 31 | public List getAllJobs() { 32 | return sysJobRepository.findAll(); 33 | } 34 | 35 | public Boolean addJob(SysJob sysJob) { 36 | List all = sysJobRepository.findAll(); 37 | for (SysJob job : all) { 38 | if (job.equals(sysJob)) { 39 | //作业重复,添加失败 40 | return false; 41 | } 42 | } 43 | //添加 44 | SysJob sj = sysJobRepository.save(sysJob); 45 | if (sj != null) { 46 | //添加成功,如果新加的job是开启状态,就顺便开启 47 | SchedulingRunnable schedulingRunnable = new SchedulingRunnable(sysJob.getBeanName(), sysJob.getMethodName(), sysJob.getMethodParams()); 48 | if (sj.getJobStatus() == 1) { 49 | cronTaskRegistrar.addCronTask(schedulingRunnable, sysJob.getCronExpression()); 50 | } 51 | //添加成功 52 | return true; 53 | } 54 | return false; 55 | } 56 | 57 | public List getJobsByStatus(int status) { 58 | return sysJobRepository.findAllByJobStatus(status); 59 | } 60 | 61 | public Boolean updateJob(SysJob sysJob) { 62 | SysJob job = sysJobRepository.saveAndFlush(sysJob); 63 | if (job != null) { 64 | SchedulingRunnable schedulingRunnable = new SchedulingRunnable(sysJob.getBeanName(), sysJob.getMethodName(), sysJob.getMethodParams()); 65 | if (sysJob.getJobStatus() == 1) { 66 | cronTaskRegistrar.addCronTask(schedulingRunnable, sysJob.getCronExpression()); 67 | } else { 68 | cronTaskRegistrar.removeCronTask(schedulingRunnable); 69 | } 70 | return true; 71 | } 72 | return false; 73 | } 74 | 75 | public Boolean deleteJobsById(Integer id) { 76 | SysJob sysJob = sysJobRepository.findById(id).get(); 77 | SchedulingRunnable schedulingRunnable = new SchedulingRunnable(sysJob.getBeanName(), sysJob.getMethodName(), sysJob.getMethodParams()); 78 | cronTaskRegistrar.removeCronTask(schedulingRunnable); 79 | sysJobRepository.delete(sysJob); 80 | return true; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/main/resources/application.yaml: -------------------------------------------------------------------------------- 1 | spring: 2 | datasource: 3 | username: root 4 | password: 123 5 | url: jdbc:mysql:///scheduling?serverTimezone=Asia/Shanghai 6 | 7 | jpa: 8 | database: mysql 9 | database-platform: mysql 10 | hibernate: 11 | ddl-auto: update 12 | properties: 13 | hibernate: 14 | dialect: org.hibernate.dialect.MySQL57Dialect -------------------------------------------------------------------------------- /src/main/resources/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 定时任务在线管理系统 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 | 26 | 27 | 28 | 41 | 42 | 43 | 44 | 45 | 46 | 50 | 51 | 52 | 添加作业 53 | 58 |
59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 开启 74 | 禁用 75 | 76 | 77 | 78 | 79 | 80 |
81 | 82 | 取 消 83 | 确 定 84 | 85 |
86 |
87 | 198 | 199 | --------------------------------------------------------------------------------