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 ...
随机推荐
- 题解 - 【NOI2015】维修数列
题面大意: 使用平衡树维护一个数列,支持插入,修改,删除,翻转,求和,求最大和这 \(6\) 个操作. 题意分析: Splay 裸题,几乎各种操作都有了,这个代码就发给大家当个模板吧. 最后求最大和的 ...
- BERT的前世今生
Transformer Transformer来自论文: All Attention Is You Need 别人的总结资源: 谷歌官方AI博客: Transformer: A Novel Neura ...
- Flask03-Form
## Web 表单 配置 为了能够处理 web 表单,我们将使用 Flask-WTF ,该扩展封装了 WTForms 并且恰当地集成进 Flask 中. 许多 Flask 扩展需要大量的配置,因此我们 ...
- Python实用笔记 (27)面向对象高级编程——使用枚举类
枚举类型定义一个class类型,然后,每个常量都是class的一个唯一实例.Python提供了Enum类来实现这个功能: from enum import Enum Month = Enum('Mon ...
- XDocument常用属性
XDocument常用属性: 1) BaseUri 获取此 XObject 的基 URI. (继承自 XObject.) 2) Declaration 获取或设置此文档的 XML 声明. 3) Doc ...
- Vmware - 安装并启动 Centos 8
下载 Linux 安装包 https://mirrors.aliyun.com/centos/8.1.1911/isos/x86_64/ 不同版本的 Centos https://mirrors.al ...
- Spring Boot注解大全,一键收藏了!
本文首发于微信公众号[猿灯塔],转载引用请说明出处 今天是猿灯塔“365天原创计划”第5天. 今天呢!灯塔君跟大家讲: Spring Boot注解大全 一.注解(annotations)列表 @Spr ...
- Flutter 中渐变的高级用法
Flutter 中渐变有三种: LinearGradient:线性渐变 RadialGradient:放射状渐变 SweepGradient:扇形渐变 看下原图,下面的渐变都是在此图基础上完成. Li ...
- 赋值,逻辑,运算符, 控制流程之if 判断
赋值运算 (1). 增量运算 age += 1 # age = age + 1 print(age) age -= 10 # age = age - 10 (2).交叉赋值 x = 111 y = 2 ...
- vscode F2无法使用
rope库可能存在bug 解决方法: "python.jediEnabled": false //自动补全用微软自带