Spring的SchedulingConfigurer实现定时任务
前提:在做业务平台的时候我们经常会遇到,某些跟时间打交道的需要修改状态,比如说在时间区间之前,属于未生效状态,区间之内属于有效期,区间之后,属于过期,或者需要每天 每周 每月,甚至是年为单位的做一些固定的操作。通过定时任务可以通过开启定时任务来完成这些需求。
我做合同管理模块,合同有未生效,已生效,已过期,三个状态,不可能每次用户登录的时候去判断这个状态,然后修改,这样做会在登录的逻辑里边耦合了合同业务逻辑,同时消耗了登录时间,不太可取。
下便是代码:
依赖:


<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.xc</groupId>
<artifactId>timetask</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>timetask</name>
<description>Demo project for Spring Boot</description> <properties>
<java.version>1.8</java.version>
</properties> <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies> <build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build> </project>
pom.xml
主启动类开启定时任务注解
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.web.bind.annotation.RestController; @SpringBootApplication
@RestController
@EnableScheduling
public class TimetaskApplication { public static void main(String[] args) {
SpringApplication.run(TimetaskApplication.class, args);
} }
SpringUtil工具类:需要ApplicationContextAware接口


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component; @Component
public class SpringUtil implements ApplicationContextAware { private Logger logger= LoggerFactory.getLogger(SpringUtil.class); private static ApplicationContext applicationContext; @Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if(SpringUtil.applicationContext == null) {
SpringUtil.applicationContext = applicationContext;
}
logger.info("========ApplicationContext配置成功,在普通类可以通过调用SpringUtils.getAppContext()获取applicationContext对象,applicationContext="+SpringUtil.applicationContext+"========");
} //获取applicationContext
public static ApplicationContext getApplicationContext() {
return applicationContext;
} //通过name获取 Bean.
public static Object getBean(String name){
Class cla = null;
try {
cla=Class.forName(name);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return getApplicationContext().getBean(cla);
} //通过class获取Bean.
public static <T> T getBean(Class<T> clazz){
return getApplicationContext().getBean(clazz);
} //通过name,以及Clazz返回指定的Bean
public static <T> T getBean(String name,Class<T> clazz){
return getApplicationContext().getBean(name, clazz);
} }
SpringUtil.java
SysTaskController实现SchedulingConfigurer接口,配置定时任务以及开启定时任务


import com.xc.timetask.entity.Task;
import com.xc.timetask.service.TaskService;
import com.xc.timetask.util.SpringUtil;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.TriggerContext;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Component; import javax.annotation.Resource;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ScheduledFuture; @Lazy(value = false)
@Component
public class SysTaskController implements SchedulingConfigurer { protected static Logger logger = LoggerFactory.getLogger(SysTaskController.class); private SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); private static ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler(); private static Map<String,ScheduledFuture<?>> scheduledFutureMap = new HashMap<>(); @Resource
private TaskService taskService; //从数据库里取得所有要执行的定时任务
private List<Task> getAllTasks() throws Exception {
List<Task> list=taskService.selectyunx();
return list;
}
static {
threadPoolTaskScheduler.initialize();
}
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
List<Task> tasks= null;
try {
tasks = getAllTasks();
} catch (Exception e) {
e.printStackTrace();
}
logger.info("定时任务启动,预计启动任务数量="+tasks.size()+"; time="+sdf.format(new Date())); //校验数据(这个步骤主要是为了打印日志,可以省略)
checkDataList(tasks); //通过校验的数据执行定时任务
int count = 0;
if(tasks.size()>0) {
for (Task task:tasks) {
try {
//taskRegistrar.addTriggerTask(getRunnable(task), getTrigger(task));
start(task);
count++;
} catch (Exception e) {
logger.error("定时任务启动错误:" + task.getBean_name() + ";" + task.getMethod_name() + ";" + e.getMessage());
}
}
}
logger.info("定时任务实际启动数量="+count+"; time="+sdf.format(new Date()));
}
private static Runnable getRunnable(Task task){
return new Runnable() {
@Override
public void run() {
try {
Object obj = SpringUtil.getBean(task.getBean_name());
Method method = obj.getClass().getMethod(task.getMethod_name());
method.invoke(obj);
} catch (InvocationTargetException e) {
logger.error("定时任务启动错误,反射异常:"+task.getBean_name()+";"+task.getMethod_name()+";"+ e.getMessage());
} catch (Exception e) {
logger.error(e.getMessage());
}
}
};
} private static Trigger getTrigger(Task task){
return new Trigger() {
@Override
public Date nextExecutionTime(TriggerContext triggerContext) {
//将Cron 0/1 * * * * ? 输入取得下一次执行的时间
CronTrigger trigger = new CronTrigger(task.getCron());
Date nextExec = trigger.nextExecutionTime(triggerContext);
return nextExec;
}
}; } private List<Task> checkDataList(List<Task> list) {
String errMsg="";
for(int i=0;i<list.size();i++){
if(!checkOneData(list.get(i)).equalsIgnoreCase("success")){
errMsg+=list.get(i).getName()+";";
list.remove(list.get(i));
i--;
};
}
if(!StringUtils.isBlank(errMsg)){
errMsg="未启动的任务:"+errMsg;
logger.error(errMsg);
}
return list;
} public static String checkOneData(Task task){
String result="success";
Class cal= null;
try {
cal = Class.forName(task.getBean_name()); Object obj = SpringUtil.getBean(cal);
Method method = obj.getClass().getMethod(task.getMethod_name());
String cron=task.getCron();
if(StringUtils.isBlank(cron)){
result="定时任务启动错误,无cron:"+task.getName();
logger.error(result);
}
} catch (ClassNotFoundException e) {
result="定时任务启动错误,找不到类:"+task.getBean_name()+ e.getMessage();
logger.error(result);
} catch (NoSuchMethodException e) {
result="定时任务启动错误,找不到方法,方法必须是public:"+task.getBean_name()+";"+task.getMethod_name()+";"+ e.getMessage();
logger.error(result);
} catch (Exception e) {
logger.error(e.getMessage());
}
return result;
}
/**
* 启动定时任务
* @param task
* @param
*/
public static void start(Task task){ ScheduledFuture<?> scheduledFuture = threadPoolTaskScheduler.schedule(getRunnable(task),getTrigger(task));
scheduledFutureMap.put(task.getId(),scheduledFuture);
logger.info("启动定时任务" + task.getId() ); } /**
* 取消定时任务
* @param task
*/
public static void cancel(Task task){ ScheduledFuture<?> scheduledFuture = scheduledFutureMap.get(task.getId()); if(scheduledFuture != null && !scheduledFuture.isCancelled()){
scheduledFuture.cancel(Boolean.FALSE);
} scheduledFutureMap.remove(task.getId());
logger.info("取消定时任务" + task.getId() ); } /**
* 编辑
* @param task
* @param
*/
public static void reset(Task task){
logger.info("修改定时任务开始" + task.getId() );
cancel(task);
start(task);
logger.info("修改定时任务结束" + task.getId());
} }
SysTaskController.java
TaskDemo:定时任务要操作的类


import org.springframework.stereotype.Component; import java.text.SimpleDateFormat;
import java.util.Date; @Component
public class TaskDemo {
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //注入需要的service
//private ContractService contractService; public String run(){ try {
/*
使用service做一些修改的业务操作
获取所有的合同列表
List<Contract> list = contractService.getAllContract();
修改合同的状态
contractService.updateContractList(list); 下边用打印***来代替业务进行测试定时任务
*/
System.out.println("*********"); System.out.println("TaskContract is running"+sdf.format(new Date()));
} catch (Exception e) {
e.printStackTrace();
}
return "/main";
}
}
TaskDemo.java
其他的controller service dao类就省略了,后边会给出github地址,有demo可执行
演示及说明
访问/timetask/list可以看到所有的定时任务列表 可以进行启动 停止 以及常规的增删改查
特别说明 新增的时候类名必须是全路径类名,因为是通过Class.forName("com.xc.timetask········") 反射来加载要执行的类
表达式 是cron表达式 就是定时任务的执行单位 不知道的可以 戳这里 去看看
方法 是要执行的方法的名字
新增和修改页面 方法哪里填写 run 就可以 和 TaskDemo 里的那个方法对应
这里忘记加上时间区间的datepicker日历控件 来进行开始时间 结束时间填充, 直接去数据库表里修改开始 结束时间 轻点喷我!!!!!
另外demo使用了LayUI 真的是惨不忍睹 弹出层会重复 -!- 不过后台代码都是好用的
结果 因为上边图中列表 只有一个定时任务开启的
需要demo 源码的 请戳这里
SysTaskController
Spring的SchedulingConfigurer实现定时任务的更多相关文章
- Spring基于SchedulingConfigurer实现定时任务
Spring 基于 SchedulingConfigurer 实现定时任务,代码如下: import org.springframework.scheduling.annotation.Schedul ...
- spring的Scheduled(定时任务)和多线程
一.前言 在我们日常的开发中,经常用到数据同步的更新,这时我们采用的是spring的定时任务和java的多线程进行数据的更新,进行时实的服务调用. 二.实现思路 1.创建线程类 ...
- spring注解scheduled实现定时任务
只想说,spring注解scheduled实现定时任务使用真的非常简单. 一.配置spring.xml文件 1.在beans加入xmlns:task="http://www.springfr ...
- Spring Boot 中实现定时任务的两种方式
在 Spring + SpringMVC 环境中,一般来说,要实现定时任务,我们有两中方案,一种是使用 Spring 自带的定时任务处理器 @Scheduled 注解,另一种就是使用第三方框架 Qua ...
- Spring Boot(九):定时任务
Spring Boot(九):定时任务 一.pom包配置 pom包里面只需要引入springboot starter包即可 <dependencies> <dependency> ...
- spring boot 学习(八)定时任务 @Scheduled
SpringBoot 定时任务 @Scheduled 前言 有时候,我们有这样的需求,需要在每天的某个固定时间或者每隔一段时间让应用去执行某一个任务.一般情况下,可以使用多线程来实现这个功能:在 Sp ...
- 【spring boot】spring boot中使用定时任务配置
spring boot中使用定时任务配置 =============================================================================== ...
- spring boot 基础篇 -- 定时任务
在日常项目中,常常会碰到定时监控项目中某个业务的变化,下面是spring boot 集成的定时任务具体配置: @Component public class IndexWarningScheduled ...
- 品Spring:关于@Scheduled定时任务的思考与探索,结果尴尬了
非Spring风格的代码与Spring的结合 现在的开发都是基于Spring的,所有的依赖都有Spring管理,这没有问题. 但是要突然写一些非Spring风格的代码时,可能会很不习惯,如果还要和Sp ...
随机推荐
- 05[掌握]高可用、集群、持久化、docker 等前置知识点
高可用 24小时对外提供服务 高并发 同一时间段能处理的请求数 1,中心化和去中心化 1.1,中心化 意思是所有的节点都要有一个主节点 缺点:中心挂了,服务就挂了 中心处理数据的能力有限,不能把节点性 ...
- Ubuntu apt update卡在Connecting to security.ubuntu.com解决方法
Ubuntu操作系统运行apt update命令时会卡在Connecting to security.ubuntu.com,搭了梯子也无法解决 尝试了网络上的方法,如: https://blog.cs ...
- Nacos配置中心原理
动态配置管理是 Nacos 的三大功能之一,通过动态配置服务,我们可以在所有环境中以集中和动态的方式管理所有应用程序或服务的配置信息. 动态配置中心可以实现配置更新时无需重新部署应用程序和服务即可使相 ...
- 以api文档为中心--前后端分开发离新思维
api文档编写好像很简单,其实不是.一个良好的api文档,需要就有以下内容:接口详细描述,接口参数详细描述,接口返回结果详细描述,容易理解的范例.这些内容其实是不少的,编写过程中还非常单调乏味.再加上 ...
- vs2017+opencv3.4.0的配置方法
1.尝试了这个博客的方法: https://blog.csdn.net/u014574279/article/details/50909425/ 结果: 无法打开文件“opencv_ml2410d.l ...
- Qt-绘图
1 简介 参考视频:https://www.bilibili.com/video/BV1XW411x7NU?p=37 参考文档:<Qt教程.docx> 本文简单介绍Qt的绘图与绘图设备. ...
- 关于CSS自文档的思考_css声明式语言式代码注释
obert C. Martin写的<Clean Code>是我读过的最好的编程书籍之一,若没有读过,推荐你将它加入书单. 注释就意味着代码无法自说明 —— Robert C. Martin ...
- PLSQL导入dmp文件完整步骤(附图)
导入dmp文件分为三大步:创建表空间.创建用户.导入dmp文件 一.创建表空间: 点击new,新建一个sql window 语句为: create tablespace "表空间名" ...
- day64 django模型层
目录 一.单表操作(增删改) 二.必知必会13个方法 三.查看内部的sql语句的方法 四.神奇的双下划线查询 五.一对多外键的增删改查 六.多对多外键的增删改查 七.正反向查询概念 八.多表查询 1 ...
- SpringBoot系列之前后端接口安全技术JWT
@ 目录 1. 什么是JWT? 2. JWT令牌结构怎么样? 2.1 标头(Header) 2.2 有效载荷(Playload) 2.3 签名(Signature) 3. JWT原理简单介绍 4. J ...